Skip to content

Commit 880023b

Browse files
rogpeppemvdan
authored andcommitted
encoding/jsonschema: pass comments to DefineSchema
This is a retry of https://cuelang.org/cl/1209259, which got reverted in https://cuelang.org/cl/1209359 to un-break Unity while we figured out why we couldn't get it to work as expected. Currently if a schema has a doc comment, that comment is lost when it's passed to DefineSchema, so add an argument that lets the callback see it. This is a breaking API change, but this API is very deliberately marked as experimental so that shouldn't matter. An alternative that would not involve breaking the API might be to add the comment to the Expr, but that proves quite awkward to use because in practice callers tend to be defining a field at a path so they'd have to jump through the hoop of extracting (and removing) the comment from the expression only to add it back into the outer struct literal. Signed-off-by: Roger Peppe <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Reviewed-by: Daniel Martí <[email protected]> Change-Id: I45564d3f0f7ba34f435a8adb47f341dc71f187ac Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1209367
1 parent b6f250e commit 880023b

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

encoding/jsonschema/decode.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ type definedSchema struct {
101101
// schema holds the actual syntax for the schema. This
102102
// is nil if the entry was created by a reference only.
103103
schema ast.Expr
104+
105+
// comment holds any doc comment associated with the above schema.
106+
comment *ast.CommentGroup
104107
}
105108

106109
// addImport registers
@@ -215,7 +218,7 @@ func (d *decoder) decode(v cue.Value) *ast.File {
215218
// have been mapped to an external location.
216219
for _, def := range d.defs {
217220
if def.schema != nil && def.importPath != "" {
218-
d.cfg.DefineSchema(def.importPath, def.path, def.schema)
221+
d.cfg.DefineSchema(def.importPath, def.path, def.schema, def.comment)
219222
}
220223
}
221224
}
@@ -749,7 +752,7 @@ func (s *state) schema(n cue.Value) ast.Expr {
749752
// schemaState returns a new state value derived from s.
750753
// n holds the JSONSchema node to translate to a schema.
751754
// types holds the set of possible types that the value can hold.
752-
func (s0 *state) schemaState(n cue.Value, types cue.Kind) (expr ast.Expr, ingo schemaInfo) {
755+
func (s0 *state) schemaState(n cue.Value, types cue.Kind) (expr ast.Expr, info schemaInfo) {
753756
s := &state{
754757
up: s0,
755758
schemaInfo: schemaInfo{
@@ -763,7 +766,7 @@ func (s0 *state) schemaState(n cue.Value, types cue.Kind) (expr ast.Expr, ingo s
763766
}
764767
defer func() {
765768
// Perhaps replace the schema expression with a reference.
766-
expr = s.maybeDefine(expr)
769+
expr = s.maybeDefine(expr, info)
767770
}()
768771
if n.Kind() == cue.BoolKind {
769772
if vfrom(VersionDraft6).contains(s.schemaVersion) {
@@ -847,12 +850,13 @@ func (s0 *state) schemaState(n cue.Value, types cue.Kind) (expr ast.Expr, ingo s
847850
// it just returns expr itself.
848851
// TODO also report whether the schema has been defined at a place
849852
// where it can be unified with something else?
850-
func (s *state) maybeDefine(expr ast.Expr) ast.Expr {
853+
func (s *state) maybeDefine(expr ast.Expr, info schemaInfo) ast.Expr {
851854
def := s.definedSchemaForNode(s.pos)
852855
if def == nil || len(def.path.Selectors()) == 0 {
853856
return expr
854857
}
855858
def.schema = expr
859+
def.comment = info.comment()
856860
if def.importPath == "" {
857861
// It's a local definition that's not at the root.
858862
if !s.builder.put(def.path, expr, s.comment()) {

encoding/jsonschema/decode_test.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,11 @@ func TestMapRefExternalRefForInternalSchema(t *testing.T) {
337337
v := cuecontext.New().CompileString(`
338338
type: "object"
339339
$id: "https://this.test"
340-
$defs: foo: type: ["number", "string"]
340+
$defs: foo: {
341+
description: "foo can be a number or a string"
342+
type: ["number", "string"]
343+
}
344+
$defs: bar: type: "boolean"
341345
$ref: "#/$defs/foo"
342346
`)
343347
var calls []string
@@ -347,14 +351,19 @@ $ref: "#/$defs/foo"
347351
calls = append(calls, loc.String())
348352
switch loc.ID.String() {
349353
case "https://this.test#/$defs/foo":
350-
return "otherpkg.example/foo", cue.ParsePath("#foo"), nil
354+
return "otherpkg.example/foo", cue.ParsePath("#x"), nil
355+
case "https://this.test#/$defs/bar":
356+
return "otherpkg.example/bar", cue.ParsePath("#x"), nil
351357
case "https://this.test":
352358
return "", cue.Path{}, nil
353359
}
354360
t.Errorf("unexpected ID")
355361
return "", cue.Path{}, fmt.Errorf("unexpected ID %q passed to MapRef", loc.ID)
356362
},
357-
DefineSchema: func(importPath string, path cue.Path, e ast.Expr) {
363+
DefineSchema: func(importPath string, path cue.Path, e ast.Expr, c *ast.CommentGroup) {
364+
if c != nil {
365+
ast.AddComment(e, c)
366+
}
358367
data, err := format.Node(e)
359368
if err != nil {
360369
t.Errorf("cannot format: %v", err)
@@ -368,20 +377,22 @@ $ref: "#/$defs/foo"
368377
if err != nil {
369378
t.Fatal(errors.Details(err, nil))
370379
}
371-
qt.Assert(t, qt.DeepEquals(calls, []string{
380+
qt.Check(t, qt.DeepEquals(calls, []string{
372381
"id=https://this.test#/$defs/foo localPath=$defs.foo",
382+
"id=https://this.test#/$defs/bar localPath=$defs.bar",
373383
"id=https://this.test localPath=",
374384
}))
375-
qt.Assert(t, qt.Equals(string(b), `
385+
qt.Check(t, qt.Equals(string(b), `
376386
import "otherpkg.example/foo"
377387
378388
@jsonschema(id="https://this.test")
379-
foo.#foo & {
389+
foo.#x & {
380390
...
381391
}
382392
`[1:]))
383-
qt.Assert(t, qt.DeepEquals(defines, map[string]string{
384-
"otherpkg.example/foo.#foo": "number | string",
393+
qt.Check(t, qt.DeepEquals(defines, map[string]string{
394+
"otherpkg.example/bar.#x": "bool",
395+
"otherpkg.example/foo.#x": "// foo can be a number or a string\nnumber | string",
385396
}))
386397
}
387398

encoding/jsonschema/jsonschema.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,15 @@ type Config struct {
216216
// DefineSchema is called, if not nil, for any schema that is defined
217217
// within the json schema being converted but is mapped somewhere
218218
// external via [Config.MapRef]. The invoker of [Extract] is
219-
// responsible for defining the schema e in the correct place as described
219+
// responsible for defining the schema in the correct place as described
220220
// by the import path and its relative CUE path.
221221
//
222222
// The importPath and path are exactly as returned by [Config.MapRef].
223223
// If this or [Config.MapRef] is nil this function will never be called.
224224
// Note that importPath will never be empty, because if MapRef
225225
// returns an empty importPath, it's specifying an internal schema
226226
// which will be defined accordingly.
227-
DefineSchema func(importPath string, path cue.Path, e ast.Expr)
227+
DefineSchema func(importPath string, path cue.Path, e ast.Expr, docComment *ast.CommentGroup)
228228

229229
// TODO: configurability to make it compatible with OpenAPI, such as
230230
// - locations of definitions: #/components/schemas, for instance.

0 commit comments

Comments
 (0)