diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go index c6684ed68..357d93406 100644 --- a/encoding/jsonschema/constraints.go +++ b/encoding/jsonschema/constraints.go @@ -84,15 +84,18 @@ var constraints = []*constraint{ p2("dependencies", constraintDependencies), todo("dependentRequired", vfrom(VersionDraft2019_09)), todo("dependentSchemas", vfrom(VersionDraft2019_09)), - p2("deprecated", constraintDeprecated), + p2d("deprecated", constraintDeprecated, vfrom(VersionDraft2019_09)), p2("description", constraintDescription), + todo("discriminator", vset(VersionOpenAPI3_0)), todo("else", vfrom(VersionDraft7)), p2("enum", constraintEnum), + todo("example", vset(VersionOpenAPI3_0)), p2d("examples", constraintExamples, vfrom(VersionDraft6)), p2("exclusiveMaximum", constraintExclusiveMaximum), p2("exclusiveMinimum", constraintExclusiveMinimum), + todo("externalDocs", vset(VersionOpenAPI3_0)), todo("format", allVersions), - p1d("id", constraintID, vto(VersionDraft4)), + p1d("id", constraintID, vto(VersionDraft4)&^openAPI3_0), todo("if", vfrom(VersionDraft7)), p2("items", constraintItems), p1d("maxContains", constraintMaxContains, vfrom(VersionDraft2019_09)), @@ -107,7 +110,7 @@ var constraints = []*constraint{ p3("minimum", constraintMinimum), p2("multipleOf", constraintMultipleOf), p3("not", constraintNot), - p2("nullable", constraintNullable), + p2d("nullable", constraintNullable, vset(VersionOpenAPI3_0)), p3("oneOf", constraintOneOf), p2("pattern", constraintPattern), p3("patternProperties", constraintPatternProperties), @@ -123,6 +126,7 @@ var constraints = []*constraint{ todo("unevaluatedProperties", vfrom(VersionDraft2019_09)), p2("uniqueItems", constraintUniqueItems), todo("writeOnly", vfrom(VersionDraft7)), + todo("xml", vset(VersionOpenAPI3_0)), } func todo(name string, versions versionSet) *constraint { diff --git a/encoding/jsonschema/version.go b/encoding/jsonschema/version.go index 88656adbc..b3f9994f5 100644 --- a/encoding/jsonschema/version.go +++ b/encoding/jsonschema/version.go @@ -30,13 +30,21 @@ const ( VersionDraft7 // http://json-schema.org/draft-07/schema# VersionDraft2019_09 // https://json-schema.org/draft/2019-09/schema VersionDraft2020_12 // https://json-schema.org/draft/2020-12/schema + VersionOpenAPI3_0 // OpenAPI 3.0 numVersions // unknown ) +var ( + openAPI3_0 = vset(VersionOpenAPI3_0) + notOpenAPI3_0 = allVersions &^ vset(VersionOpenAPI3_0) +) + type versionSet int -const allVersions = versionSet(1<= Version(len(_Version_index)-1) { diff --git a/encoding/openapi/decode.go b/encoding/openapi/decode.go index d60e82d68..84fc0cfb8 100644 --- a/encoding/openapi/decode.go +++ b/encoding/openapi/decode.go @@ -15,6 +15,7 @@ package openapi import ( + "fmt" "strings" "cuelang.org/go/cue" @@ -41,15 +42,24 @@ func Extract(data cue.InstanceOrValue, c *Config) (*ast.File, error) { } } - js, err := jsonschema.Extract(data, &jsonschema.Config{ - Root: oapiSchemas, - Map: openAPIMapping, - }) + v := data.Value() + versionValue := v.LookupPath(cue.MakePath(cue.Str("openapi"))) + if versionValue.Err() != nil { + return nil, fmt.Errorf("openapi field is required but not found") + } + version, err := versionValue.String() if err != nil { - return nil, err + return nil, fmt.Errorf("invalid openapi field (must be string): %v", err) + } + var schemaVersion jsonschema.Version + switch { + case strings.HasPrefix(version, "v3.0."): + schemaVersion = jsonschema.OpenAPI3_0 + case strings.HasPrefix(version, "v3.1."): + schemaVersion = VersionDraft2020_12 + default: + return nil, fmt.Errorf("unknown OpenAPI version %q", version) } - - v := data.Value() doc, _ := v.LookupPath(cue.MakePath(cue.Str("info"), cue.Str("title"))).String() // Required if s, _ := v.LookupPath(cue.MakePath(cue.Str("info"), cue.Str("description"))).String(); s != "" { @@ -65,6 +75,14 @@ func Extract(data cue.InstanceOrValue, c *Config) (*ast.File, error) { add(cg) } + js, err := jsonschema.Extract(data, &jsonschema.Config{ + Root: oapiSchemas, + Map: openAPIMapping, + DefaultVersion: schemaVersion, + }) + if err != nil { + return nil, err + } preamble := js.Preamble() body := js.Decls[len(preamble):] for _, d := range preamble {