this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

encoding/openapi: implement strict mode

There is currently no way to import OpenAPI schemas in strict mode, by
contrast with JSON Schema.

This addresses that shortcoming. We also make strict mode the default,
and make the OpenAPI 3.0 implementation always use strict keywords mode,
as that spec explicitly prohibits other fields, unlike JSON Schema.

We support the `strict` filetype for OpenAPI to mirror that for
`jsonschema`, but we don't need to add a corresponding field to
`encoding/openapi.Config` - it's nicer to have orthogonal configuration
fields.

Fixes #3445.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: Ie703b4f294ce7ef68dee3abd5fd2f4bf952e14b8
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1201113
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>

+159 -13
+1
cmd/cue/cmd/testdata/script/def_openapi.txtar
··· 56 56 "required": [ 57 57 "foo" 58 58 ], 59 + "x-ignored": "something", 59 60 "properties": { 60 61 "foo": { 61 62 "$ref": "#/components/schemas/Foo"
+99
cmd/cue/cmd/testdata/script/def_openapi_strict.txtar
··· 1 + ! exec cue def openapi: openapi.json 2 + cmp stderr expect-stderr-1 3 + 4 + ! exec cue def openapi.json 5 + cmp stderr expect-stderr-1 6 + 7 + ! exec cue def openapi+strictFeatures: openapi.json 8 + cmp stderr expect-stderr-1 9 + 10 + # We can disable strictFeatures to enable best effort mode. 11 + exec cue def openapi+strictFeatures=0: openapi.json 12 + cmp stdout expect-out.cue 13 + 14 + # Bad keywords are still always bad with OpenAPI 3.0. 15 + ! exec cue def openapi-badkeyword.json 16 + cmp stderr expect-stderr-2 17 + ! exec cue def openapi+strictKeywords=0: openapi-badkeyword.json 18 + cmp stderr expect-stderr-2 19 + 20 + # Keywords should be less strict in OpenAPI 3.1, because 21 + # it mirrors JSON Schema. 22 + exec cue def openapi3.1-badkeyword.json 23 + 24 + # We can enable strict keyword checking similarly 25 + # to jsonschema, with either strict or strictKeywords: 26 + ! exec cue def openapi+strictKeywords: openapi3.1-badkeyword.json 27 + cmp stderr expect-stderr-3 28 + ! exec cue def openapi+strict: openapi3.1-badkeyword.json 29 + cmp stderr expect-stderr-3 30 + 31 + -- openapi.json -- 32 + { 33 + "openapi": "3.0.0", 34 + "info": { 35 + "title": "My OpenAPI", 36 + "version": "v1alpha1" 37 + }, 38 + "paths": {}, 39 + "components": { 40 + "schemas": { 41 + "Bar": { 42 + "type": "object", 43 + "x-foo": "always allowed", 44 + "xml": "maybe we will never implement this" 45 + } 46 + } 47 + } 48 + } 49 + -- openapi-badkeyword.json -- 50 + { 51 + "openapi": "3.0.0", 52 + "info": { 53 + "title": "My OpenAPI", 54 + "version": "v1alpha1" 55 + }, 56 + "paths": {}, 57 + "components": { 58 + "schemas": { 59 + "Bar": { 60 + "type": "object", 61 + "unknown": "never OK" 62 + } 63 + } 64 + } 65 + } 66 + -- openapi3.1-badkeyword.json -- 67 + { 68 + "openapi": "3.1.0", 69 + "info": { 70 + "title": "My OpenAPI", 71 + "version": "v1alpha1" 72 + }, 73 + "paths": {}, 74 + "components": { 75 + "schemas": { 76 + "Bar": { 77 + "type": "object", 78 + "unknown": "usually OK" 79 + } 80 + } 81 + } 82 + } 83 + -- expect-stderr-1 -- 84 + keyword "xml" not yet implemented: 85 + ./openapi.json:13:17 86 + -- expect-out.cue -- 87 + info: { 88 + title: *"My OpenAPI" | string 89 + version: *"v1alpha1" | string 90 + } 91 + #Bar: { 92 + ... 93 + } 94 + -- expect-stderr-2 -- 95 + unknown keyword "unknown": 96 + ./openapi-badkeyword.json:12:17 97 + -- expect-stderr-3 -- 98 + unknown keyword "unknown": 99 + ./openapi3.1-badkeyword.json:12:17
+3
encoding/openapi/decode.go
··· 83 83 Root: oapiSchemas, 84 84 Map: openAPIMapping, 85 85 DefaultVersion: schemaVersion, 86 + StrictFeatures: c.StrictFeatures, 87 + // OpenAPI 3.0 is stricter than JSON Schema about allowed keywords. 88 + StrictKeywords: schemaVersion == jsonschema.VersionOpenAPI || c.StrictKeywords, 86 89 }) 87 90 if err != nil { 88 91 return nil, err
+10
encoding/openapi/openapi.go
··· 81 81 // OpenAPI Schema. It is an error for an CUE value to refer to itself 82 82 // if this option is used. 83 83 ExpandReferences bool 84 + 85 + // StrictFeatures reports an error for features that are known 86 + // to be unsupported. 87 + StrictFeatures bool 88 + 89 + // StrictKeywords reports an error when unknown keywords 90 + // are encountered. For OpenAPI 3.0, this is implicitly always 91 + // true, as that specification explicitly prohibits unknown keywords 92 + // other than "x-" prefixed keywords. 93 + StrictKeywords bool 84 94 } 85 95 86 96 type Generator = Config
+21 -13
internal/encoding/encoding.go
··· 13 13 // limitations under the License. 14 14 15 15 // TODO: make this package public in cuelang.org/go/encoding 16 - // once stabalized. 16 + // once stabilized. 17 17 18 18 package encoding 19 19 ··· 310 310 } 311 311 312 312 func openAPIFunc(c *Config, f *build.File) interpretFunc { 313 - cfg := &openapi.Config{PkgName: c.PkgName} 314 313 return func(v cue.Value) (file *ast.File, err error) { 315 - file, err = openapi.Extract(v, cfg) 314 + tags := boolTagsForFile(f, build.JSONSchema) 315 + file, err = openapi.Extract(v, &openapi.Config{ 316 + PkgName: c.PkgName, 317 + 318 + // Note: don't populate Strict (see more detailed 319 + // comment in jsonSchemaFunc) 320 + 321 + StrictKeywords: c.Strict || tags["strictKeywords"], 322 + StrictFeatures: c.Strict || tags["strictFeatures"], 323 + }) 316 324 // TODO: simplify currently erases file line info. Reintroduce after fix. 317 325 // file, err = simplify(file, err) 318 326 return file, err 327 + } 328 + } 329 + 330 + func protobufJSONFunc(cfg *Config, file *build.File) rewriteFunc { 331 + return func(f *ast.File) (*ast.File, error) { 332 + if !cfg.Schema.Exists() { 333 + return f, errors.Newf(token.NoPos, 334 + "no schema specified for protobuf interpretation.") 335 + } 336 + return f, jsonpb.NewDecoder(cfg.Schema).RewriteFile(f) 319 337 } 320 338 } 321 339 ··· 348 366 } 349 367 maps.Copy(tags, f.BoolTags) 350 368 return tags 351 - } 352 - 353 - func protobufJSONFunc(cfg *Config, file *build.File) rewriteFunc { 354 - return func(f *ast.File) (*ast.File, error) { 355 - if !cfg.Schema.Exists() { 356 - return f, errors.Newf(token.NoPos, 357 - "no schema specified for protobuf interpretation.") 358 - } 359 - return f, jsonpb.NewDecoder(cfg.Schema).RewriteFile(f) 360 - } 361 369 } 362 370 363 371 func shouldValidate(i *filetypes.FileInfo) bool {
+20
internal/filetypes/filetypes_test.go
··· 130 130 Encoding: build.YAML, 131 131 Interpretation: build.OpenAPI, 132 132 Form: build.Schema, 133 + BoolTags: map[string]bool{ 134 + "strict": false, 135 + "strictFeatures": true, 136 + "strictKeywords": false, 137 + }, 133 138 }, 134 139 Definitions: true, 135 140 Data: true, ··· 211 216 Encoding: build.JSON, 212 217 Interpretation: build.OpenAPI, 213 218 Form: build.Schema, 219 + BoolTags: map[string]bool{ 220 + "strict": false, 221 + "strictFeatures": true, 222 + "strictKeywords": false, 223 + }, 214 224 }, 215 225 Definitions: true, 216 226 Data: true, ··· 237 247 Encoding: build.JSON, 238 248 Interpretation: build.OpenAPI, 239 249 Form: build.Schema, 250 + BoolTags: map[string]bool{ 251 + "strict": false, 252 + "strictFeatures": true, 253 + "strictKeywords": false, 254 + }, 240 255 }, 241 256 Definitions: true, 242 257 Data: true, ··· 332 347 Encoding: build.JSON, 333 348 Interpretation: build.OpenAPI, 334 349 Form: build.Schema, 350 + BoolTags: map[string]bool{ 351 + "strict": false, 352 + "strictFeatures": true, 353 + "strictKeywords": false, 354 + }, 335 355 }, 336 356 }, { 337 357 in: "cue:file.json",
+5
internal/filetypes/types.cue
··· 304 304 interpretations: openapi: { 305 305 forms.schema 306 306 encoding: *"json" | _ 307 + boolTags: { 308 + strict: *false | bool 309 + strictKeywords: *strict | bool 310 + strictFeatures: *true | bool 311 + } 307 312 } 308 313 309 314 interpretations: pb: {