cue icon indicating copy to clipboard operation
cue copied to clipboard

encoding/openapi: add support for openapi extension attributes in builder

Open nkvoll opened this issue 1 year ago • 3 comments

This is just taking a quick stab at something for https://github.com/cue-lang/cue/issues/2638, just to see what it would take to fix. I'm not experienced with nor do I fully understand the ast or if what I'm doing is very uncanonical (just learning cue and first time potential contributor), so please bear with me as I'm looking for some feedback on whether some direction like this makes sense or if I'm looking in the wrong directions.

What I wanted to add support for is extending the openapi schema to support extensions like CEL (see https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules). This requires a little more than adding string string:string key:value pairs, so I opted for using JSON inside the cue attribute.

Having the attribute be @openapi(extension:keyName:json-value) meant a little more parsing than something like @openapiExtension(keyName:json-value) (or similar).

Example

Example resource (example.cue):

package example

#SomeResource: {
	spec: {
		parameters: {
			foo: string
        } @openapi(extension=x-kubernetes-validations:[
            {
                "rule": "self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas",
                "message": "replicas should be in the range minReplicas..maxReplicas."
            }
        ])
    }
}

Generate:

go run ./cmd/cue def example.cue -o example.openapi.yaml --out openapi+yaml

Output

openapi: 3.0.0
info:
  title: Generated by cue.
  version: no version
paths: {}
components:
  schemas:
    SomeResource:
      type: object
      required:
        - spec
      properties:
        spec:
          type: object
          required:
            - parameters
          properties:
            parameters:
              type: object
              required:
                - foo
              properties:
                foo:
                  type: string
              x-kubernetes-validations:
                - rule: self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas
                  message: replicas should be in the range minReplicas..maxReplicas.

nkvoll avatar Mar 06 '25 23:03 nkvoll

Thanks for this @nkvoll - I'll ask @rogpeppe to take a look and get back to you!

myitcv avatar Mar 08 '25 05:03 myitcv

@nkvoll Thanks very much for the PR! We definitely want to support aspects of OpenAPI like this, although the right approach isn't entirely clear currently. For now, we are super busy in the run up to Kubecon. We will get back to you with feedback on this after that :)

rogpeppe avatar Mar 10 '25 12:03 rogpeppe

Hi @rogpeppe, just checking in if you have had some time to take a look. I just pushed a change that moves the extension definition from being json to being in cue (not 100% sure on the implementation, whether there is a better / more canonical approach available -- happy to learn).

Example usage right now (pluralized extensions since multiple extensions can be defined here -- though there are currently no explicit guardrails for not colliding with the built-in attributes):

package example

#SomeResource: {
    spec: {
        parameters: {
            minReplicas: int
            maxReplicas: int
            replicas: int
        } @openapi(extensions={
            "x-kubernetes-validations": [
                {
                    rule: "self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas",
                    message: "replicas should be in the range minReplicas..maxReplicas",
                }
            ]
        })
    }
}

nkvoll avatar May 23 '25 17:05 nkvoll

I like the generality of this approach, but if the main objective is support for validation expressions, we should probably see whether it might be possible to express those in CUE directly rather than in an auxilliary non-CUE syntax in attributes.

To take the example from the validation rules page, ISTM it would be much nicer if it was possible to specify it as:

minReplicas!: int & <= replicas
replicas!: & <= maxReplicas
maxReplicas!: int

rather than using the Kubernetes extension syntax directly.

I've created #4048 to track that possibility.

Given that this particular PR implements general support for OpenAPI "extensions", it would be nice to know what other kinds of extension are also good use cases.

rogpeppe avatar Aug 29 '25 12:08 rogpeppe

I don't know of any general overview, but https://swagger.io/docs/specification/v3_0/openapi-extensions/ has an example that uses a lambda authorizer: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-authorizer.html

Google has a bunch of theirs listed here: https://cloud.google.com/endpoints/docs/openapi/openapi-extensions

Redocly has a number of extensions as well https://redocly.com/docs-legacy/api-reference-docs/spec-extensions

Most generically, https://openapispec.com/docs/what/what-are-openapi-extensions/ describes some /possibilities/

nkvoll avatar Sep 15 '25 18:09 nkvoll