From af62a0529bb37a87a0d71edf25de29b46138b1fe Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 30 Jun 2020 16:20:03 +0200 Subject: [PATCH] vendor: xeipuuv/gojsonschema v1.2.0 full diff: https://github.com/xeipuuv/gojsonschema/compare/v1.1.0...v1.2.0 - Fix a race condition when registering new formats. - Improve the performance of uniqueItems. - Fix an issue where integers would be shown as a fraction - Require format to be of type string and formats can now be registered after a schema is parsed. - Improve the handling of true and false schema. - Fix an issue where an invalid schema would cause a panic. - Properly handle file URIs that contain spaces. Signed-off-by: Sebastiaan van Stijn --- vendor.conf | 2 +- .../github.com/xeipuuv/gojsonschema/README.md | 3 +- .../github.com/xeipuuv/gojsonschema/draft.go | 13 +- .../github.com/xeipuuv/gojsonschema/errors.go | 104 ++++++--- .../xeipuuv/gojsonschema/format_checkers.go | 103 +++++---- vendor/github.com/xeipuuv/gojsonschema/go.mod | 7 + .../xeipuuv/gojsonschema/jsonContext.go | 1 + .../xeipuuv/gojsonschema/jsonLoader.go | 49 +++-- .../xeipuuv/gojsonschema/locales.go | 165 ++++++++++++++- .../github.com/xeipuuv/gojsonschema/result.go | 37 +++- .../github.com/xeipuuv/gojsonschema/schema.go | 75 ++++--- .../xeipuuv/gojsonschema/schemaLoader.go | 3 + .../xeipuuv/gojsonschema/schemaPool.go | 10 +- .../xeipuuv/gojsonschema/subSchema.go | 117 +--------- .../github.com/xeipuuv/gojsonschema/types.go | 4 + .../github.com/xeipuuv/gojsonschema/utils.go | 61 +----- .../xeipuuv/gojsonschema/validation.go | 199 ++++++------------ 17 files changed, 526 insertions(+), 427 deletions(-) create mode 100644 vendor/github.com/xeipuuv/gojsonschema/go.mod diff --git a/vendor.conf b/vendor.conf index 3ac5c36c09..480b65bdd6 100755 --- a/vendor.conf +++ b/vendor.conf @@ -70,7 +70,7 @@ github.com/tonistiigi/fsutil c2c7d7b0e1441705cd802e5699c0 github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2 github.com/xeipuuv/gojsonpointer 02993c407bfbf5f6dae44c4f4b1cf6a39b5fc5bb github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b -github.com/xeipuuv/gojsonschema f971f3cd73b2899de6923801c147f075263e0c50 # v1.1.0 +github.com/xeipuuv/gojsonschema 82fcdeb203eb6ab2a67d0a623d9c19e5e5a64927 # v1.2.0 golang.org/x/crypto 2aa609cf4a9d7d1126360de73b55b6002f9e052a golang.org/x/net 0de0cce0169b09b364e001f108dc0399ea8630b3 golang.org/x/oauth2 bf48bf16ab8d622ce64ec6ce98d2c98f916b6303 diff --git a/vendor/github.com/xeipuuv/gojsonschema/README.md b/vendor/github.com/xeipuuv/gojsonschema/README.md index 24ca347443..758f26df0f 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/README.md +++ b/vendor/github.com/xeipuuv/gojsonschema/README.md @@ -1,5 +1,6 @@ [![GoDoc](https://godoc.org/github.com/xeipuuv/gojsonschema?status.svg)](https://godoc.org/github.com/xeipuuv/gojsonschema) [![Build Status](https://travis-ci.org/xeipuuv/gojsonschema.svg)](https://travis-ci.org/xeipuuv/gojsonschema) +[![Go Report Card](https://goreportcard.com/badge/github.com/xeipuuv/gojsonschema)](https://goreportcard.com/report/github.com/xeipuuv/gojsonschema) # gojsonschema @@ -343,7 +344,7 @@ Not all formats defined in draft-07 are available. Implemented formats are: `email`, `uri` and `uri-reference` use the same validation code as their unicode counterparts `idn-email`, `iri` and `iri-reference`. If you rely on unicode support you should use the specific unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats. -The validation code for `uri`, `idn-email` and their relatives use mostly standard library code. Go 1.5 and 1.6 contain some minor bugs with handling URIs and unicode. You are encouraged to use Go 1.7+ if you rely on these formats. +The validation code for `uri`, `idn-email` and their relatives use mostly standard library code. For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this: diff --git a/vendor/github.com/xeipuuv/gojsonschema/draft.go b/vendor/github.com/xeipuuv/gojsonschema/draft.go index bfde4a2e1e..61298e7aa0 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/draft.go +++ b/vendor/github.com/xeipuuv/gojsonschema/draft.go @@ -22,8 +22,10 @@ import ( "github.com/xeipuuv/gojsonreference" ) +// Draft is a JSON-schema draft version type Draft int +// Supported Draft versions const ( Draft4 Draft = 4 Draft6 Draft = 6 @@ -42,17 +44,17 @@ var drafts draftConfigs func init() { drafts = []draftConfig{ - draftConfig{ + { Version: Draft4, MetaSchemaURL: "http://json-schema.org/draft-04/schema", MetaSchema: `{"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}`, }, - draftConfig{ + { Version: Draft6, MetaSchemaURL: "http://json-schema.org/draft-06/schema", MetaSchema: `{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}`, }, - draftConfig{ + { Version: Draft7, MetaSchemaURL: "http://json-schema.org/draft-07/schema", MetaSchema: `{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}`, @@ -90,6 +92,11 @@ func parseSchemaURL(documentNode interface{}) (string, *Draft, error) { if isKind(documentNode, reflect.Bool) { return "", nil, nil } + + if !isKind(documentNode, reflect.Map) { + return "", nil, errors.New("schema is invalid") + } + m := documentNode.(map[string]interface{}) if existsMapKey(m, KEY_SCHEMA) { diff --git a/vendor/github.com/xeipuuv/gojsonschema/errors.go b/vendor/github.com/xeipuuv/gojsonschema/errors.go index 2f01a1c2c7..e4e9814f31 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/errors.go +++ b/vendor/github.com/xeipuuv/gojsonschema/errors.go @@ -6,7 +6,7 @@ import ( "text/template" ) -var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"), sync.RWMutex{}} +var errorTemplates = errorTemplate{template.New("errors-new"), sync.RWMutex{}} // template.Template is not thread-safe for writing, so some locking is done // sync.RWMutex is used for efficiently locking when new templates are created @@ -16,157 +16,194 @@ type errorTemplate struct { } type ( - // RequiredError. ErrorDetails: property string + + // FalseError. ErrorDetails: - + FalseError struct { + ResultErrorFields + } + + // RequiredError indicates that a required field is missing + // ErrorDetails: property string RequiredError struct { ResultErrorFields } - // InvalidTypeError. ErrorDetails: expected, given + // InvalidTypeError indicates that a field has the incorrect type + // ErrorDetails: expected, given InvalidTypeError struct { ResultErrorFields } - // NumberAnyOfError. ErrorDetails: - + // NumberAnyOfError is produced in case of a failing "anyOf" validation + // ErrorDetails: - NumberAnyOfError struct { ResultErrorFields } - // NumberOneOfError. ErrorDetails: - + // NumberOneOfError is produced in case of a failing "oneOf" validation + // ErrorDetails: - NumberOneOfError struct { ResultErrorFields } - // NumberAllOfError. ErrorDetails: - + // NumberAllOfError is produced in case of a failing "allOf" validation + // ErrorDetails: - NumberAllOfError struct { ResultErrorFields } - // NumberNotError. ErrorDetails: - + // NumberNotError is produced if a "not" validation failed + // ErrorDetails: - NumberNotError struct { ResultErrorFields } - // MissingDependencyError. ErrorDetails: dependency + // MissingDependencyError is produced in case of a "missing dependency" problem + // ErrorDetails: dependency MissingDependencyError struct { ResultErrorFields } - // InternalError. ErrorDetails: error + // InternalError indicates an internal error + // ErrorDetails: error InternalError struct { ResultErrorFields } - // ConstError. ErrorDetails: allowed + // ConstError indicates a const error + // ErrorDetails: allowed ConstError struct { ResultErrorFields } - // EnumError. ErrorDetails: allowed + // EnumError indicates an enum error + // ErrorDetails: allowed EnumError struct { ResultErrorFields } - // ArrayNoAdditionalItemsError. ErrorDetails: - + // ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed + // ErrorDetails: - ArrayNoAdditionalItemsError struct { ResultErrorFields } - // ArrayMinItemsError. ErrorDetails: min + // ArrayMinItemsError is produced if an array contains less items than the allowed minimum + // ErrorDetails: min ArrayMinItemsError struct { ResultErrorFields } - // ArrayMaxItemsError. ErrorDetails: max + // ArrayMaxItemsError is produced if an array contains more items than the allowed maximum + // ErrorDetails: max ArrayMaxItemsError struct { ResultErrorFields } - // ItemsMustBeUniqueError. ErrorDetails: type, i, j + // ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items + // ErrorDetails: type, i, j ItemsMustBeUniqueError struct { ResultErrorFields } - // ArrayContainsError. ErrorDetails: + // ArrayContainsError is produced if an array contains invalid items + // ErrorDetails: ArrayContainsError struct { ResultErrorFields } - // ArrayMinPropertiesError. ErrorDetails: min + // ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum + // ErrorDetails: min ArrayMinPropertiesError struct { ResultErrorFields } - // ArrayMaxPropertiesError. ErrorDetails: max + // ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum + // ErrorDetails: max ArrayMaxPropertiesError struct { ResultErrorFields } - // AdditionalPropertyNotAllowedError. ErrorDetails: property + // AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed + // ErrorDetails: property AdditionalPropertyNotAllowedError struct { ResultErrorFields } - // InvalidPropertyPatternError. ErrorDetails: property, pattern + // InvalidPropertyPatternError is produced if an pattern was found + // ErrorDetails: property, pattern InvalidPropertyPatternError struct { ResultErrorFields } - // InvalidPopertyNameError. ErrorDetails: property + // InvalidPropertyNameError is produced if an invalid-named property was found + // ErrorDetails: property InvalidPropertyNameError struct { ResultErrorFields } - // StringLengthGTEError. ErrorDetails: min + // StringLengthGTEError is produced if a string is shorter than the minimum required length + // ErrorDetails: min StringLengthGTEError struct { ResultErrorFields } - // StringLengthLTEError. ErrorDetails: max + // StringLengthLTEError is produced if a string is longer than the maximum allowed length + // ErrorDetails: max StringLengthLTEError struct { ResultErrorFields } - // DoesNotMatchPatternError. ErrorDetails: pattern + // DoesNotMatchPatternError is produced if a string does not match the defined pattern + // ErrorDetails: pattern DoesNotMatchPatternError struct { ResultErrorFields } - // DoesNotMatchFormatError. ErrorDetails: format + // DoesNotMatchFormatError is produced if a string does not match the defined format + // ErrorDetails: format DoesNotMatchFormatError struct { ResultErrorFields } - // MultipleOfError. ErrorDetails: multiple + // MultipleOfError is produced if a number is not a multiple of the defined multipleOf + // ErrorDetails: multiple MultipleOfError struct { ResultErrorFields } - // NumberGTEError. ErrorDetails: min + // NumberGTEError is produced if a number is lower than the allowed minimum + // ErrorDetails: min NumberGTEError struct { ResultErrorFields } - // NumberGTError. ErrorDetails: min + // NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set + // ErrorDetails: min NumberGTError struct { ResultErrorFields } - // NumberLTEError. ErrorDetails: max + // NumberLTEError is produced if a number is higher than the allowed maximum + // ErrorDetails: max NumberLTEError struct { ResultErrorFields } - // NumberLTError. ErrorDetails: max + // NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set + // ErrorDetails: max NumberLTError struct { ResultErrorFields } - // ConditionThenError. ErrorDetails: - + // ConditionThenError is produced if a condition's "then" validation is invalid + // ErrorDetails: - ConditionThenError struct { ResultErrorFields } - // ConditionElseError. ErrorDetails: - + // ConditionElseError is produced if a condition's "else" condition is invalid + // ErrorDetails: - ConditionElseError struct { ResultErrorFields } @@ -177,6 +214,9 @@ func newError(err ResultError, context *JsonContext, value interface{}, locale l var t string var d string switch err.(type) { + case *FalseError: + t = "false" + d = locale.False() case *RequiredError: t = "required" d = locale.Required() diff --git a/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go b/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go index 26217fca12..873ffc7d79 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go +++ b/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go @@ -13,6 +13,7 @@ import ( type ( // FormatChecker is the interface all formatters added to FormatCheckerChain must implement FormatChecker interface { + // IsFormat checks if input has the correct format and type IsFormat(input interface{}) bool } @@ -21,13 +22,13 @@ type ( formatters map[string]FormatChecker } - // EmailFormatter verifies email address formats + // EmailFormatChecker verifies email address formats EmailFormatChecker struct{} - // IPV4FormatChecker verifies IP addresses in the ipv4 format + // IPV4FormatChecker verifies IP addresses in the IPv4 format IPV4FormatChecker struct{} - // IPV6FormatChecker verifies IP addresses in the ipv6 format + // IPV6FormatChecker verifies IP addresses in the IPv6 format IPV6FormatChecker struct{} // DateTimeFormatChecker verifies date/time formats per RFC3339 5.6 @@ -53,8 +54,29 @@ type ( // http://tools.ietf.org/html/rfc3339#section-5.6 DateTimeFormatChecker struct{} + // DateFormatChecker verifies date formats + // + // Valid format: + // Full Date: YYYY-MM-DD + // + // Where + // YYYY = 4DIGIT year + // MM = 2DIGIT month ; 01-12 + // DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year DateFormatChecker struct{} + // TimeFormatChecker verifies time formats + // + // Valid formats: + // Partial Time: HH:MM:SS + // Full Time: HH:MM:SSZ-07:00 + // + // Where + // HH = 2DIGIT hour ; 00-23 + // MM = 2DIGIT ; 00-59 + // SS = 2DIGIT ; 00-58, 00-60 based on leap second rules + // T = Literal + // Z = Literal TimeFormatChecker struct{} // URIFormatChecker validates a URI with a valid Scheme per RFC3986 @@ -83,7 +105,7 @@ type ( ) var ( - // Formatters holds the valid formatters, and is a public variable + // FormatCheckers holds the valid formatters, and is a public variable // so library users can add custom formatters FormatCheckers = FormatCheckerChain{ formatters: map[string]FormatChecker{ @@ -119,7 +141,7 @@ var ( rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$") - lock = new(sync.Mutex) + lock = new(sync.RWMutex) ) // Add adds a FormatChecker to the FormatCheckerChain @@ -143,9 +165,9 @@ func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain { // Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name func (c *FormatCheckerChain) Has(name string) bool { - lock.Lock() + lock.RLock() _, ok := c.formatters[name] - lock.Unlock() + lock.RUnlock() return ok } @@ -153,55 +175,57 @@ func (c *FormatCheckerChain) Has(name string) bool { // IsFormat will check an input against a FormatChecker with the given name // to see if it is the correct format func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool { + lock.RLock() f, ok := c.formatters[name] + lock.RUnlock() + // If a format is unrecognized it should always pass validation if !ok { - return false + return true } return f.IsFormat(input) } +// IsFormat checks if input is a correctly formatted e-mail address func (f EmailFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } _, err := mail.ParseAddress(asString) - return err == nil } -// Credit: https://github.com/asaskevich/govalidator +// IsFormat checks if input is a correctly formatted IPv4-address func (f IPV4FormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } + // Credit: https://github.com/asaskevich/govalidator ip := net.ParseIP(asString) return ip != nil && strings.Contains(asString, ".") } -// Credit: https://github.com/asaskevich/govalidator +// IsFormat checks if input is a correctly formatted IPv6=address func (f IPV6FormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } + // Credit: https://github.com/asaskevich/govalidator ip := net.ParseIP(asString) return ip != nil && strings.Contains(asString, ":") } +// IsFormat checks if input is a correctly formatted date/time per RFC3339 5.6 func (f DateTimeFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } @@ -222,18 +246,20 @@ func (f DateTimeFormatChecker) IsFormat(input interface{}) bool { return false } +// IsFormat checks if input is a correctly formatted date (YYYY-MM-DD) func (f DateFormatChecker) IsFormat(input interface{}) bool { asString, ok := input.(string) - if ok == false { + if !ok { return false } _, err := time.Parse("2006-01-02", asString) return err == nil } +// IsFormat checks if input correctly formatted time (HH:MM:SS or HH:MM:SSZ-07:00) func (f TimeFormatChecker) IsFormat(input interface{}) bool { asString, ok := input.(string) - if ok == false { + if !ok { return false } @@ -245,10 +271,10 @@ func (f TimeFormatChecker) IsFormat(input interface{}) bool { return err == nil } +// IsFormat checks if input is correctly formatted URI with a valid Scheme per RFC3986 func (f URIFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } @@ -261,10 +287,10 @@ func (f URIFormatChecker) IsFormat(input interface{}) bool { return !strings.Contains(asString, `\`) } +// IsFormat checks if input is a correctly formatted URI or relative-reference per RFC3986 func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } @@ -272,9 +298,10 @@ func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool { return err == nil && !strings.Contains(asString, `\`) } +// IsFormat checks if input is a correctly formatted URI template per RFC6570 func (f URITemplateFormatChecker) IsFormat(input interface{}) bool { asString, ok := input.(string) - if ok == false { + if !ok { return false } @@ -286,31 +313,30 @@ func (f URITemplateFormatChecker) IsFormat(input interface{}) bool { return rxURITemplate.MatchString(u.Path) } +// IsFormat checks if input is a correctly formatted hostname func (f HostnameFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } return rxHostname.MatchString(asString) && len(asString) < 256 } +// IsFormat checks if input is a correctly formatted UUID func (f UUIDFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } return rxUUID.MatchString(asString) } -// IsFormat implements FormatChecker interface. +// IsFormat checks if input is a correctly formatted regular expression func (f RegexFormatChecker) IsFormat(input interface{}) bool { - asString, ok := input.(string) - if ok == false { + if !ok { return false } @@ -318,24 +344,23 @@ func (f RegexFormatChecker) IsFormat(input interface{}) bool { return true } _, err := regexp.Compile(asString) - if err != nil { - return false - } - return true + return err == nil } +// IsFormat checks if input is a correctly formatted JSON Pointer per RFC6901 func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool { asString, ok := input.(string) - if ok == false { + if !ok { return false } return rxJSONPointer.MatchString(asString) } +// IsFormat checks if input is a correctly formatted relative JSON Pointer func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool { asString, ok := input.(string) - if ok == false { + if !ok { return false } diff --git a/vendor/github.com/xeipuuv/gojsonschema/go.mod b/vendor/github.com/xeipuuv/gojsonschema/go.mod new file mode 100644 index 0000000000..b709d7fcd6 --- /dev/null +++ b/vendor/github.com/xeipuuv/gojsonschema/go.mod @@ -0,0 +1,7 @@ +module github.com/xeipuuv/gojsonschema + +require ( + github.com/stretchr/testify v1.3.0 + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 +) diff --git a/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go b/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go index f40668a74c..0e979707b4 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go +++ b/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go @@ -32,6 +32,7 @@ type JsonContext struct { tail *JsonContext } +// NewJsonContext creates a new JsonContext func NewJsonContext(head string, tail *JsonContext) *JsonContext { return &JsonContext{head, tail} } diff --git a/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go b/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go index cfa5f6a3a0..5d88af263e 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go +++ b/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go @@ -33,6 +33,7 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "os" "path/filepath" "runtime" @@ -43,8 +44,7 @@ import ( var osFS = osFileSystem(os.Open) -// JSON loader interface - +// JSONLoader defines the JSON loader interface type JSONLoader interface { JsonSource() interface{} LoadJSON() (interface{}, error) @@ -52,17 +52,22 @@ type JSONLoader interface { LoaderFactory() JSONLoaderFactory } +// JSONLoaderFactory defines the JSON loader factory interface type JSONLoaderFactory interface { + // New creates a new JSON loader for the given source New(source string) JSONLoader } +// DefaultJSONLoaderFactory is the default JSON loader factory type DefaultJSONLoaderFactory struct { } +// FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem type FileSystemJSONLoaderFactory struct { fs http.FileSystem } +// New creates a new JSON loader for the given source func (d DefaultJSONLoaderFactory) New(source string) JSONLoader { return &jsonReferenceLoader{ fs: osFS, @@ -70,6 +75,7 @@ func (d DefaultJSONLoaderFactory) New(source string) JSONLoader { } } +// New creates a new JSON loader for the given source func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader { return &jsonReferenceLoader{ fs: f.fs, @@ -80,6 +86,7 @@ func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader { // osFileSystem is a functional wrapper for os.Open that implements http.FileSystem. type osFileSystem func(string) (*os.File, error) +// Opens a file with the given name func (o osFileSystem) Open(name string) (http.File, error) { return o(name) } @@ -131,14 +138,20 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) { return nil, err } - refToUrl := reference - refToUrl.GetUrl().Fragment = "" + refToURL := reference + refToURL.GetUrl().Fragment = "" var document interface{} if reference.HasFileScheme { - filename := strings.TrimPrefix(refToUrl.String(), "file://") + filename := strings.TrimPrefix(refToURL.String(), "file://") + filename, err = url.QueryUnescape(filename) + + if err != nil { + return nil, err + } + if runtime.GOOS == "windows" { // on Windows, a file URL may have an extra leading slash, use slashes // instead of backslashes, and have spaces escaped @@ -153,7 +166,7 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) { } else { - document, err = l.loadFromHTTP(refToUrl.String()) + document, err = l.loadFromHTTP(refToURL.String()) if err != nil { return nil, err } @@ -169,7 +182,7 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) // returned cached versions for metaschemas for drafts 4, 6 and 7 // for performance and allow for easier offline use if metaSchema := drafts.GetMetaSchema(address); metaSchema != "" { - return decodeJsonUsingNumber(strings.NewReader(metaSchema)) + return decodeJSONUsingNumber(strings.NewReader(metaSchema)) } resp, err := http.Get(address) @@ -187,7 +200,7 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) return nil, err } - return decodeJsonUsingNumber(bytes.NewReader(bodyBuff)) + return decodeJSONUsingNumber(bytes.NewReader(bodyBuff)) } func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) { @@ -202,7 +215,7 @@ func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) { return nil, err } - return decodeJsonUsingNumber(bytes.NewReader(bodyBuff)) + return decodeJSONUsingNumber(bytes.NewReader(bodyBuff)) } @@ -224,13 +237,14 @@ func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory { return &DefaultJSONLoaderFactory{} } +// NewStringLoader creates a new JSONLoader, taking a string as source func NewStringLoader(source string) JSONLoader { return &jsonStringLoader{source: source} } func (l *jsonStringLoader) LoadJSON() (interface{}, error) { - return decodeJsonUsingNumber(strings.NewReader(l.JsonSource().(string))) + return decodeJSONUsingNumber(strings.NewReader(l.JsonSource().(string))) } @@ -252,12 +266,13 @@ func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory { return &DefaultJSONLoaderFactory{} } +// NewBytesLoader creates a new JSONLoader, taking a `[]byte` as source func NewBytesLoader(source []byte) JSONLoader { return &jsonBytesLoader{source: source} } func (l *jsonBytesLoader) LoadJSON() (interface{}, error) { - return decodeJsonUsingNumber(bytes.NewReader(l.JsonSource().([]byte))) + return decodeJSONUsingNumber(bytes.NewReader(l.JsonSource().([]byte))) } // JSON Go (types) loader @@ -279,6 +294,7 @@ func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory { return &DefaultJSONLoaderFactory{} } +// NewGoLoader creates a new JSONLoader from a given Go struct func NewGoLoader(source interface{}) JSONLoader { return &jsonGoLoader{source: source} } @@ -292,7 +308,7 @@ func (l *jsonGoLoader) LoadJSON() (interface{}, error) { return nil, err } - return decodeJsonUsingNumber(bytes.NewReader(jsonBytes)) + return decodeJSONUsingNumber(bytes.NewReader(jsonBytes)) } @@ -300,11 +316,13 @@ type jsonIOLoader struct { buf *bytes.Buffer } +// NewReaderLoader creates a new JSON loader using the provided io.Reader func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) { buf := &bytes.Buffer{} return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf) } +// NewWriterLoader creates a new JSON loader using the provided io.Writer func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) { buf := &bytes.Buffer{} return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf) @@ -315,7 +333,7 @@ func (l *jsonIOLoader) JsonSource() interface{} { } func (l *jsonIOLoader) LoadJSON() (interface{}, error) { - return decodeJsonUsingNumber(l.buf) + return decodeJSONUsingNumber(l.buf) } func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) { @@ -334,7 +352,8 @@ type jsonRawLoader struct { source interface{} } -func NewRawLoader(source interface{}) *jsonRawLoader { +// NewRawLoader creates a new JSON raw loader for the given source +func NewRawLoader(source interface{}) JSONLoader { return &jsonRawLoader{source: source} } func (l *jsonRawLoader) JsonSource() interface{} { @@ -350,7 +369,7 @@ func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory { return &DefaultJSONLoaderFactory{} } -func decodeJsonUsingNumber(r io.Reader) (interface{}, error) { +func decodeJSONUsingNumber(r io.Reader) (interface{}, error) { var document interface{} diff --git a/vendor/github.com/xeipuuv/gojsonschema/locales.go b/vendor/github.com/xeipuuv/gojsonschema/locales.go index 9b4570f011..a416225cdb 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/locales.go +++ b/vendor/github.com/xeipuuv/gojsonschema/locales.go @@ -28,61 +28,163 @@ package gojsonschema type ( // locale is an interface for defining custom error strings locale interface { + + // False returns a format-string for "false" schema validation errors + False() string + + // Required returns a format-string for "required" schema validation errors Required() string + + // InvalidType returns a format-string for "invalid type" schema validation errors InvalidType() string + + // NumberAnyOf returns a format-string for "anyOf" schema validation errors NumberAnyOf() string + + // NumberOneOf returns a format-string for "oneOf" schema validation errors NumberOneOf() string + + // NumberAllOf returns a format-string for "allOf" schema validation errors NumberAllOf() string + + // NumberNot returns a format-string to format a NumberNotError NumberNot() string + + // MissingDependency returns a format-string for "missing dependency" schema validation errors MissingDependency() string + + // Internal returns a format-string for internal errors Internal() string + + // Const returns a format-string to format a ConstError Const() string + + // Enum returns a format-string to format an EnumError Enum() string + + // ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema ArrayNotEnoughItems() string + + // ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError ArrayNoAdditionalItems() string + + // ArrayMinItems returns a format-string to format an ArrayMinItemsError ArrayMinItems() string + + // ArrayMaxItems returns a format-string to format an ArrayMaxItemsError ArrayMaxItems() string + + // Unique returns a format-string to format an ItemsMustBeUniqueError Unique() string + + // ArrayContains returns a format-string to format an ArrayContainsError ArrayContains() string + + // ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError ArrayMinProperties() string + + // ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError ArrayMaxProperties() string + + // AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError AdditionalPropertyNotAllowed() string + + // InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError InvalidPropertyPattern() string + + // InvalidPropertyName returns a format-string to format an InvalidPropertyNameError InvalidPropertyName() string + + // StringGTE returns a format-string to format an StringLengthGTEError StringGTE() string + + // StringLTE returns a format-string to format an StringLengthLTEError StringLTE() string + + // DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError DoesNotMatchPattern() string + + // DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError DoesNotMatchFormat() string + + // MultipleOf returns a format-string to format an MultipleOfError MultipleOf() string + + // NumberGTE returns a format-string to format an NumberGTEError NumberGTE() string + + // NumberGT returns a format-string to format an NumberGTError NumberGT() string + + // NumberLTE returns a format-string to format an NumberLTEError NumberLTE() string + + // NumberLT returns a format-string to format an NumberLTError NumberLT() string // Schema validations + + // RegexPattern returns a format-string to format a regex-pattern error RegexPattern() string + + // GreaterThanZero returns a format-string to format an error where a number must be greater than zero GreaterThanZero() string + + // MustBeOfA returns a format-string to format an error where a value is of the wrong type MustBeOfA() string + + // MustBeOfAn returns a format-string to format an error where a value is of the wrong type MustBeOfAn() string + + // CannotBeUsedWithout returns a format-string to format a "cannot be used without" error CannotBeUsedWithout() string + + // CannotBeGT returns a format-string to format an error where a value are greater than allowed CannotBeGT() string + + // MustBeOfType returns a format-string to format an error where a value does not match the required type MustBeOfType() string + + // MustBeValidRegex returns a format-string to format an error where a regex is invalid MustBeValidRegex() string + + // MustBeValidFormat returns a format-string to format an error where a value does not match the expected format MustBeValidFormat() string + + // MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0 MustBeGTEZero() string + + // KeyCannotBeGreaterThan returns a format-string to format an error where a key is greater than the maximum allowed KeyCannotBeGreaterThan() string + + // KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type KeyItemsMustBeOfType() string + + // KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique KeyItemsMustBeUnique() string + + // ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error ReferenceMustBeCanonical() string + + // NotAValidType returns a format-string to format an invalid type error NotAValidType() string + + // Duplicated returns a format-string to format an error where types are duplicated Duplicated() string + + // HttpBadStatus returns a format-string for errors when loading a schema using HTTP HttpBadStatus() string + + // ParseError returns a format-string for JSON parsing errors ParseError() string + // ConditionThen returns a format-string for ConditionThenError errors ConditionThen() string + + // ConditionElse returns a format-string for ConditionElseError errors ConditionElse() string - // ErrorFormat + // ErrorFormat returns a format string for errors ErrorFormat() string } @@ -90,214 +192,271 @@ type ( DefaultLocale struct{} ) +// False returns a format-string for "false" schema validation errors +func (l DefaultLocale) False() string { + return "False always fails validation" +} + +// Required returns a format-string for "required" schema validation errors func (l DefaultLocale) Required() string { return `{{.property}} is required` } +// InvalidType returns a format-string for "invalid type" schema validation errors func (l DefaultLocale) InvalidType() string { return `Invalid type. Expected: {{.expected}}, given: {{.given}}` } +// NumberAnyOf returns a format-string for "anyOf" schema validation errors func (l DefaultLocale) NumberAnyOf() string { return `Must validate at least one schema (anyOf)` } +// NumberOneOf returns a format-string for "oneOf" schema validation errors func (l DefaultLocale) NumberOneOf() string { return `Must validate one and only one schema (oneOf)` } +// NumberAllOf returns a format-string for "allOf" schema validation errors func (l DefaultLocale) NumberAllOf() string { return `Must validate all the schemas (allOf)` } +// NumberNot returns a format-string to format a NumberNotError func (l DefaultLocale) NumberNot() string { return `Must not validate the schema (not)` } +// MissingDependency returns a format-string for "missing dependency" schema validation errors func (l DefaultLocale) MissingDependency() string { return `Has a dependency on {{.dependency}}` } +// Internal returns a format-string for internal errors func (l DefaultLocale) Internal() string { return `Internal Error {{.error}}` } +// Const returns a format-string to format a ConstError func (l DefaultLocale) Const() string { return `{{.field}} does not match: {{.allowed}}` } +// Enum returns a format-string to format an EnumError func (l DefaultLocale) Enum() string { return `{{.field}} must be one of the following: {{.allowed}}` } +// ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError func (l DefaultLocale) ArrayNoAdditionalItems() string { return `No additional items allowed on array` } +// ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema func (l DefaultLocale) ArrayNotEnoughItems() string { return `Not enough items on array to match positional list of schema` } +// ArrayMinItems returns a format-string to format an ArrayMinItemsError func (l DefaultLocale) ArrayMinItems() string { return `Array must have at least {{.min}} items` } +// ArrayMaxItems returns a format-string to format an ArrayMaxItemsError func (l DefaultLocale) ArrayMaxItems() string { return `Array must have at most {{.max}} items` } +// Unique returns a format-string to format an ItemsMustBeUniqueError func (l DefaultLocale) Unique() string { return `{{.type}} items[{{.i}},{{.j}}] must be unique` } +// ArrayContains returns a format-string to format an ArrayContainsError func (l DefaultLocale) ArrayContains() string { return `At least one of the items must match` } +// ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError func (l DefaultLocale) ArrayMinProperties() string { return `Must have at least {{.min}} properties` } +// ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError func (l DefaultLocale) ArrayMaxProperties() string { return `Must have at most {{.max}} properties` } +// AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError func (l DefaultLocale) AdditionalPropertyNotAllowed() string { return `Additional property {{.property}} is not allowed` } +// InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError func (l DefaultLocale) InvalidPropertyPattern() string { return `Property "{{.property}}" does not match pattern {{.pattern}}` } +// InvalidPropertyName returns a format-string to format an InvalidPropertyNameError func (l DefaultLocale) InvalidPropertyName() string { return `Property name of "{{.property}}" does not match` } +// StringGTE returns a format-string to format an StringLengthGTEError func (l DefaultLocale) StringGTE() string { return `String length must be greater than or equal to {{.min}}` } +// StringLTE returns a format-string to format an StringLengthLTEError func (l DefaultLocale) StringLTE() string { return `String length must be less than or equal to {{.max}}` } +// DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError func (l DefaultLocale) DoesNotMatchPattern() string { return `Does not match pattern '{{.pattern}}'` } +// DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError func (l DefaultLocale) DoesNotMatchFormat() string { return `Does not match format '{{.format}}'` } +// MultipleOf returns a format-string to format an MultipleOfError func (l DefaultLocale) MultipleOf() string { return `Must be a multiple of {{.multiple}}` } +// NumberGTE returns the format string to format a NumberGTEError func (l DefaultLocale) NumberGTE() string { return `Must be greater than or equal to {{.min}}` } +// NumberGT returns the format string to format a NumberGTError func (l DefaultLocale) NumberGT() string { return `Must be greater than {{.min}}` } +// NumberLTE returns the format string to format a NumberLTEError func (l DefaultLocale) NumberLTE() string { return `Must be less than or equal to {{.max}}` } +// NumberLT returns the format string to format a NumberLTError func (l DefaultLocale) NumberLT() string { return `Must be less than {{.max}}` } // Schema validators + +// RegexPattern returns a format-string to format a regex-pattern error func (l DefaultLocale) RegexPattern() string { return `Invalid regex pattern '{{.pattern}}'` } +// GreaterThanZero returns a format-string to format an error where a number must be greater than zero func (l DefaultLocale) GreaterThanZero() string { return `{{.number}} must be strictly greater than 0` } +// MustBeOfA returns a format-string to format an error where a value is of the wrong type func (l DefaultLocale) MustBeOfA() string { return `{{.x}} must be of a {{.y}}` } +// MustBeOfAn returns a format-string to format an error where a value is of the wrong type func (l DefaultLocale) MustBeOfAn() string { return `{{.x}} must be of an {{.y}}` } +// CannotBeUsedWithout returns a format-string to format a "cannot be used without" error func (l DefaultLocale) CannotBeUsedWithout() string { return `{{.x}} cannot be used without {{.y}}` } +// CannotBeGT returns a format-string to format an error where a value are greater than allowed func (l DefaultLocale) CannotBeGT() string { return `{{.x}} cannot be greater than {{.y}}` } +// MustBeOfType returns a format-string to format an error where a value does not match the required type func (l DefaultLocale) MustBeOfType() string { return `{{.key}} must be of type {{.type}}` } +// MustBeValidRegex returns a format-string to format an error where a regex is invalid func (l DefaultLocale) MustBeValidRegex() string { return `{{.key}} must be a valid regex` } +// MustBeValidFormat returns a format-string to format an error where a value does not match the expected format func (l DefaultLocale) MustBeValidFormat() string { return `{{.key}} must be a valid format {{.given}}` } +// MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0 func (l DefaultLocale) MustBeGTEZero() string { return `{{.key}} must be greater than or equal to 0` } +// KeyCannotBeGreaterThan returns a format-string to format an error where a value is greater than the maximum allowed func (l DefaultLocale) KeyCannotBeGreaterThan() string { return `{{.key}} cannot be greater than {{.y}}` } +// KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type func (l DefaultLocale) KeyItemsMustBeOfType() string { return `{{.key}} items must be {{.type}}` } +// KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique func (l DefaultLocale) KeyItemsMustBeUnique() string { return `{{.key}} items must be unique` } +// ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error func (l DefaultLocale) ReferenceMustBeCanonical() string { return `Reference {{.reference}} must be canonical` } +// NotAValidType returns a format-string to format an invalid type error func (l DefaultLocale) NotAValidType() string { return `has a primitive type that is NOT VALID -- given: {{.given}} Expected valid values are:{{.expected}}` } +// Duplicated returns a format-string to format an error where types are duplicated func (l DefaultLocale) Duplicated() string { return `{{.type}} type is duplicated` } +// HttpBadStatus returns a format-string for errors when loading a schema using HTTP func (l DefaultLocale) HttpBadStatus() string { return `Could not read schema from HTTP, response status is {{.status}}` } +// ErrorFormat returns a format string for errors // Replacement options: field, description, context, value func (l DefaultLocale) ErrorFormat() string { return `{{.field}}: {{.description}}` } -//Parse error +// ParseError returns a format-string for JSON parsing errors func (l DefaultLocale) ParseError() string { return `Expected: {{.expected}}, given: Invalid JSON` } -//If/Else +// ConditionThen returns a format-string for ConditionThenError errors +// If/Else func (l DefaultLocale) ConditionThen() string { return `Must validate "then" as "if" was valid` } +// ConditionElse returns a format-string for ConditionElseError errors func (l DefaultLocale) ConditionElse() string { return `Must validate "else" as "if" was not valid` } +// constants const ( STRING_NUMBER = "number" STRING_ARRAY_OF_STRINGS = "array of strings" diff --git a/vendor/github.com/xeipuuv/gojsonschema/result.go b/vendor/github.com/xeipuuv/gojsonschema/result.go index 040d35c8df..0a0179148b 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/result.go +++ b/vendor/github.com/xeipuuv/gojsonschema/result.go @@ -37,19 +37,34 @@ type ( // ResultError is the interface that library errors must implement ResultError interface { + // Field returns the field name without the root context + // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName Field() string + // SetType sets the error-type SetType(string) + // Type returns the error-type Type() string + // SetContext sets the JSON-context for the error SetContext(*JsonContext) + // Context returns the JSON-context of the error Context() *JsonContext + // SetDescription sets a description for the error SetDescription(string) + // Description returns the description of the error Description() string + // SetDescriptionFormat sets the format for the description in the default text/template format SetDescriptionFormat(string) + // DescriptionFormat returns the format for the description in the default text/template format DescriptionFormat() string + // SetValue sets the value related to the error SetValue(interface{}) + // Value returns the value related to the error Value() interface{} + // SetDetails sets the details specific to the error SetDetails(ErrorDetails) + // Details returns details about the error Details() ErrorDetails + // String returns a string representation of the error String() string } @@ -65,6 +80,7 @@ type ( details ErrorDetails } + // Result holds the result of a validation Result struct { errors []ResultError // Scores how well the validation matched. Useful in generating @@ -73,60 +89,73 @@ type ( } ) -// Field outputs the field name without the root context +// Field returns the field name without the root context // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName func (v *ResultErrorFields) Field() string { return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".") } +// SetType sets the error-type func (v *ResultErrorFields) SetType(errorType string) { v.errorType = errorType } +// Type returns the error-type func (v *ResultErrorFields) Type() string { return v.errorType } +// SetContext sets the JSON-context for the error func (v *ResultErrorFields) SetContext(context *JsonContext) { v.context = context } +// Context returns the JSON-context of the error func (v *ResultErrorFields) Context() *JsonContext { return v.context } +// SetDescription sets a description for the error func (v *ResultErrorFields) SetDescription(description string) { v.description = description } +// Description returns the description of the error func (v *ResultErrorFields) Description() string { return v.description } +// SetDescriptionFormat sets the format for the description in the default text/template format func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) { v.descriptionFormat = descriptionFormat } +// DescriptionFormat returns the format for the description in the default text/template format func (v *ResultErrorFields) DescriptionFormat() string { return v.descriptionFormat } +// SetValue sets the value related to the error func (v *ResultErrorFields) SetValue(value interface{}) { v.value = value } +// Value returns the value related to the error func (v *ResultErrorFields) Value() interface{} { return v.value } +// SetDetails sets the details specific to the error func (v *ResultErrorFields) SetDetails(details ErrorDetails) { v.details = details } +// Details returns details about the error func (v *ResultErrorFields) Details() ErrorDetails { return v.details } +// String returns a string representation of the error func (v ResultErrorFields) String() string { // as a fallback, the value is displayed go style valueString := fmt.Sprintf("%v", v.value) @@ -135,7 +164,7 @@ func (v ResultErrorFields) String() string { if v.value == nil { valueString = TYPE_NULL } else { - if vs, err := marshalToJsonString(v.value); err == nil { + if vs, err := marshalToJSONString(v.value); err == nil { if vs == nil { valueString = TYPE_NULL } else { @@ -152,15 +181,17 @@ func (v ResultErrorFields) String() string { }) } +// Valid indicates if no errors were found func (v *Result) Valid() bool { return len(v.errors) == 0 } +// Errors returns the errors that were found func (v *Result) Errors() []ResultError { return v.errors } -// Add a fully filled error to the error set +// AddError appends a fully filled error to the error set // SetDescription() will be called with the result of the parsed err.DescriptionFormat() func (v *Result) AddError(err ResultError, details ErrorDetails) { if _, exists := details["context"]; !exists && err.Context() != nil { diff --git a/vendor/github.com/xeipuuv/gojsonschema/schema.go b/vendor/github.com/xeipuuv/gojsonschema/schema.go index 323fe8559b..9e93cd7955 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/schema.go +++ b/vendor/github.com/xeipuuv/gojsonschema/schema.go @@ -45,10 +45,12 @@ var ( ErrorTemplateFuncs template.FuncMap ) +// NewSchema instances a schema using the given JSONLoader func NewSchema(l JSONLoader) (*Schema, error) { return NewSchemaLoader().Compile(l) } +// Schema holds a schema type Schema struct { documentReference gojsonreference.JsonReference rootSchema *subSchema @@ -61,6 +63,7 @@ func (d *Schema) parse(document interface{}, draft Draft) error { return d.parseSchema(document, d.rootSchema) } +// SetRootSchemaName sets the root-schema name func (d *Schema) SetRootSchemaName(name string) { d.rootSchema.property = name } @@ -83,11 +86,8 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) // 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} - } + currentSchema.pass = &b + return nil } if !isKind(documentNode, reflect.Map) { @@ -267,8 +267,9 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) "given": KEY_TYPE, }, )) - } else { - currentSchema.types.Add(typeInArray.(string)) + } + if err := currentSchema.types.Add(typeInArray.(string)); err != nil { + return err } } @@ -382,7 +383,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if isKind(itemElement, reflect.Map, reflect.Bool) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref - currentSchema.AddItemsChild(newSchema) + currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema) err := d.parseSchema(itemElement, newSchema) if err != nil { return err @@ -401,7 +402,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) } else if isKind(m[KEY_ITEMS], reflect.Map, reflect.Bool) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref - currentSchema.AddItemsChild(newSchema) + currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema) err := d.parseSchema(m[KEY_ITEMS], newSchema) if err != nil { return err @@ -507,7 +508,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) currentSchema.exclusiveMinimum = currentSchema.minimum currentSchema.minimum = nil } - } else if isJsonNumber(m[KEY_EXCLUSIVE_MINIMUM]) { + } else if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) { currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM]) } else { return errors.New(formatErrorDescription( @@ -519,7 +520,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) )) } default: - if isJsonNumber(m[KEY_EXCLUSIVE_MINIMUM]) { + if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) { currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM]) } else { return errors.New(formatErrorDescription( @@ -578,7 +579,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) currentSchema.exclusiveMaximum = currentSchema.maximum currentSchema.maximum = nil } - } else if isJsonNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { + } else if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM]) } else { return errors.New(formatErrorDescription( @@ -590,7 +591,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) )) } default: - if isJsonNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { + if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM]) } else { return errors.New(formatErrorDescription( @@ -669,9 +670,13 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if existsMapKey(m, KEY_FORMAT) { formatString, ok := m[KEY_FORMAT].(string) - if ok && FormatCheckers.Has(formatString) { - currentSchema.format = formatString + if !ok { + return errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{"key": KEY_FORMAT, "type": TYPE_STRING}, + )) } + currentSchema.format = formatString } // validation : object @@ -724,10 +729,13 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) requiredValues := m[KEY_REQUIRED].([]interface{}) for _, requiredValue := range requiredValues { if isKind(requiredValue, reflect.String) { - err := currentSchema.AddRequired(requiredValue.(string)) - if err != nil { - return err + if isStringInSlice(currentSchema.required, requiredValue.(string)) { + return errors.New(formatErrorDescription( + Locale.KeyItemsMustBeUnique(), + ErrorDetails{"key": KEY_REQUIRED}, + )) } + currentSchema.required = append(currentSchema.required, requiredValue.(string)) } else { return errors.New(formatErrorDescription( Locale.KeyItemsMustBeOfType(), @@ -802,19 +810,27 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) // validation : all if existsMapKey(m, KEY_CONST) && *currentSchema.draft >= Draft6 { - err := currentSchema.AddConst(m[KEY_CONST]) + is, err := marshalWithoutNumber(m[KEY_CONST]) if err != nil { return err } + currentSchema._const = is } if existsMapKey(m, KEY_ENUM) { if isKind(m[KEY_ENUM], reflect.Slice) { for _, v := range m[KEY_ENUM].([]interface{}) { - err := currentSchema.AddEnum(v) + is, err := marshalWithoutNumber(v) if err != nil { return err } + if isStringInSlice(currentSchema.enum, *is) { + return errors.New(formatErrorDescription( + Locale.KeyItemsMustBeUnique(), + ErrorDetails{"key": KEY_ENUM}, + )) + } + currentSchema.enum = append(currentSchema.enum, *is) } } else { return errors.New(formatErrorDescription( @@ -830,7 +846,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if isKind(m[KEY_ONE_OF], reflect.Slice) { for _, v := range m[KEY_ONE_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref} - currentSchema.AddOneOf(newSchema) + currentSchema.oneOf = append(currentSchema.oneOf, newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err @@ -848,7 +864,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if isKind(m[KEY_ANY_OF], reflect.Slice) { for _, v := range m[KEY_ANY_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref} - currentSchema.AddAnyOf(newSchema) + currentSchema.anyOf = append(currentSchema.anyOf, newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err @@ -866,7 +882,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if isKind(m[KEY_ALL_OF], reflect.Slice) { for _, v := range m[KEY_ALL_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref} - currentSchema.AddAllOf(newSchema) + currentSchema.allOf = append(currentSchema.allOf, newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err @@ -883,7 +899,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) if existsMapKey(m, KEY_NOT) { if isKind(m[KEY_NOT], reflect.Map, reflect.Bool) { newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref} - currentSchema.SetNot(newSchema) + currentSchema.not = newSchema err := d.parseSchema(m[KEY_NOT], newSchema) if err != nil { return err @@ -900,7 +916,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) 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) + currentSchema._if = newSchema err := d.parseSchema(m[KEY_IF], newSchema) if err != nil { return err @@ -916,7 +932,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) 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) + currentSchema._then = newSchema err := d.parseSchema(m[KEY_THEN], newSchema) if err != nil { return err @@ -932,7 +948,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) 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) + currentSchema._else = newSchema err := d.parseSchema(m[KEY_ELSE], newSchema) if err != nil { return err @@ -1004,7 +1020,7 @@ func (d *Schema) parseProperties(documentNode interface{}, currentSchema *subSch for k := range m { schemaProperty := k newSchema := &subSchema{property: schemaProperty, parent: currentSchema, ref: currentSchema.ref} - currentSchema.AddPropertiesChild(newSchema) + currentSchema.propertiesChildren = append(currentSchema.propertiesChildren, newSchema) err := d.parseSchema(m[k], newSchema) if err != nil { return err @@ -1042,9 +1058,8 @@ func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subS "type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS, }, )) - } else { - valuesToRegister = append(valuesToRegister, value.(string)) } + valuesToRegister = append(valuesToRegister, value.(string)) currentSchema.dependencies[k] = valuesToRegister } diff --git a/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go b/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go index 95e0568abb..20db0c1f99 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go +++ b/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go @@ -21,6 +21,7 @@ import ( "github.com/xeipuuv/gojsonreference" ) +// SchemaLoader is used to load schemas type SchemaLoader struct { pool *schemaPool AutoDetect bool @@ -28,6 +29,7 @@ type SchemaLoader struct { Draft Draft } +// NewSchemaLoader creates a new NewSchemaLoader func NewSchemaLoader() *SchemaLoader { ps := &SchemaLoader{ @@ -141,6 +143,7 @@ func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error { return sl.pool.parseReferences(doc, ref, true) } +// Compile loads and compiles a schema func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) { ref, err := rootSchema.JsonReference() diff --git a/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go b/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go index f124e038d7..35b1cc6306 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go +++ b/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go @@ -150,12 +150,12 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche } // Create a deep copy, so we can remove the fragment part later on without altering the original - refToUrl, _ := gojsonreference.NewJsonReference(reference.String()) + refToURL, _ := gojsonreference.NewJsonReference(reference.String()) // First check if the given fragment is a location independent identifier // http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3 - if spd, ok = p.schemaPoolDocuments[refToUrl.String()]; ok { + if spd, ok = p.schemaPoolDocuments[refToURL.String()]; ok { if internalLogEnabled { internalLog(" From pool") } @@ -165,9 +165,9 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche // If the given reference is not a location independent identifier, // strip the fragment and look for a document with it's base URI - refToUrl.GetUrl().Fragment = "" + refToURL.GetUrl().Fragment = "" - if cachedSpd, ok := p.schemaPoolDocuments[refToUrl.String()]; ok { + if cachedSpd, ok := p.schemaPoolDocuments[refToURL.String()]; ok { document, _, err := reference.GetPointer().Get(cachedSpd.Document) if err != nil { @@ -200,7 +200,7 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche } // add the whole document to the pool for potential re-use - p.parseReferences(document, refToUrl, true) + p.parseReferences(document, refToURL, true) _, draft, _ = parseSchemaURL(document) diff --git a/vendor/github.com/xeipuuv/gojsonschema/subSchema.go b/vendor/github.com/xeipuuv/gojsonschema/subSchema.go index 362d86ca95..ec779812c3 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/subSchema.go +++ b/vendor/github.com/xeipuuv/gojsonschema/subSchema.go @@ -27,14 +27,12 @@ package gojsonschema import ( - "errors" + "github.com/xeipuuv/gojsonreference" "math/big" "regexp" - "strings" - - "github.com/xeipuuv/gojsonreference" ) +// Constants const ( KEY_SCHEMA = "$schema" KEY_ID = "id" @@ -88,6 +86,9 @@ type subSchema struct { property string + // Quick pass/fail for boolean schemas + pass *bool + // Types associated with the subSchema types jsonSchemaType @@ -146,111 +147,3 @@ type subSchema struct { _then *subSchema _else *subSchema } - -func (s *subSchema) AddConst(i interface{}) error { - - is, err := marshalWithoutNumber(i) - if err != nil { - return err - } - s._const = is - return nil -} - -func (s *subSchema) AddEnum(i interface{}) error { - - is, err := marshalWithoutNumber(i) - if err != nil { - return err - } - - if isStringInSlice(s.enum, *is) { - return errors.New(formatErrorDescription( - Locale.KeyItemsMustBeUnique(), - ErrorDetails{"key": KEY_ENUM}, - )) - } - - s.enum = append(s.enum, *is) - - return nil -} - -func (s *subSchema) ContainsEnum(i interface{}) (bool, error) { - - is, err := marshalWithoutNumber(i) - if err != nil { - return false, err - } - - return isStringInSlice(s.enum, *is), nil -} - -func (s *subSchema) AddOneOf(subSchema *subSchema) { - s.oneOf = append(s.oneOf, subSchema) -} - -func (s *subSchema) AddAllOf(subSchema *subSchema) { - s.allOf = append(s.allOf, subSchema) -} - -func (s *subSchema) AddAnyOf(subSchema *subSchema) { - s.anyOf = append(s.anyOf, subSchema) -} - -func (s *subSchema) SetNot(subSchema *subSchema) { - s.not = subSchema -} - -func (s *subSchema) SetIf(subSchema *subSchema) { - s._if = subSchema -} - -func (s *subSchema) SetThen(subSchema *subSchema) { - s._then = subSchema -} - -func (s *subSchema) SetElse(subSchema *subSchema) { - s._else = subSchema -} - -func (s *subSchema) AddRequired(value string) error { - - if isStringInSlice(s.required, value) { - return errors.New(formatErrorDescription( - Locale.KeyItemsMustBeUnique(), - ErrorDetails{"key": KEY_REQUIRED}, - )) - } - - s.required = append(s.required, value) - - return nil -} - -func (s *subSchema) AddItemsChild(child *subSchema) { - s.itemsChildren = append(s.itemsChildren, child) -} - -func (s *subSchema) AddPropertiesChild(child *subSchema) { - s.propertiesChildren = append(s.propertiesChildren, child) -} - -func (s *subSchema) PatternPropertiesString() string { - - if s.patternProperties == nil || len(s.patternProperties) == 0 { - return STRING_UNDEFINED // should never happen - } - - patternPropertiesKeySlice := []string{} - for pk := range s.patternProperties { - patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`) - } - - if len(patternPropertiesKeySlice) == 1 { - return patternPropertiesKeySlice[0] - } - - return "[" + strings.Join(patternPropertiesKeySlice, ",") + "]" - -} diff --git a/vendor/github.com/xeipuuv/gojsonschema/types.go b/vendor/github.com/xeipuuv/gojsonschema/types.go index 952d22ef65..0e6fd51735 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/types.go +++ b/vendor/github.com/xeipuuv/gojsonschema/types.go @@ -25,6 +25,7 @@ package gojsonschema +// Type constants const ( TYPE_ARRAY = `array` TYPE_BOOLEAN = `boolean` @@ -35,7 +36,10 @@ const ( TYPE_STRING = `string` ) +// JSON_TYPES hosts the list of type that are supported in JSON var JSON_TYPES []string + +// SCHEMA_TYPES hosts the list of type that are supported in schemas var SCHEMA_TYPES []string func init() { diff --git a/vendor/github.com/xeipuuv/gojsonschema/utils.go b/vendor/github.com/xeipuuv/gojsonschema/utils.go index 88d223fbf8..a17d22e3bd 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/utils.go +++ b/vendor/github.com/xeipuuv/gojsonschema/utils.go @@ -27,15 +27,13 @@ package gojsonschema import ( "encoding/json" - "fmt" - "math" "math/big" "reflect" ) func isKind(what interface{}, kinds ...reflect.Kind) bool { target := what - if isJsonNumber(what) { + if isJSONNumber(what) { // JSON Numbers are strings! target = *mustBeNumber(what) } @@ -72,7 +70,7 @@ func indexStringInSlice(s []string, what string) int { return -1 } -func marshalToJsonString(value interface{}) (*string, error) { +func marshalToJSONString(value interface{}) (*string, error) { mBytes, err := json.Marshal(value) if err != nil { @@ -90,7 +88,7 @@ func marshalWithoutNumber(value interface{}) (*string, error) { // One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber // so that these differences in representation are removed - jsonString, err := marshalToJsonString(value) + jsonString, err := marshalToJSONString(value) if err != nil { return nil, err } @@ -102,10 +100,10 @@ func marshalWithoutNumber(value interface{}) (*string, error) { return nil, err } - return marshalToJsonString(document) + return marshalToJSONString(document) } -func isJsonNumber(what interface{}) bool { +func isJSONNumber(what interface{}) bool { switch what.(type) { @@ -116,7 +114,7 @@ func isJsonNumber(what interface{}) bool { return false } -func checkJsonInteger(what interface{}) (isInt bool) { +func checkJSONInteger(what interface{}) (isInt bool) { jsonNumber := what.(json.Number) @@ -128,26 +126,17 @@ func checkJsonInteger(what interface{}) (isInt bool) { // same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER const ( - max_json_float = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 - min_json_float = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 + maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 + minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 ) -func isFloat64AnInteger(f float64) bool { - - if math.IsNaN(f) || math.IsInf(f, 0) || f < min_json_float || f > max_json_float { - return false - } - - return f == float64(int64(f)) || f == float64(uint64(f)) -} - func mustBeInteger(what interface{}) *int { - if isJsonNumber(what) { + if isJSONNumber(what) { number := what.(json.Number) - isInt := checkJsonInteger(number) + isInt := checkJSONInteger(number) if isInt { @@ -158,9 +147,6 @@ func mustBeInteger(what interface{}) *int { int32Value := int(int64Value) return &int32Value - - } else { - return nil } } @@ -170,43 +156,18 @@ func mustBeInteger(what interface{}) *int { func mustBeNumber(what interface{}) *big.Rat { - if isJsonNumber(what) { + if isJSONNumber(what) { number := what.(json.Number) float64Value, success := new(big.Rat).SetString(string(number)) if success { return float64Value - } else { - return nil } - } return nil } -// formats a number so that it is displayed as the smallest string possible -func resultErrorFormatJsonNumber(n json.Number) string { - - if int64Value, err := n.Int64(); err == nil { - return fmt.Sprintf("%d", int64Value) - } - - float64Value, _ := n.Float64() - - return fmt.Sprintf("%g", float64Value) -} - -// formats a number so that it is displayed as the smallest string possible -func resultErrorFormatNumber(n float64) string { - - if isFloat64AnInteger(n) { - return fmt.Sprintf("%d", int64(n)) - } - - return fmt.Sprintf("%g", n) -} - func convertDocumentNode(val interface{}) interface{} { if lval, ok := val.([]interface{}); ok { diff --git a/vendor/github.com/xeipuuv/gojsonschema/validation.go b/vendor/github.com/xeipuuv/gojsonschema/validation.go index 090c11e932..74091bca19 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/validation.go +++ b/vendor/github.com/xeipuuv/gojsonschema/validation.go @@ -35,42 +35,29 @@ import ( "unicode/utf8" ) +// Validate loads and validates a JSON schema func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) { - - var err error - // load schema - schema, err := NewSchema(ls) if err != nil { return nil, err } - - // begine validation - return schema.Validate(ld) - } +// Validate loads and validates a JSON document func (v *Schema) Validate(l JSONLoader) (*Result, error) { - - // load document - root, err := l.LoadJSON() if err != nil { 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) v.rootSchema.validateRecursive(v.rootSchema, root, result, context) - return result } @@ -88,6 +75,19 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i internalLog(" %v", currentNode) } + // Handle true/false schema as early as possible as all other fields will be nil + if currentSubSchema.pass != nil { + if !*currentSubSchema.pass { + result.addInternalError( + new(FalseError), + context, + currentNode, + ErrorDetails{}, + ) + } + return + } + // Handle referenced schemas, returns directly when a $ref is found if currentSubSchema.refSchema != nil { v.validateRecursive(currentSubSchema.refSchema, currentNode, result, context) @@ -114,11 +114,11 @@ func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode i } else { // Not a null value - if isJsonNumber(currentNode) { + if isJSONNumber(currentNode) { value := currentNode.(json.Number) - isInt := checkJsonInteger(value) + isInt := checkJSONInteger(value) validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isInt && currentSubSchema.types.Contains(TYPE_INTEGER)) @@ -424,11 +424,11 @@ func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{ // enum: if len(currentSubSchema.enum) > 0 { - has, err := currentSubSchema.ContainsEnum(value) + vString, err := marshalWithoutNumber(value) if err != nil { result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) } - if !has { + if !isStringInSlice(currentSubSchema.enum, *vString) { result.addInternalError( new(EnumError), context, @@ -516,13 +516,13 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface // uniqueItems: if currentSubSchema.uniqueItems { - var stringifiedItems []string + var stringifiedItems = make(map[string]int) for j, v := range value { vString, err := marshalWithoutNumber(v) if err != nil { result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err}) } - if i := indexStringInSlice(stringifiedItems, *vString); i > -1 { + if i, ok := stringifiedItems[*vString]; ok { result.addInternalError( new(ItemsMustBeUniqueError), context, @@ -530,7 +530,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j}, ) } - stringifiedItems = append(stringifiedItems, *vString) + stringifiedItems[*vString] = j } } @@ -614,101 +614,37 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string } // additionalProperty & patternProperty: - if currentSubSchema.additionalProperties != nil { - - switch currentSubSchema.additionalProperties.(type) { - case bool: - - if !currentSubSchema.additionalProperties.(bool) { - - for pk := range value { - - found := false - for _, spValue := range currentSubSchema.propertiesChildren { - if pk == spValue.property { - found = true - } - } - - pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) - - if found { - - if pp_has && !pp_match { - result.addInternalError( - new(AdditionalPropertyNotAllowedError), - context, - value[pk], - ErrorDetails{"property": pk}, - ) - } - - } else { - - if !pp_has || !pp_match { - result.addInternalError( - new(AdditionalPropertyNotAllowedError), - context, - value[pk], - ErrorDetails{"property": pk}, - ) - } - - } - } - } - - case *subSchema: - - additionalPropertiesSchema := currentSubSchema.additionalProperties.(*subSchema) - for pk := range value { - - found := false - for _, spValue := range currentSubSchema.propertiesChildren { - if pk == spValue.property { - found = true - } - } - - pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) - - if found { - - if pp_has && !pp_match { - validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context) - result.mergeErrors(validationResult) - } - - } else { - - if !pp_has || !pp_match { - validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context) - result.mergeErrors(validationResult) - } - - } + for pk := range value { + // Check whether this property is described by "properties" + found := false + for _, spValue := range currentSubSchema.propertiesChildren { + if pk == spValue.property { + found = true } } - } else { - for pk := range value { + // Check whether this property is described by "patternProperties" + ppMatch := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) - pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) + // If it is not described by neither "properties" nor "patternProperties" it must pass "additionalProperties" + if !found && !ppMatch { + switch ap := currentSubSchema.additionalProperties.(type) { + case bool: + // Handle the boolean case separately as it's cleaner to return a specific error than failing to pass the false schema + if !ap { + result.addInternalError( + new(AdditionalPropertyNotAllowedError), + context, + value[pk], + ErrorDetails{"property": pk}, + ) - if pp_has && !pp_match { - - result.addInternalError( - new(InvalidPropertyPatternError), - context, - value[pk], - ErrorDetails{ - "property": pk, - "pattern": currentSubSchema.PatternPropertiesString(), - }, - ) + } + case *subSchema: + validationResult := ap.subValidateWithContext(value[pk], NewJsonContext(pk, context)) + result.mergeErrors(validationResult) } - } } @@ -730,40 +666,36 @@ func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string 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) bool { if internalLogEnabled { internalLog("validatePatternProperty %s", context.String()) internalLog(" %s %v", key, value) } - has = false - - validatedkey := false + validated := false for pk, pv := range currentSubSchema.patternProperties { if matches, _ := regexp.MatchString(pk, key); matches { - has = true + validated = true subContext := NewJsonContext(key, context) validationResult := pv.subValidateWithContext(value, subContext) result.mergeErrors(validationResult) - validatedkey = true } } - if !validatedkey { - return has, false + if !validated { + return false } result.incrementScore() - - return has, true + return true } func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { // Ignore JSON numbers - if isJsonNumber(value) { + if isJSONNumber(value) { return } @@ -832,7 +764,7 @@ func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { // Ignore non numbers - if !isJsonNumber(value) { + if !isJSONNumber(value) { return } @@ -850,8 +782,10 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{ result.addInternalError( new(MultipleOfError), context, - resultErrorFormatJsonNumber(number), - ErrorDetails{"multiple": new(big.Float).SetRat(currentSubSchema.multipleOf)}, + number, + ErrorDetails{ + "multiple": new(big.Float).SetRat(currentSubSchema.multipleOf), + }, ) } } @@ -862,9 +796,9 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{ result.addInternalError( new(NumberLTEError), context, - resultErrorFormatJsonNumber(number), + number, ErrorDetails{ - "max": currentSubSchema.maximum, + "max": new(big.Float).SetRat(currentSubSchema.maximum), }, ) } @@ -874,9 +808,9 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{ result.addInternalError( new(NumberLTError), context, - resultErrorFormatJsonNumber(number), + number, ErrorDetails{ - "max": currentSubSchema.exclusiveMaximum, + "max": new(big.Float).SetRat(currentSubSchema.exclusiveMaximum), }, ) } @@ -888,22 +822,21 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{ result.addInternalError( new(NumberGTEError), context, - resultErrorFormatJsonNumber(number), + number, ErrorDetails{ - "min": currentSubSchema.minimum, + "min": new(big.Float).SetRat(currentSubSchema.minimum), }, ) } } if currentSubSchema.exclusiveMinimum != nil { if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 { - // if float64Value <= *currentSubSchema.minimum { result.addInternalError( new(NumberGTError), context, - resultErrorFormatJsonNumber(number), + number, ErrorDetails{ - "min": currentSubSchema.exclusiveMinimum, + "min": new(big.Float).SetRat(currentSubSchema.exclusiveMinimum), }, ) }