Skip to content

Commit

Permalink
Propagate recursive parent descriptor with ValidationContext
Browse files Browse the repository at this point in the history
  • Loading branch information
vassilvk committed Jul 26, 2024
1 parent 1a2ef10 commit e5ee36d
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 8 deletions.
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, ParentDescriptor{})
}

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
}

// GetDoc 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
27 changes: 20 additions & 7 deletions schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ type Schema struct {
Extensions map[string]ExtSchema
}

type ParentDescriptor struct {
parent *ParentDescriptor
value interface{}
}

func (s *Schema) String() string {
return s.Location
}
Expand Down Expand Up @@ -165,10 +170,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, "", ParentDescriptor{})
}

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 +184,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 +197,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 +207,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 +231,22 @@ 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 = ParentDescriptor{
parent: &parent,
value: 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 +748,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

0 comments on commit e5ee36d

Please sign in to comment.