package validate import ( "context" "fmt" "net/url" "strconv" "strings" "github.com/santhosh-tekuri/jsonschema/v6" ) // These schema-bearing positions mirror the inherited subschema tables in // jsonschema/v6 v6.0.2 draft.go. Annotation objects are deliberately excluded. func checkSchemaLocations(ctx context.Context, compiler *jsonschema.Compiler, resourceURL string, document any, pointer string, compiled *jsonschema.Schema) error { if err := ctx.Err(); err != nil { return err } object, ok := document.(map[string]any) if !ok { return nil } version := compiled.DraftVersion mapKeys := []string{"definitions", "properties", "patternProperties", "dependencies"} singleKeys := []string{"not", "additionalProperties", "items", "additionalItems"} arrayKeys := []string{"allOf", "anyOf", "oneOf", "items"} if version >= 6 { singleKeys = append(singleKeys, "propertyNames", "contains") } if version >= 7 { singleKeys = append(singleKeys, "if", "then", "else") } if version >= 2019 { mapKeys = append(mapKeys, "$defs", "dependentSchemas") singleKeys = append(singleKeys, "unevaluatedProperties", "unevaluatedItems", "contentSchema") } if version >= 2020 { arrayKeys = append(arrayKeys, "prefixItems") } visit := func(value any, segments ...string) error { switch value.(type) { case map[string]any, bool: default: return nil } childPointer := pointer for _, segment := range segments { childPointer += "/" + strings.ReplaceAll(strings.ReplaceAll(segment, "~", "~0"), "/", "~1") } parsed, err := url.Parse(resourceURL) if err != nil { return err } parsed.Fragment = childPointer child, err := compileJSONSchema(ctx, parsed.String(), compiler.Compile) if err != nil { return fmt.Errorf("compile declared schema %q: %w", childPointer, err) } return checkSchemaLocations(ctx, compiler, resourceURL, value, childPointer, child) } for _, key := range mapKeys { entries, ok := object[key].(map[string]any) if !ok { continue } for name, value := range entries { if err := visit(value, key, name); err != nil { return err } } } for _, key := range singleKeys { if value, ok := object[key]; ok { if err := visit(value, key); err != nil { return err } } } for _, key := range arrayKeys { entries, ok := object[key].([]any) if !ok { continue } for i, value := range entries { if err := visit(value, key, strconv.Itoa(i)); err != nil { return err } } } return ctx.Err() }