Skip to content

Commit

Permalink
refactor(jsonschema): further cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Arqu committed May 20, 2020
1 parent 4bc1017 commit f41640c
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 130 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ import (

func main() {
var schemaData = []byte(`{
"title": "Person",
"type": "object",
"$id": "https://qri.io/schema/",
"$comment" : "sample comment",
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"type": "string"
Expand Down Expand Up @@ -118,7 +118,7 @@ The [godoc](https://godoc.org/github.com/qri-io/jsonschema) gives an example of

It involves three steps that should happen _before_ allocating any Schema instances that use the validator:
1. create a custom type that implements the `Keyword` interface
2. Load the appropriate draf keyword set (see `draft2019_09_keywords.go`)
2. Load the appropriate draft keyword set (see `draft2019_09_keywords.go`)
3. call RegisterKeyword with the keyword you'd like to detect in JSON, and a `KeyMaker` function.


Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/qri-io/jsonschema
go 1.13

require (
github.com/pkg/profile v1.4.0
github.com/qri-io/jsonpointer v0.1.1
github.com/sergi/go-diff v1.0.0
github.com/stretchr/testify v1.3.0 // indirect
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI=
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qri-io/jsonpointer v0.1.0 h1:OcTtTmorodUCRc2CZhj/ZwOET8zVj6uo0ArEmzoThZI=
github.com/qri-io/jsonpointer v0.1.0/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA=
github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
Expand Down
34 changes: 23 additions & 11 deletions keyword.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,26 @@ var notSupported = map[string]bool{
"contentSchema": true,
"deprecated": true,

// backward compatibility
// backward compatibility with draft7
"definitions": true,
"dependencies": true,
}

var keywordRegistry = map[string]KeyMaker{}
var keywordOrder = map[string]int{}
var keywordInsertOrder = map[string]int{}
var (
keywordRegistry = map[string]KeyMaker{}
keywordOrder = map[string]int{}
keywordInsertOrder = map[string]int{}
)

// IsKeyword validates if a given prop string is a registered keyword
func IsKeyword(prop string) bool {
// IsRegisteredKeyword validates if a given prop string is a registered keyword
func IsRegisteredKeyword(prop string) bool {
_, ok := keywordRegistry[prop]
return ok
}

// GetKeyword returns a new instance of the keyword
func GetKeyword(prop string) Keyword {
if !IsKeyword(prop) {
if !IsRegisteredKeyword(prop) {
return NewVoid()
}
return keywordRegistry[prop]()
Expand Down Expand Up @@ -95,11 +97,21 @@ type Keyword interface {
// validation errors (if any) to an outparam slice of KeyErrors
ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError)

// Register subscribes all subschema to the parent schema
// which is later used for ref resolution
// Register builds up the schema tree by evaluating the current key
// and the current location pointer which is later used with resolve to
// navigate the schema tree and substitute the propper schema for a given
// reference.
Register(uri string, registry *SchemaRegistry)
// Resolve resolves the pointer to the respective schema
// allowing validation to flow through referenced schemas
// Resolve unraps a pointer to the destination schema
// It usually starts with a $ref validation call which
// uses the pointer token by token to navigate the
// schema tree to get to the last schema in the chain.
// Since every keyword can have it's specifics around resolving
// each keyword need to implement it's own version of Resolve.
// Terminal keywords should respond with nil as it's not a schema
// Keywords that wrap a schema should return the appropriate schema.
// In case of a non-existing location it will fail to resolve, return nil
// on ref resolution and error out.
Resolve(pointer jptr.Pointer, uri string) *Schema
}

Expand Down
2 changes: 1 addition & 1 deletion keyword_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestRegisterFooKeyword(t *testing.T) {

RegisterKeyword("foo", newFoo)

if !IsKeyword("foo") {
if !IsRegisteredKeyword("foo") {
t.Errorf("expected %s to be added as a default validator", "foo")
}
}
18 changes: 9 additions & 9 deletions keywords_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (it *Items) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for Items
func (it Items) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[Items] Validating")
schemaDebug("[Items] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if it.single {
subCtx := NewSchemaContextFromSource(*schCtx)
Expand Down Expand Up @@ -162,7 +162,7 @@ func (m *MaxItems) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for MaxItems
func (m MaxItems) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[MaxItems] Validating")
schemaDebug("[MaxItems] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if len(arr) > int(m) {
AddErrorCtx(errs, schCtx, fmt.Sprintf("array length %d exceeds %d max", len(arr), m))
Expand All @@ -189,7 +189,7 @@ func (m *MinItems) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for MinItems
func (m MinItems) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[MinItems] Validating")
schemaDebug("[MinItems] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if len(arr) < int(m) {
AddErrorCtx(errs, schCtx, fmt.Sprintf("array length %d below %d minimum items", len(arr), m))
Expand All @@ -216,7 +216,7 @@ func (u *UniqueItems) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for UniqueItems
func (u UniqueItems) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[UniqueItems] Validating")
schemaDebug("[UniqueItems] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
found := []interface{}{}
for _, elem := range arr {
Expand Down Expand Up @@ -251,7 +251,7 @@ func (c *Contains) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for Contains
func (c *Contains) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[Contains] Validating")
schemaDebug("[Contains] Validating")
v := Schema(*c)
if arr, ok := schCtx.Instance.([]interface{}); ok {
valid := false
Expand Down Expand Up @@ -321,7 +321,7 @@ func (m *MaxContains) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for MaxContains
func (m MaxContains) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[MaxContains] Validating")
schemaDebug("[MaxContains] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if containsCount, ok := schCtx.Misc["containsCount"]; ok {
if containsCount.(int) > int(m) {
Expand Down Expand Up @@ -349,7 +349,7 @@ func (m *MinContains) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for MinContains
func (m MinContains) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[MinContains] Validating")
schemaDebug("[MinContains] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if containsCount, ok := schCtx.Misc["containsCount"]; ok {
if containsCount.(int) < int(m) {
Expand Down Expand Up @@ -379,7 +379,7 @@ func (ai *AdditionalItems) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for AdditionalItems
func (ai *AdditionalItems) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[AdditionalItems] Validating")
schemaDebug("[AdditionalItems] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if schCtx.LastEvaluatedIndex > -1 && schCtx.LastEvaluatedIndex < len(arr) {
for i := schCtx.LastEvaluatedIndex + 1; i < len(arr); i++ {
Expand Down Expand Up @@ -437,7 +437,7 @@ func (ui *UnevaluatedItems) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for UnevaluatedItems
func (ui *UnevaluatedItems) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[UnevaluatedItems] Validating")
schemaDebug("[UnevaluatedItems] Validating")
if arr, ok := schCtx.Instance.([]interface{}); ok {
if schCtx.LastEvaluatedIndex < len(arr) {
for i := schCtx.LastEvaluatedIndex + 1; i < len(arr); i++ {
Expand Down
8 changes: 4 additions & 4 deletions keywords_boolean.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (a *AllOf) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for AllOf
func (a *AllOf) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[AllOf] Validating")
schemaDebug("[AllOf] Validating")
contextCopy := NewSchemaContextFromSourceClean(*schCtx)
invalid := false
for i, sch := range *a {
Expand Down Expand Up @@ -136,7 +136,7 @@ func (a *AnyOf) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for AnyOf
func (a *AnyOf) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[AnyOf] Validating")
schemaDebug("[AnyOf] Validating")
for i, sch := range *a {
subCtx := NewSchemaContextFromSourceClean(*schCtx)
if subCtx.BaseRelativeLocation != nil {
Expand Down Expand Up @@ -220,7 +220,7 @@ func (o *OneOf) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for OneOf
func (o *OneOf) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[OneOf] Validating")
schemaDebug("[OneOf] Validating")
matched := false
contextCopy := NewSchemaContextFromSourceClean(*schCtx)
for i, sch := range *o {
Expand Down Expand Up @@ -292,7 +292,7 @@ func (n *Not) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for Not
func (n *Not) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[Not] Validating")
schemaDebug("[Not] Validating")
subCtx := NewSchemaContextFromSource(*schCtx)
if subCtx.BaseRelativeLocation != nil {
if newPtr, err := schCtx.BaseRelativeLocation.Descendant("not"); err == nil {
Expand Down
7 changes: 4 additions & 3 deletions keywords_conditional.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ func (f *If) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for If
func (f *If) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[If] Validating")
schemaDebug("[If] Validating")
thenKW := schCtx.Local.keywords["then"]
elseKW := schCtx.Local.keywords["else"]

if thenKW == nil && elseKW == nil {
// no then or else for if, aborting validation
schemaDebug("[If] Aborting validation as no then or else is present")
return
}

Expand Down Expand Up @@ -96,7 +97,7 @@ func (t *Then) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for Then
func (t *Then) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[Then] Validating")
schemaDebug("[Then] Validating")
ifResult, okIf := schCtx.Misc["ifResult"]
if !okIf {
// if not found
Expand Down Expand Up @@ -166,7 +167,7 @@ func (e *Else) Resolve(pointer jptr.Pointer, uri string) *Schema {

// ValidateFromContext implements the Keyword interface for Else
func (e *Else) ValidateFromContext(schCtx *SchemaContext, errs *[]KeyError) {
SchemaDebug("[Else] Validating")
schemaDebug("[Else] Validating")
ifResult, okIf := schCtx.Misc["ifResult"]
if !okIf {
// if not found
Expand Down
Loading

0 comments on commit f41640c

Please sign in to comment.