this repo has no description
0
fork

Configure Feed

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

encoding/jsonschema: implement `Config.DefineSchema`

This lets the caller know about internal schemas that have
been mapped to an external location.

Also add an explicit test for `MapRef`.

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

+125
+12
encoding/jsonschema/decode.go
··· 211 211 } 212 212 213 213 d.builder = structBuilder{} 214 + for _, def := range d.defs { 215 + def.schema = nil 216 + } 214 217 d.needAnotherPass = false 218 + } 219 + if d.cfg.DefineSchema != nil { 220 + // Let the caller know about any internal schemas that 221 + // have been mapped to an external location. 222 + for _, def := range d.defs { 223 + if def.schema != nil && def.importPath != "" { 224 + d.cfg.DefineSchema(def.importPath, def.path, def.schema) 225 + } 226 + } 215 227 } 216 228 f, err := d.builder.syntax() 217 229 if err != nil {
+97
encoding/jsonschema/decode_test.go
··· 286 286 `[1:])) 287 287 } 288 288 289 + func TestMapRef(t *testing.T) { 290 + v := cuecontext.New().CompileString(` 291 + type: "object" 292 + $id: "https://this.test" 293 + $defs: foo: type: "string" 294 + properties: x: $ref: "https://something.test/foo#/$defs/blah" 295 + `) 296 + var calls []string 297 + expr, err := jsonschema.Extract(v, &jsonschema.Config{ 298 + MapRef: func(loc jsonschema.SchemaLoc) (string, cue.Path, error) { 299 + calls = append(calls, loc.String()) 300 + switch loc.ID.String() { 301 + case "https://this.test#/$defs/foo": 302 + return "", cue.ParsePath("#x.#def.#foo"), nil 303 + case "https://something.test/foo#/$defs/blah": 304 + return "other.test/something:blah", cue.ParsePath("#Foo.bar"), nil 305 + case "https://this.test": 306 + return "", cue.Path{}, nil 307 + } 308 + t.Errorf("unexpected ID") 309 + return "", cue.Path{}, fmt.Errorf("unexpected ID %q passed to MapRef", loc.ID) 310 + }, 311 + }) 312 + qt.Assert(t, qt.IsNil(err)) 313 + b, err := format.Node(expr, format.Simplify()) 314 + if err != nil { 315 + t.Fatal(errors.Details(err, nil)) 316 + } 317 + qt.Assert(t, qt.DeepEquals(calls, []string{ 318 + "id=https://this.test#/$defs/foo localPath=$defs.foo", 319 + "id=https://something.test/foo#/$defs/blah", 320 + "id=https://this.test localPath=", 321 + "id=https://something.test/foo#/$defs/blah", 322 + })) 323 + qt.Assert(t, qt.Equals(string(b), ` 324 + import "other.test/something:blah" 325 + 326 + @jsonschema(id="https://this.test") 327 + x?: blah.#Foo.bar 328 + 329 + #x: #def: #foo: string 330 + ... 331 + `[1:])) 332 + } 333 + 334 + func TestMapRefExternalRefForInternalSchema(t *testing.T) { 335 + v := cuecontext.New().CompileString(` 336 + type: "object" 337 + $id: "https://this.test" 338 + $defs: foo: type: ["number", "string"] 339 + $ref: "#/$defs/foo" 340 + `) 341 + var calls []string 342 + defines := make(map[string]string) 343 + expr, err := jsonschema.Extract(v, &jsonschema.Config{ 344 + MapRef: func(loc jsonschema.SchemaLoc) (string, cue.Path, error) { 345 + calls = append(calls, loc.String()) 346 + switch loc.ID.String() { 347 + case "https://this.test#/$defs/foo": 348 + return "otherpkg.example/foo", cue.ParsePath("#foo"), nil 349 + case "https://this.test": 350 + return "", cue.Path{}, nil 351 + } 352 + t.Errorf("unexpected ID") 353 + return "", cue.Path{}, fmt.Errorf("unexpected ID %q passed to MapRef", loc.ID) 354 + }, 355 + DefineSchema: func(importPath string, path cue.Path, e ast.Expr) { 356 + data, err := format.Node(e) 357 + if err != nil { 358 + t.Errorf("cannot format: %v", err) 359 + return 360 + } 361 + defines[fmt.Sprintf("%s.%v", importPath, path)] = string(data) 362 + }, 363 + }) 364 + qt.Assert(t, qt.IsNil(err)) 365 + b, err := format.Node(expr, format.Simplify()) 366 + if err != nil { 367 + t.Fatal(errors.Details(err, nil)) 368 + } 369 + qt.Assert(t, qt.DeepEquals(calls, []string{ 370 + "id=https://this.test#/$defs/foo localPath=$defs.foo", 371 + "id=https://this.test localPath=", 372 + })) 373 + qt.Assert(t, qt.Equals(string(b), ` 374 + import "otherpkg.example/foo" 375 + 376 + @jsonschema(id="https://this.test") 377 + foo.#foo & { 378 + ... 379 + } 380 + `[1:])) 381 + qt.Assert(t, qt.DeepEquals(defines, map[string]string{ 382 + "otherpkg.example/foo.#foo": "number | string", 383 + })) 384 + } 385 + 289 386 func TestX(t *testing.T) { 290 387 t.Skip() 291 388 data := `
+16
encoding/jsonschema/jsonschema.go
··· 205 205 // nil, [DefaultMapRef] will be used. 206 206 MapRef func(loc SchemaLoc) (importPath string, relPath cue.Path, err error) 207 207 208 + // NOTE: this method is currently experimental. Its usage and type 209 + // signature may change. 210 + // 211 + // DefineSchema is called, if not nil, for any schema that is defined 212 + // within the json schema being converted but is mapped somewhere 213 + // external via [Config.MapRef]. The invoker of [Extract] is 214 + // responsible for defining the schema e in the correct place as described 215 + // by the import path and its relative CUE path. 216 + // 217 + // The importPath and path are exactly as returned by [Config.MapRef]. 218 + // If this or [Config.MapRef] is nil this function will never be called. 219 + // Note that importPath will never be empty, because if MapRef 220 + // returns an empty importPath, it's specifying an internal schema 221 + // which will be defined accordingly. 222 + DefineSchema func(importPath string, path cue.Path, e ast.Expr) 223 + 208 224 // TODO: configurability to make it compatible with OpenAPI, such as 209 225 // - locations of definitions: #/components/schemas, for instance. 210 226 // - selection and definition of formats