Skip to content

Commit

Permalink
Add ValidateWithContext schema method to pass context.Context (#3)
Browse files Browse the repository at this point in the history
* Add ValidateWithContext schema method to pass context.Context

* Add public method to get supplied context.
  • Loading branch information
eugenea authored Sep 27, 2024
1 parent 531774d commit 5689f47
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 8 deletions.
3 changes: 2 additions & 1 deletion compiler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jsonschema

import (
"context"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -852,7 +853,7 @@ func (c *Compiler) validateSchema(r *resource, v interface{}, vloc string) error
}

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

meta := r.draft.meta
Expand Down
8 changes: 8 additions & 0 deletions extension.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package jsonschema

import "context"

// ExtCompiler compiles custom keyword(s) into ExtSchema.
type ExtCompiler interface {
// Compile compiles the custom keywords in schema m and returns its compiled representation.
Expand Down Expand Up @@ -89,6 +91,7 @@ func (ctx CompilerContext) GetResourceSchema() *Schema {

// ValidationContext provides additional context required in validating for extension.
type ValidationContext struct {
externalContext context.Context
result validationResult
doc interface{}
vloc string
Expand Down Expand Up @@ -143,6 +146,11 @@ func (ctx ValidationContext) GetParent() ParentDescriptor {
return ctx.parent
}

// GetExternalContext returns the external context supplied by user.
func (ctx ValidationContext) GetExternalContext() context.Context {
return ctx.externalContext
}

// 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
28 changes: 21 additions & 7 deletions schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jsonschema

import (
"bytes"
"context"
"encoding/json"
"fmt"
"hash/maphash"
Expand Down Expand Up @@ -178,10 +179,23 @@ 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, "", NewParentDescriptor(nil, nil))
return s.validateValue(context.TODO(), v, v, "", NewParentDescriptor(nil, nil))
}

func (s *Schema) validateValue(doc interface{}, v interface{}, vloc string, parent ParentDescriptor) (err error) {
// ValidateWithContext validates given doc, against the json-schema s with the context.
//
// the v must be the raw json value. for number precision
// unmarshal with json.UseNumber().
// ctx is the context which may carry additional information to be used inside of schema extension code.
//
// returns *ValidationError if v does not confirm with schema s.
// returns InfiniteLoopError if it detects loop during validation.
// returns InvalidJSONTypeError if it detects any non json value in v.
func (s *Schema) ValidateWithContext(ctx context.Context, v interface{}) (err error) {
return s.validateValue(ctx, v, v, "", NewParentDescriptor(nil, nil))
}

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

// validate validates given value v with this schema.
func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc interface{}, v interface{}, vloc string, parent ParentDescriptor) (result validationResult, err error) {
func (s *Schema) validate(ctx context.Context, 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 Down Expand Up @@ -245,12 +259,12 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, doc inter
vloc += "/" + vpath
subParent = NewParentDescriptor(parent, thisValue)
}
_, err := sch.validate(scope, 0, schPath, doc, v, vloc, subParent)
_, err := sch.validate(ctx, 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, parent)
vr, err := sch.validate(ctx, scope, vscope, schPath, doc, v, vloc, parent)
if err == nil {
// update result
for pname := range result.unevalProps {
Expand Down Expand Up @@ -752,7 +766,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, parent, validate, validateInplace, validationError}, v); err != nil {
if err := ext.Validate(ValidationContext{ctx, result, doc, vloc, parent, validate, validateInplace, validationError}, v); err != nil {
errors = append(errors, err)
}
}
Expand Down

0 comments on commit 5689f47

Please sign in to comment.