diff options
Diffstat (limited to 'vendor/github.com/xeipuuv/gojsonschema/schema.go')
-rw-r--r-- | vendor/github.com/xeipuuv/gojsonschema/schema.go | 452 |
1 files changed, 298 insertions, 154 deletions
diff --git a/vendor/github.com/xeipuuv/gojsonschema/schema.go b/vendor/github.com/xeipuuv/gojsonschema/schema.go index 2cac71e9b..323fe8559 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/schema.go +++ b/vendor/github.com/xeipuuv/gojsonschema/schema.go @@ -27,8 +27,8 @@ package gojsonschema import ( - // "encoding/json" "errors" + "math/big" "reflect" "regexp" "text/template" @@ -46,39 +46,7 @@ var ( ) func NewSchema(l JSONLoader) (*Schema, error) { - ref, err := l.JsonReference() - if err != nil { - return nil, err - } - - d := Schema{} - d.pool = newSchemaPool(l.LoaderFactory()) - d.documentReference = ref - d.referencePool = newSchemaReferencePool() - - var doc interface{} - if ref.String() != "" { - // Get document from schema pool - spd, err := d.pool.GetDocument(d.documentReference) - if err != nil { - return nil, err - } - doc = spd.Document - } else { - // Load JSON directly - doc, err = l.LoadJSON() - if err != nil { - return nil, err - } - d.pool.SetStandaloneDocument(doc) - } - - err = d.parse(doc) - if err != nil { - return nil, err - } - - return &d, nil + return NewSchemaLoader().Compile(l) } type Schema struct { @@ -88,8 +56,8 @@ type Schema struct { referencePool *schemaReferencePool } -func (d *Schema) parse(document interface{}) error { - d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY} +func (d *Schema) parse(document interface{}, draft Draft) error { + d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY, draft: &draft} return d.parseSchema(document, d.rootSchema) } @@ -105,6 +73,23 @@ func (d *Schema) SetRootSchemaName(name string) { // func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error { + if currentSchema.draft == nil { + if currentSchema.parent == nil { + return errors.New("Draft not set") + } + currentSchema.draft = currentSchema.parent.draft + } + + // As of draft 6 "true" is equivalent to an empty schema "{}" and false equals "{"not":{}}" + if *currentSchema.draft >= Draft6 && isKind(documentNode, reflect.Bool) { + b := documentNode.(bool) + if b { + documentNode = map[string]interface{}{} + } else { + documentNode = map[string]interface{}{"not": true} + } + } + if !isKind(documentNode, reflect.Map) { return errors.New(formatErrorDescription( Locale.ParseError(), @@ -116,81 +101,67 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) m := documentNode.(map[string]interface{}) - if currentSchema == d.rootSchema { + if currentSchema.parent == nil { currentSchema.ref = &d.documentReference + currentSchema.id = &d.documentReference } - // $subSchema - if existsMapKey(m, KEY_SCHEMA) { - if !isKind(m[KEY_SCHEMA], reflect.String) { - return errors.New(formatErrorDescription( - Locale.InvalidType(), - ErrorDetails{ - "expected": TYPE_STRING, - "given": KEY_SCHEMA, - }, - )) - } - schemaRef := m[KEY_SCHEMA].(string) - schemaReference, err := gojsonreference.NewJsonReference(schemaRef) - currentSchema.subSchema = &schemaReference - if err != nil { - return err - } + if currentSchema.id == nil && currentSchema.parent != nil { + currentSchema.id = currentSchema.parent.id } - // $ref - if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) { + // In draft 6 the id keyword was renamed to $id + // Hybrid mode uses the old id by default + var keyID string + + switch *currentSchema.draft { + case Draft4: + keyID = KEY_ID + case Hybrid: + keyID = KEY_ID_NEW + if existsMapKey(m, KEY_ID) { + keyID = KEY_ID + } + default: + keyID = KEY_ID_NEW + } + if existsMapKey(m, keyID) && !isKind(m[keyID], reflect.String) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, - "given": KEY_REF, + "given": keyID, }, )) } - if k, ok := m[KEY_REF].(string); ok { - + if k, ok := m[keyID].(string); ok { jsonReference, err := gojsonreference.NewJsonReference(k) if err != nil { return err } - - if jsonReference.HasFullUrl { - currentSchema.ref = &jsonReference - } else { - inheritedReference, err := currentSchema.ref.Inherits(jsonReference) - if err != nil { - return err - } - - currentSchema.ref = inheritedReference - } - - if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok { - currentSchema.refSchema = sch - + if currentSchema == d.rootSchema { + currentSchema.id = &jsonReference } else { - err := d.parseReference(documentNode, currentSchema, k) + ref, err := currentSchema.parent.id.Inherits(jsonReference) if err != nil { return err } - - return nil + currentSchema.id = ref } } // definitions if existsMapKey(m, KEY_DEFINITIONS) { - if isKind(m[KEY_DEFINITIONS], reflect.Map) { - currentSchema.definitions = make(map[string]*subSchema) - for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) { - if isKind(dv, reflect.Map) { - newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref} - currentSchema.definitions[dk] = newSchema + if isKind(m[KEY_DEFINITIONS], reflect.Map, reflect.Bool) { + for _, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) { + if isKind(dv, reflect.Map, reflect.Bool) { + + newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema} + err := d.parseSchema(dv, newSchema) + if err != nil { - return errors.New(err.Error()) + return err } } else { return errors.New(formatErrorDescription( @@ -214,20 +185,6 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } - // id - if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) { - return errors.New(formatErrorDescription( - Locale.InvalidType(), - ErrorDetails{ - "expected": TYPE_STRING, - "given": KEY_ID, - }, - )) - } - if k, ok := m[KEY_ID].(string); ok { - currentSchema.id = &k - } - // title if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) { return errors.New(formatErrorDescription( @@ -256,6 +213,39 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) currentSchema.description = &k } + // $ref + if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_STRING, + "given": KEY_REF, + }, + )) + } + + if k, ok := m[KEY_REF].(string); ok { + + jsonReference, err := gojsonreference.NewJsonReference(k) + if err != nil { + return err + } + + currentSchema.ref = &jsonReference + + if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok { + currentSchema.refSchema = sch + } else { + err := d.parseReference(documentNode, currentSchema) + + if err != nil { + return err + } + + return nil + } + } + // type if existsMapKey(m, KEY_TYPE) { if isKind(m[KEY_TYPE], reflect.String) { @@ -357,6 +347,26 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } } + // propertyNames + if existsMapKey(m, KEY_PROPERTY_NAMES) && *currentSchema.draft >= Draft6 { + if isKind(m[KEY_PROPERTY_NAMES], reflect.Map, reflect.Bool) { + newSchema := &subSchema{property: KEY_PROPERTY_NAMES, parent: currentSchema, ref: currentSchema.ref} + currentSchema.propertyNames = newSchema + err := d.parseSchema(m[KEY_PROPERTY_NAMES], newSchema) + if err != nil { + return err + } + } else { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": STRING_SCHEMA, + "given": KEY_PATTERN_PROPERTIES, + }, + )) + } + } + // dependencies if existsMapKey(m, KEY_DEPENDENCIES) { err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema) @@ -369,7 +379,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if existsMapKey(m, KEY_ITEMS) { if isKind(m[KEY_ITEMS], reflect.Slice) { for _, itemElement := range m[KEY_ITEMS].([]interface{}) { - if isKind(itemElement, reflect.Map) { + if isKind(itemElement, reflect.Map, reflect.Bool) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) @@ -388,7 +398,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } currentSchema.itemsChildrenIsSingleSchema = false } - } else if isKind(m[KEY_ITEMS], reflect.Map) { + } else if isKind(m[KEY_ITEMS], reflect.Map, reflect.Bool) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) @@ -443,7 +453,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) }, )) } - if *multipleOfValue <= 0 { + if multipleOfValue.Cmp(big.NewRat(0, 1)) <= 0 { return errors.New(formatErrorDescription( Locale.GreaterThanZero(), ErrorDetails{"number": KEY_MULTIPLE_OF}, @@ -464,20 +474,62 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) { - if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { + switch *currentSchema.draft { + case Draft4: + if !isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_BOOLEAN, + "given": KEY_EXCLUSIVE_MINIMUM, + }, + )) + } if currentSchema.minimum == nil { return errors.New(formatErrorDescription( Locale.CannotBeUsedWithout(), ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM}, )) } - exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool) - currentSchema.exclusiveMinimum = exclusiveMinimumValue - } else { - return errors.New(formatErrorDescription( - Locale.MustBeOfA(), - ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN}, - )) + if m[KEY_EXCLUSIVE_MINIMUM].(bool) { + currentSchema.exclusiveMinimum = currentSchema.minimum + currentSchema.minimum = nil + } + case Hybrid: + if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { + if currentSchema.minimum == nil { + return errors.New(formatErrorDescription( + Locale.CannotBeUsedWithout(), + ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM}, + )) + } + if m[KEY_EXCLUSIVE_MINIMUM].(bool) { + currentSchema.exclusiveMinimum = currentSchema.minimum + currentSchema.minimum = nil + } + } else if isJsonNumber(m[KEY_EXCLUSIVE_MINIMUM]) { + currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM]) + } else { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER, + "given": KEY_EXCLUSIVE_MINIMUM, + }, + )) + } + default: + if isJsonNumber(m[KEY_EXCLUSIVE_MINIMUM]) { + currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM]) + } else { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_NUMBER, + "given": KEY_EXCLUSIVE_MINIMUM, + }, + )) + } } } @@ -493,29 +545,62 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) { - if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { + switch *currentSchema.draft { + case Draft4: + if !isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_BOOLEAN, + "given": KEY_EXCLUSIVE_MAXIMUM, + }, + )) + } if currentSchema.maximum == nil { return errors.New(formatErrorDescription( Locale.CannotBeUsedWithout(), ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM}, )) } - exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool) - currentSchema.exclusiveMaximum = exclusiveMaximumValue - } else { - return errors.New(formatErrorDescription( - Locale.MustBeOfA(), - ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER}, - )) - } - } - - if currentSchema.minimum != nil && currentSchema.maximum != nil { - if *currentSchema.minimum > *currentSchema.maximum { - return errors.New(formatErrorDescription( - Locale.CannotBeGT(), - ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM}, - )) + if m[KEY_EXCLUSIVE_MAXIMUM].(bool) { + currentSchema.exclusiveMaximum = currentSchema.maximum + currentSchema.maximum = nil + } + case Hybrid: + if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { + if currentSchema.maximum == nil { + return errors.New(formatErrorDescription( + Locale.CannotBeUsedWithout(), + ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM}, + )) + } + if m[KEY_EXCLUSIVE_MAXIMUM].(bool) { + currentSchema.exclusiveMaximum = currentSchema.maximum + currentSchema.maximum = nil + } + } else if isJsonNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { + currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM]) + } else { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER, + "given": KEY_EXCLUSIVE_MAXIMUM, + }, + )) + } + default: + if isJsonNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { + currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM]) + } else { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": TYPE_NUMBER, + "given": KEY_EXCLUSIVE_MAXIMUM, + }, + )) + } } } @@ -705,8 +790,24 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } } + if existsMapKey(m, KEY_CONTAINS) && *currentSchema.draft >= Draft6 { + newSchema := &subSchema{property: KEY_CONTAINS, parent: currentSchema, ref: currentSchema.ref} + currentSchema.contains = newSchema + err := d.parseSchema(m[KEY_CONTAINS], newSchema) + if err != nil { + return err + } + } + // validation : all + if existsMapKey(m, KEY_CONST) && *currentSchema.draft >= Draft6 { + err := currentSchema.AddConst(m[KEY_CONST]) + if err != nil { + return err + } + } + if existsMapKey(m, KEY_ENUM) { if isKind(m[KEY_ENUM], reflect.Slice) { for _, v := range m[KEY_ENUM].([]interface{}) { @@ -780,7 +881,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } if existsMapKey(m, KEY_NOT) { - if isKind(m[KEY_NOT], reflect.Map) { + if isKind(m[KEY_NOT], reflect.Map, reflect.Bool) { newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref} currentSchema.SetNot(newSchema) err := d.parseSchema(m[KEY_NOT], newSchema) @@ -795,48 +896,91 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } } + if *currentSchema.draft >= Draft7 { + if existsMapKey(m, KEY_IF) { + if isKind(m[KEY_IF], reflect.Map, reflect.Bool) { + newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref} + currentSchema.SetIf(newSchema) + err := d.parseSchema(m[KEY_IF], newSchema) + if err != nil { + return err + } + } else { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT}, + )) + } + } + + if existsMapKey(m, KEY_THEN) { + if isKind(m[KEY_THEN], reflect.Map, reflect.Bool) { + newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref} + currentSchema.SetThen(newSchema) + err := d.parseSchema(m[KEY_THEN], newSchema) + if err != nil { + return err + } + } else { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT}, + )) + } + } + + if existsMapKey(m, KEY_ELSE) { + if isKind(m[KEY_ELSE], reflect.Map, reflect.Bool) { + newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref} + currentSchema.SetElse(newSchema) + err := d.parseSchema(m[KEY_ELSE], newSchema) + if err != nil { + return err + } + } else { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT}, + )) + } + } + } + return nil } -func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error { - var refdDocumentNode interface{} - jsonPointer := currentSchema.ref.GetPointer() - standaloneDocument := d.pool.GetStandaloneDocument() +func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error { + var ( + refdDocumentNode interface{} + dsp *schemaPoolDocument + err error + ) - if standaloneDocument != nil { + newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref} - var err error - refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument) - if err != nil { - return err - } + d.referencePool.Add(currentSchema.ref.String(), newSchema) - } else { - dsp, err := d.pool.GetDocument(*currentSchema.ref) - if err != nil { - return err - } + dsp, err = d.pool.GetDocument(*currentSchema.ref) + if err != nil { + return err + } + newSchema.id = currentSchema.ref - refdDocumentNode, _, err = jsonPointer.Get(dsp.Document) - if err != nil { - return err - } + refdDocumentNode = dsp.Document + newSchema.draft = dsp.Draft + if err != nil { + return err } - if !isKind(refdDocumentNode, reflect.Map) { + if !isKind(refdDocumentNode, reflect.Map, reflect.Bool) { return errors.New(formatErrorDescription( Locale.MustBeOfType(), ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT}, )) } - // returns the loaded referenced subSchema for the caller to update its current subSchema - newSchemaDocument := refdDocumentNode.(map[string]interface{}) - newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref} - d.referencePool.Add(currentSchema.ref.String()+reference, newSchema) - - err := d.parseSchema(newSchemaDocument, newSchema) + err = d.parseSchema(refdDocumentNode, newSchema) if err != nil { return err } @@ -904,7 +1048,7 @@ func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subS currentSchema.dependencies[k] = valuesToRegister } - case reflect.Map: + case reflect.Map, reflect.Bool: depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref} err := d.parseSchema(m[k], depSchema) if err != nil { |