Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate recursive parent descriptor with ValidationContext #2

Merged
merged 4 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ func (c *Compiler) validateSchema(r *resource, v interface{}, vloc string) error
}

validate := func(meta *Schema) error {
return meta.validateValue(v, v, vloc)
return meta.validateValue(v, v, vloc, NewParentDescriptor(nil, nil))
}

meta := r.draft.meta
Expand Down
6 changes: 6 additions & 0 deletions extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type ValidationContext struct {
result validationResult
doc interface{}
vloc string
parent ParentDescriptor
validate func(sch *Schema, schPath string, v interface{}, vpath string) error
validateInplace func(sch *Schema, schPath string) error
validationError func(keywordPath string, format string, a ...interface{}) *ValidationError
Expand Down Expand Up @@ -137,6 +138,11 @@ func (ctx ValidationContext) GetDoc() interface{} {
return ctx.doc
}

// GetParent returns the parent descriptor of the value being validated.
func (ctx ValidationContext) GetParent() ParentDescriptor {
return ctx.parent
}

// Group is used by extensions to group multiple errors as causes to parent error.
// This is useful in implementing keywords like allOf where each schema specified
// in allOf can result a validationError.
Expand Down
31 changes: 24 additions & 7 deletions schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ type Schema struct {
Extensions map[string]ExtSchema
}

type ParentDescriptor map[string]interface{}

func NewParentDescriptor(parent ParentDescriptor, value interface{}) ParentDescriptor {
pd := ParentDescriptor{}

if parent != nil {
pd["parent"] = parent
pd["value"] = value
}

return pd
}

func (s *Schema) String() string {
return s.Location
}
Expand Down Expand Up @@ -165,10 +178,10 @@ func (s *Schema) hasVocab(name string) bool {
// returns InfiniteLoopError if it detects loop during validation.
// returns InvalidJSONTypeError if it detects any non json value in v.
func (s *Schema) Validate(v interface{}) (err error) {
return s.validateValue(v, v, "")
return s.validateValue(v, v, "", NewParentDescriptor(nil, nil))
}

func (s *Schema) validateValue(doc interface{}, v interface{}, vloc string) (err error) {
func (s *Schema) validateValue(doc interface{}, v interface{}, vloc string, parent ParentDescriptor) (err error) {
defer func() {
if r := recover(); r != nil {
switch r := r.(type) {
Expand All @@ -179,7 +192,7 @@ func (s *Schema) validateValue(doc interface{}, v interface{}, vloc string) (err
}
}
}()
if _, err := s.validate(nil, 0, "", doc, v, vloc); err != nil {
if _, err := s.validate(nil, 0, "", doc, v, vloc, parent); err != nil {
ve := ValidationError{
KeywordLocation: "",
AbsoluteKeywordLocation: s.Location,
Expand All @@ -192,7 +205,7 @@ func (s *Schema) validateValue(doc interface{}, v interface{}, vloc string) (err
}

// validate validates given value v with this schema.
func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc interface{}, v interface{}, vloc string) (result validationResult, err error) {
func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc interface{}, v interface{}, vloc string, parent ParentDescriptor) (result validationResult, err error) {
validationError := func(keywordPath string, format string, a ...interface{}) *ValidationError {
return &ValidationError{
KeywordLocation: keywordLocation(scope, keywordPath),
Expand All @@ -202,6 +215,7 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc inter
}
}

thisValue := v
sref := schemaRef{spath, s, false}
if err := checkLoop(scope[len(scope)-vscope:], sref); err != nil {
panic(err)
Expand All @@ -225,15 +239,18 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc inter

validate := func(sch *Schema, schPath string, v interface{}, vpath string) error {
vloc := vloc
subParent := parent

if vpath != "" {
vloc += "/" + vpath
subParent = NewParentDescriptor(parent, thisValue)
}
_, err := sch.validate(scope, 0, schPath, doc, v, vloc)
_, err := sch.validate(scope, 0, schPath, doc, v, vloc, subParent)
return err
}

validateInplace := func(sch *Schema, schPath string) error {
vr, err := sch.validate(scope, vscope, schPath, doc, v, vloc)
vr, err := sch.validate(scope, vscope, schPath, doc, v, vloc, parent)
if err == nil {
// update result
for pname := range result.unevalProps {
Expand Down Expand Up @@ -735,7 +752,7 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc inter
}

for _, ext := range s.Extensions {
if err := ext.Validate(ValidationContext{result, doc, vloc, validate, validateInplace, validationError}, v); err != nil {
if err := ext.Validate(ValidationContext{result, doc, vloc, parent, validate, validateInplace, validationError}, v); err != nil {
errors = append(errors, err)
}
}
Expand Down
Loading