diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go index 8296efff477..1abd67f90b3 100644 --- a/encoding/jsonschema/constraints.go +++ b/encoding/jsonschema/constraints.go @@ -15,7 +15,6 @@ package jsonschema import ( - "fmt" "math/big" "path" "regexp" @@ -143,13 +142,7 @@ var constraints = []*constraint{ return } s.id = u - - obj := s.object(n) - - // TODO: handle the case where this is always defined and we don't want - // to include the default value. - obj.Elts = append(obj.Elts, &ast.Attribute{ - Text: fmt.Sprintf("@jsonschema(id=%q)", u)}) + s.idPos = n.Pos() }), // Generic constraint diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go index 82cba4f11d2..0582e18a2e9 100644 --- a/encoding/jsonschema/decode.go +++ b/encoding/jsonschema/decode.go @@ -336,6 +336,7 @@ type state struct { exclusiveMax bool // For OpenAPI and legacy support. jsonschema string id *url.URL // base URI for $ref + idPos token.Pos definitions []ast.Decl @@ -362,9 +363,19 @@ type refs struct { refs []*ast.Ident } +func (s *state) idTag() *ast.Attribute { + return &ast.Attribute{ + At: s.idPos, + Text: fmt.Sprintf("@jsonschema(id=%q)", s.id)} +} + func (s *state) object(n cue.Value) *ast.StructLit { if s.obj == nil { s.obj = &ast.StructLit{} + + if s.id != nil { + s.obj.Elts = append(s.obj.Elts, s.idTag()) + } s.add(n, objectType, s.obj) } return s.obj @@ -382,7 +393,8 @@ func (s *state) hasConstraints() bool { return len(s.patterns) > 0 || s.title != "" || s.description != "" || - s.obj != nil + s.obj != nil || + s.id != nil } const allTypes = cue.NullKind | cue.BoolKind | cue.NumberKind | cue.IntKind | @@ -499,6 +511,17 @@ outer: s.linkReferences() + // If an "$id" exists and has not been included in any object constraints + if s.id != nil && s.obj == nil { + if st, ok := e.(*ast.StructLit); ok { + st.Elts = append([]ast.Decl{s.idTag()}, st.Elts...) + } else { + st = &ast.StructLit{Elts: []ast.Decl{s.idTag()}} + st.Elts = append(st.Elts, &ast.EmbedDecl{Expr: e}) + e = st + } + } + return e } diff --git a/encoding/jsonschema/testdata/id_in_oneOf.txtar b/encoding/jsonschema/testdata/id_in_oneOf.txtar new file mode 100644 index 00000000000..62fd52636e3 --- /dev/null +++ b/encoding/jsonschema/testdata/id_in_oneOf.txtar @@ -0,0 +1,28 @@ +-- schema.json -- +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://test.example/foo", + "oneOf": [ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://1.test.example/string", + "type": "string" + }, + { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://2.test.example/object", + "type": "object" + } + ] +} + +-- out/decode/cue -- +@jsonschema(schema="http://json-schema.org/draft-07/schema#") +@jsonschema(id="https://test.example/foo") +{ + @jsonschema(id="https://1.test.example/string") + string +} | { + @jsonschema(id="https://2.test.example/object") + ... +} diff --git a/encoding/jsonschema/testdata/id_scalar.txtar b/encoding/jsonschema/testdata/id_scalar.txtar new file mode 100644 index 00000000000..ff86e853a2e --- /dev/null +++ b/encoding/jsonschema/testdata/id_scalar.txtar @@ -0,0 +1,12 @@ +-- schema.json -- +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://cuelang.org/go/encoding/openapi/testdata/id_scalar.json", + + "type": "string" +} + +-- out/decode/cue -- +@jsonschema(schema="http://json-schema.org/draft-07/schema#") +@jsonschema(id="http://cuelang.org/go/encoding/openapi/testdata/id_scalar.json") +string