diff options
Diffstat (limited to 'vendor/github.com/xeipuuv/gojsonschema/validation.go')
-rw-r--r-- | vendor/github.com/xeipuuv/gojsonschema/validation.go | 293 |
1 files changed, 187 insertions, 106 deletions
diff --git a/vendor/github.com/xeipuuv/gojsonschema/validation.go b/vendor/github.com/xeipuuv/gojsonschema/validation.go index 9afea2518..090c11e93 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/validation.go +++ b/vendor/github.com/xeipuuv/gojsonschema/validation.go @@ -27,6 +27,7 @@ package gojsonschema import ( "encoding/json" + "math/big" "reflect" "regexp" "strconv" @@ -60,24 +61,27 @@ func (v *Schema) Validate(l JSONLoader) (*Result, error) { return nil, err } + return v.validateDocument(root), nil +} + +func (v *Schema) validateDocument(root interface{}) *Result { // begin validation result := &Result{} - context := newJsonContext(STRING_CONTEXT_ROOT, nil) + context := NewJsonContext(STRING_CONTEXT_ROOT, nil) v.rootSchema.validateRecursive(v.rootSchema, root, result, context) - return result, nil - + return result } -func (v *subSchema) subValidateWithContext(document interface{}, context *jsonContext) *Result { +func (v *subSchema) subValidateWithContext(document interface{}, context *JsonContext) *Result { result := &Result{} v.validateRecursive(v, document, result, context) return result } // Walker function to validate the json recursively against the subSchema -func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) { if internalLogEnabled { internalLog("validateRecursive %s", context.String()) @@ -93,7 +97,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i // Check for null value if currentNode == nil { if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) { - result.addError( + result.addInternalError( new(InvalidTypeError), context, currentNode, @@ -114,18 +118,18 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i value := currentNode.(json.Number) - _, isValidInt64, _ := checkJsonNumber(value) + isInt := checkJsonInteger(value) - validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isValidInt64 && currentSubSchema.types.Contains(TYPE_INTEGER)) + validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isInt && currentSubSchema.types.Contains(TYPE_INTEGER)) if currentSubSchema.types.IsTyped() && !validType { givenType := TYPE_INTEGER - if !isValidInt64 { + if !isInt { givenType = TYPE_NUMBER } - result.addError( + result.addInternalError( new(InvalidTypeError), context, currentNode, @@ -154,7 +158,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i case reflect.Slice: if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) { - result.addError( + result.addInternalError( new(InvalidTypeError), context, currentNode, @@ -177,7 +181,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i case reflect.Map: if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) { - result.addError( + result.addInternalError( new(InvalidTypeError), context, currentNode, @@ -202,7 +206,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i for _, pSchema := range currentSubSchema.propertiesChildren { nextNode, ok := castCurrentNode[pSchema.property] if ok { - subContext := newJsonContext(pSchema.property, context) + subContext := NewJsonContext(pSchema.property, context) v.validateRecursive(pSchema, nextNode, result, subContext) } } @@ -212,7 +216,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i case reflect.Bool: if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) { - result.addError( + result.addInternalError( new(InvalidTypeError), context, currentNode, @@ -234,7 +238,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i case reflect.String: if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) { - result.addError( + result.addInternalError( new(InvalidTypeError), context, currentNode, @@ -263,7 +267,7 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i } // Different kinds of validation there, subSchema / common / array / object / string... -func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) { if internalLogEnabled { internalLog("validateSchema %s", context.String()) @@ -287,7 +291,7 @@ func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode inte } if !validatedAnyOf { - result.addError(new(NumberAnyOfError), context, currentNode, ErrorDetails{}) + result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{}) if bestValidationResult != nil { // add error messages of closest matching subSchema as @@ -313,7 +317,7 @@ func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode inte if nbValidated != 1 { - result.addError(new(NumberOneOfError), context, currentNode, ErrorDetails{}) + result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{}) if nbValidated == 0 { // add error messages of closest matching subSchema as @@ -336,14 +340,14 @@ func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode inte } if nbValidated != len(currentSubSchema.allOf) { - result.addError(new(NumberAllOfError), context, currentNode, ErrorDetails{}) + result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{}) } } if currentSubSchema.not != nil { validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context) if validationResult.Valid() { - result.addError(new(NumberNotError), context, currentNode, ErrorDetails{}) + result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{}) } } @@ -356,7 +360,7 @@ func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode inte case []string: for _, dependOnKey := range dependency { if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved { - result.addError( + result.addInternalError( new(MissingDependencyError), context, currentNode, @@ -367,31 +371,65 @@ func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode inte case *subSchema: dependency.validateRecursive(dependency, currentNode, result, context) - } } } } } + if currentSubSchema._if != nil { + validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context) + if currentSubSchema._then != nil && validationResultIf.Valid() { + validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context) + if !validationResultThen.Valid() { + result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{}) + result.mergeErrors(validationResultThen) + } + } + if currentSubSchema._else != nil && !validationResultIf.Valid() { + validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context) + if !validationResultElse.Valid() { + result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{}) + result.mergeErrors(validationResultElse) + } + } + } + result.incrementScore() } -func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { if internalLogEnabled { internalLog("validateCommon %s", context.String()) internalLog(" %v", value) } + // const: + if currentSubSchema._const != nil { + vString, err := marshalWithoutNumber(value) + if err != nil { + result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) + } + if *vString != *currentSubSchema._const { + result.addInternalError(new(ConstError), + context, + value, + ErrorDetails{ + "allowed": *currentSubSchema._const, + }, + ) + } + } + // enum: if len(currentSubSchema.enum) > 0 { has, err := currentSubSchema.ContainsEnum(value) if err != nil { - result.addError(new(InternalError), context, value, ErrorDetails{"error": err}) + result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) } if !has { - result.addError( + result.addInternalError( new(EnumError), context, value, @@ -405,7 +443,7 @@ func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{ result.incrementScore() } -func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *JsonContext) { if internalLogEnabled { internalLog("validateArray %s", context.String()) @@ -417,7 +455,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface // TODO explain if currentSubSchema.itemsChildrenIsSingleSchema { for i := range value { - subContext := newJsonContext(strconv.Itoa(i), context) + subContext := NewJsonContext(strconv.Itoa(i), context) validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext) result.mergeErrors(validationResult) } @@ -428,7 +466,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface // while we have both schemas and values, check them against each other for i := 0; i != nbItems && i != nbValues; i++ { - subContext := newJsonContext(strconv.Itoa(i), context) + subContext := NewJsonContext(strconv.Itoa(i), context) validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext) result.mergeErrors(validationResult) } @@ -440,12 +478,12 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface switch currentSubSchema.additionalItems.(type) { case bool: if !currentSubSchema.additionalItems.(bool) { - result.addError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{}) + result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{}) } case *subSchema: additionalItemSchema := currentSubSchema.additionalItems.(*subSchema) for i := nbItems; i != nbValues; i++ { - subContext := newJsonContext(strconv.Itoa(i), context) + subContext := NewJsonContext(strconv.Itoa(i), context) validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext) result.mergeErrors(validationResult) } @@ -457,7 +495,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface // minItems & maxItems if currentSubSchema.minItems != nil { if nbValues < int(*currentSubSchema.minItems) { - result.addError( + result.addInternalError( new(ArrayMinItemsError), context, value, @@ -467,7 +505,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface } if currentSubSchema.maxItems != nil { if nbValues > int(*currentSubSchema.maxItems) { - result.addError( + result.addInternalError( new(ArrayMaxItemsError), context, value, @@ -479,27 +517,59 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface // uniqueItems: if currentSubSchema.uniqueItems { var stringifiedItems []string - for _, v := range value { - vString, err := marshalToJsonString(v) + for j, v := range value { + vString, err := marshalWithoutNumber(v) if err != nil { - result.addError(new(InternalError), context, value, ErrorDetails{"err": err}) + result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err}) } - if isStringInSlice(stringifiedItems, *vString) { - result.addError( + if i := indexStringInSlice(stringifiedItems, *vString); i > -1 { + result.addInternalError( new(ItemsMustBeUniqueError), context, value, - ErrorDetails{"type": TYPE_ARRAY}, + ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j}, ) } stringifiedItems = append(stringifiedItems, *vString) } } + // contains: + + if currentSubSchema.contains != nil { + validatedOne := false + var bestValidationResult *Result + + for i, v := range value { + subContext := NewJsonContext(strconv.Itoa(i), context) + + validationResult := currentSubSchema.contains.subValidateWithContext(v, subContext) + if validationResult.Valid() { + validatedOne = true + break + } else { + if bestValidationResult == nil || validationResult.score > bestValidationResult.score { + bestValidationResult = validationResult + } + } + } + if !validatedOne { + result.addInternalError( + new(ArrayContainsError), + context, + value, + ErrorDetails{}, + ) + if bestValidationResult != nil { + result.mergeErrors(bestValidationResult) + } + } + } + result.incrementScore() } -func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *JsonContext) { if internalLogEnabled { internalLog("validateObject %s", context.String()) @@ -509,7 +579,7 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string // minProperties & maxProperties: if currentSubSchema.minProperties != nil { if len(value) < int(*currentSubSchema.minProperties) { - result.addError( + result.addInternalError( new(ArrayMinPropertiesError), context, value, @@ -519,7 +589,7 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string } if currentSubSchema.maxProperties != nil { if len(value) > int(*currentSubSchema.maxProperties) { - result.addError( + result.addInternalError( new(ArrayMaxPropertiesError), context, value, @@ -534,7 +604,7 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string if ok { result.incrementScore() } else { - result.addError( + result.addInternalError( new(RequiredError), context, value, @@ -565,7 +635,7 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string if found { if pp_has && !pp_match { - result.addError( + result.addInternalError( new(AdditionalPropertyNotAllowedError), context, value[pk], @@ -576,7 +646,7 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string } else { if !pp_has || !pp_match { - result.addError( + result.addInternalError( new(AdditionalPropertyNotAllowedError), context, value[pk], @@ -628,7 +698,7 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string if pp_has && !pp_match { - result.addError( + result.addInternalError( new(InvalidPropertyPatternError), context, value[pk], @@ -642,10 +712,25 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string } } + // propertyNames: + if currentSubSchema.propertyNames != nil { + for pk := range value { + validationResult := currentSubSchema.propertyNames.subValidateWithContext(pk, context) + if !validationResult.Valid() { + result.addInternalError(new(InvalidPropertyNameError), + context, + value, ErrorDetails{ + "property": pk, + }) + result.mergeErrors(validationResult) + } + } + } + result.incrementScore() } -func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *jsonContext) (has bool, matched bool) { +func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *JsonContext) (has bool, matched bool) { if internalLogEnabled { internalLog("validatePatternProperty %s", context.String()) @@ -659,12 +744,10 @@ func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key str for pk, pv := range currentSubSchema.patternProperties { if matches, _ := regexp.MatchString(pk, key); matches { has = true - subContext := newJsonContext(key, context) + subContext := NewJsonContext(key, context) validationResult := pv.subValidateWithContext(value, subContext) result.mergeErrors(validationResult) - if validationResult.Valid() { - validatedkey = true - } + validatedkey = true } } @@ -677,7 +760,7 @@ func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key str return has, true } -func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { // Ignore JSON numbers if isJsonNumber(value) { @@ -699,7 +782,7 @@ func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{ // minLength & maxLength: if currentSubSchema.minLength != nil { if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) { - result.addError( + result.addInternalError( new(StringLengthGTEError), context, value, @@ -709,7 +792,7 @@ func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{ } if currentSubSchema.maxLength != nil { if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) { - result.addError( + result.addInternalError( new(StringLengthLTEError), context, value, @@ -721,7 +804,7 @@ func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{ // pattern: if currentSubSchema.pattern != nil { if !currentSubSchema.pattern.MatchString(stringValue) { - result.addError( + result.addInternalError( new(DoesNotMatchPatternError), context, value, @@ -734,7 +817,7 @@ func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{ // format if currentSubSchema.format != "" { if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) { - result.addError( + result.addInternalError( new(DoesNotMatchFormatError), context, value, @@ -746,7 +829,7 @@ func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{ result.incrementScore() } -func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) { +func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { // Ignore non numbers if !isJsonNumber(value) { @@ -759,79 +842,77 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{ } number := value.(json.Number) - float64Value, _ := number.Float64() + float64Value, _ := new(big.Rat).SetString(string(number)) // multipleOf: if currentSubSchema.multipleOf != nil { - - if !isFloat64AnInteger(float64Value / *currentSubSchema.multipleOf) { - result.addError( + if q := new(big.Rat).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() { + result.addInternalError( new(MultipleOfError), context, resultErrorFormatJsonNumber(number), - ErrorDetails{"multiple": *currentSubSchema.multipleOf}, + ErrorDetails{"multiple": new(big.Float).SetRat(currentSubSchema.multipleOf)}, ) } } //maximum & exclusiveMaximum: if currentSubSchema.maximum != nil { - if currentSubSchema.exclusiveMaximum { - if float64Value >= *currentSubSchema.maximum { - result.addError( - new(NumberLTError), - context, - resultErrorFormatJsonNumber(number), - ErrorDetails{ - "max": resultErrorFormatNumber(*currentSubSchema.maximum), - }, - ) - } - } else { - if float64Value > *currentSubSchema.maximum { - result.addError( - new(NumberLTEError), - context, - resultErrorFormatJsonNumber(number), - ErrorDetails{ - "max": resultErrorFormatNumber(*currentSubSchema.maximum), - }, - ) - } + if float64Value.Cmp(currentSubSchema.maximum) == 1 { + result.addInternalError( + new(NumberLTEError), + context, + resultErrorFormatJsonNumber(number), + ErrorDetails{ + "max": currentSubSchema.maximum, + }, + ) + } + } + if currentSubSchema.exclusiveMaximum != nil { + if float64Value.Cmp(currentSubSchema.exclusiveMaximum) >= 0 { + result.addInternalError( + new(NumberLTError), + context, + resultErrorFormatJsonNumber(number), + ErrorDetails{ + "max": currentSubSchema.exclusiveMaximum, + }, + ) } } //minimum & exclusiveMinimum: if currentSubSchema.minimum != nil { - if currentSubSchema.exclusiveMinimum { - if float64Value <= *currentSubSchema.minimum { - result.addError( - new(NumberGTError), - context, - resultErrorFormatJsonNumber(number), - ErrorDetails{ - "min": resultErrorFormatNumber(*currentSubSchema.minimum), - }, - ) - } - } else { - if float64Value < *currentSubSchema.minimum { - result.addError( - new(NumberGTEError), - context, - resultErrorFormatJsonNumber(number), - ErrorDetails{ - "min": resultErrorFormatNumber(*currentSubSchema.minimum), - }, - ) - } + if float64Value.Cmp(currentSubSchema.minimum) == -1 { + result.addInternalError( + new(NumberGTEError), + context, + resultErrorFormatJsonNumber(number), + ErrorDetails{ + "min": currentSubSchema.minimum, + }, + ) + } + } + if currentSubSchema.exclusiveMinimum != nil { + if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 { + // if float64Value <= *currentSubSchema.minimum { + result.addInternalError( + new(NumberGTError), + context, + resultErrorFormatJsonNumber(number), + ErrorDetails{ + "min": currentSubSchema.exclusiveMinimum, + }, + ) } } // format if currentSubSchema.format != "" { if !FormatCheckers.IsFormat(currentSubSchema.format, float64Value) { - result.addError( + result.addInternalError( new(DoesNotMatchFormatError), context, value, |