summaryrefslogtreecommitdiff
path: root/vendor/github.com/xeipuuv/gojsonschema
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/xeipuuv/gojsonschema')
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/README.md181
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/draft.go118
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/errors.go47
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/format_checkers.go123
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/jsonContext.go16
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go54
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/locales.go33
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/result.go49
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/schema.go452
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go203
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/schemaPool.go168
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go5
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/subSchema.go67
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/utils.go72
-rw-r--r--vendor/github.com/xeipuuv/gojsonschema/validation.go293
15 files changed, 1483 insertions, 398 deletions
diff --git a/vendor/github.com/xeipuuv/gojsonschema/README.md b/vendor/github.com/xeipuuv/gojsonschema/README.md
index e02976bc6..24ca34744 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/README.md
+++ b/vendor/github.com/xeipuuv/gojsonschema/README.md
@@ -1,10 +1,11 @@
+[![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)
# gojsonschema
## Description
-An implementation of JSON Schema, based on IETF's draft v4 - Go language
+An implementation of JSON Schema for the Go programming language. Supports draft-04, draft-06 and draft-07.
References :
@@ -54,7 +55,6 @@ func main() {
fmt.Printf("- %s\n", desc)
}
}
-
}
@@ -148,6 +148,87 @@ To check the result :
}
```
+
+## Loading local schemas
+
+By default `file` and `http(s)` references to external schemas are loaded automatically via the file system or via http(s). An external schema can also be loaded using a `SchemaLoader`.
+
+```go
+ sl := gojsonschema.NewSchemaLoader()
+ loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`)
+ err := sl.AddSchema("http://some_host.com/string.json", loader1)
+```
+
+Alternatively if your schema already has an `$id` you can use the `AddSchemas` function
+```go
+ loader2 := gojsonschema.NewStringLoader(`{
+ "$id" : "http://some_host.com/maxlength.json",
+ "maxLength" : 5
+ }`)
+ err = sl.AddSchemas(loader2)
+```
+
+The main schema should be passed to the `Compile` function. This main schema can then directly reference the added schemas without needing to download them.
+```go
+ loader3 := gojsonschema.NewStringLoader(`{
+ "$id" : "http://some_host.com/main.json",
+ "allOf" : [
+ { "$ref" : "http://some_host.com/string.json" },
+ { "$ref" : "http://some_host.com/maxlength.json" }
+ ]
+ }`)
+
+ schema, err := sl.Compile(loader3)
+
+ documentLoader := gojsonschema.NewStringLoader(`"hello world"`)
+
+ result, err := schema.Validate(documentLoader)
+```
+
+It's also possible to pass a `ReferenceLoader` to the `Compile` function that references a loaded schema.
+
+```go
+err = sl.AddSchemas(loader3)
+schema, err := sl.Compile(gojsonschema.NewReferenceLoader("http://some_host.com/main.json"))
+```
+
+Schemas added by `AddSchema` and `AddSchemas` are only validated when the entire schema is compiled, unless meta-schema validation is used.
+
+## Using a specific draft
+By default `gojsonschema` will try to detect the draft of a schema by using the `$schema` keyword and parse it in a strict draft-04, draft-06 or draft-07 mode. If `$schema` is missing, or the draft version is not explicitely set, a hybrid mode is used which merges together functionality of all drafts into one mode.
+
+Autodectection can be turned off with the `AutoDetect` property. Specific draft versions can be specified with the `Draft` property.
+
+```go
+sl := gojsonschema.NewSchemaLoader()
+sl.Draft = gojsonschema.Draft7
+sl.AutoDetect = false
+```
+
+If autodetection is on (default), a draft-07 schema can savely reference draft-04 schemas and vice-versa, as long as `$schema` is specified in all schemas.
+
+## Meta-schema validation
+Schemas that are added using the `AddSchema`, `AddSchemas` and `Compile` can be validated against their meta-schema by setting the `Validate` property.
+
+The following example will produce an error as `multipleOf` must be a number. If `Validate` is off (default), this error is only returned at the `Compile` step.
+
+```go
+sl := gojsonschema.NewSchemaLoader()
+sl.Validate = true
+err := sl.AddSchemas(gojsonschema.NewStringLoader(`{
+ $id" : "http://some_host.com/invalid.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "multipleOf" : true
+}`))
+ ```
+```
+ ```
+
+Errors returned by meta-schema validation are more readable and contain more information, which helps significantly if you are developing a schema.
+
+Meta-schema validation also works with a custom `$schema`. In case `$schema` is missing, or `AutoDetect` is set to `false`, the meta-schema of the used draft is used.
+
+
## Working with Errors
The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it
@@ -155,7 +236,9 @@ The library handles string error codes which you can customize by creating your
gojsonschema.Locale = YourCustomLocale{}
```
-However, each error contains additional contextual information.
+However, each error contains additional contextual information.
+
+Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens.
**err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below
@@ -169,15 +252,18 @@ Note: An error of RequiredType has an err.Type() return value of "required"
"number_not": NumberNotError
"missing_dependency": MissingDependencyError
"internal": InternalError
+ "const": ConstEror
"enum": EnumError
"array_no_additional_items": ArrayNoAdditionalItemsError
"array_min_items": ArrayMinItemsError
"array_max_items": ArrayMaxItemsError
"unique": ItemsMustBeUniqueError
+ "contains" : ArrayContainsError
"array_min_properties": ArrayMinPropertiesError
"array_max_properties": ArrayMaxPropertiesError
"additional_property_not_allowed": AdditionalPropertyNotAllowedError
"invalid_property_pattern": InvalidPropertyPatternError
+ "invalid_property_name": InvalidPropertyNameError
"string_gte": StringLengthGTEError
"string_lte": StringLengthLTEError
"pattern": DoesNotMatchPatternError
@@ -186,15 +272,19 @@ Note: An error of RequiredType has an err.Type() return value of "required"
"number_gt": NumberGTError
"number_lte": NumberLTEError
"number_lt": NumberLTError
+ "condition_then" : ConditionThenError
+ "condition_else" : ConditionElseError
**err.Value()**: *interface{}* Returns the value given
-**err.Context()**: *gojsonschema.jsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
+**err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
**err.Field()**: *string* Returns the fieldname in the format firstName, or for embedded properties, person.firstName. This returns the same as the String() method on *err.Context()* but removes the (root). prefix.
**err.Description()**: *string* The error description. This is based on the locale you are using. See the beginning of this section for overwriting the locale with a custom implementation.
+**err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result.
+
**err.Details()**: *gojsonschema.ErrorDetails* Returns a map[string]interface{} of additional error details specific to the error. For example, GTE errors will have a "min" value, LTE will have a "max" value. See errors.go for a full description of all the error details. Every error always contains a "field" key that holds the value of *err.Field()*
Note in most cases, the err.Details() will be used to generate replacement strings in your locales, and not used directly. These strings follow the text/template format i.e.
@@ -225,10 +315,35 @@ Learn more about what types of template functions you can use in `ErrorTemplateF
## Formats
JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this:
+
````json
{"type": "string", "format": "email"}
````
-Available formats: date-time, hostname, email, ipv4, ipv6, uri, uri-reference.
+
+Not all formats defined in draft-07 are available. Implemented formats are:
+
+* `date`
+* `time`
+* `date-time`
+* `hostname`. Subdomains that start with a number are also supported, but this means that it doesn't strictly follow [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5) and has the implication that ipv4 addresses are also recognized as valid hostnames.
+* `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support.
+* `idn-email`. Same caveat as `email`.
+* `ipv4`
+* `ipv6`
+* `uri`. Includes unicode support.
+* `uri-reference`. Includes unicode support.
+* `iri`
+* `iri-reference`
+* `uri-template`
+* `uuid`
+* `regex`. Go uses the [RE2](https://github.com/google/re2/wiki/Syntax) engine and is not [ECMA262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) compatible.
+* `json-pointer`
+* `relative-json-pointer`
+
+`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.
For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this:
@@ -285,7 +400,63 @@ func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool {
gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{})
````
+Formats can also be removed, for example if you want to override one of the formats that is defined by default.
+
+```go
+gojsonschema.FormatCheckers.Remove("hostname")
+```
+
+
+## Additional custom validation
+After the validation has run and you have the results, you may add additional
+errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead
+of having to add special exceptions for your own errors. Below is an example.
+
+```go
+type AnswerInvalidError struct {
+ gojsonschema.ResultErrorFields
+}
+
+func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError {
+ err := AnswerInvalidError{}
+ err.SetContext(context)
+ err.SetType("custom_invalid_error")
+ // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed
+ // using the description of err will be overridden by this.
+ err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}")
+ err.SetValue(value)
+ err.SetDetails(details)
+
+ return &err
+}
+
+func main() {
+ // ...
+ schema, err := gojsonschema.NewSchema(schemaLoader)
+ result, err := gojsonschema.Validate(schemaLoader, documentLoader)
+
+ if true { // some validation
+ jsonContext := gojsonschema.NewJsonContext("question", nil)
+ errDetail := gojsonschema.ErrorDetails{
+ "answer": 42,
+ }
+ result.AddError(
+ newAnswerInvalidError(
+ gojsonschema.NewJsonContext("answer", jsonContext),
+ 52,
+ errDetail,
+ ),
+ errDetail,
+ )
+ }
+
+ return result, err
+
+}
+```
+This is especially useful if you want to add validation beyond what the
+json schema drafts can provide such business specific logic.
## Uses
diff --git a/vendor/github.com/xeipuuv/gojsonschema/draft.go b/vendor/github.com/xeipuuv/gojsonschema/draft.go
new file mode 100644
index 000000000..bfde4a2e1
--- /dev/null
+++ b/vendor/github.com/xeipuuv/gojsonschema/draft.go
@@ -0,0 +1,118 @@
+// Copyright 2018 johandorland ( https://github.com/johandorland )
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package gojsonschema
+
+import (
+ "errors"
+ "math"
+ "reflect"
+
+ "github.com/xeipuuv/gojsonreference"
+)
+
+type Draft int
+
+const (
+ Draft4 Draft = 4
+ Draft6 Draft = 6
+ Draft7 Draft = 7
+ Hybrid Draft = math.MaxInt32
+)
+
+type draftConfig struct {
+ Version Draft
+ MetaSchemaURL string
+ MetaSchema string
+}
+type draftConfigs []draftConfig
+
+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}`,
+ },
+ }
+}
+
+func (dc draftConfigs) GetMetaSchema(url string) string {
+ for _, config := range dc {
+ if config.MetaSchemaURL == url {
+ return config.MetaSchema
+ }
+ }
+ return ""
+}
+func (dc draftConfigs) GetDraftVersion(url string) *Draft {
+ for _, config := range dc {
+ if config.MetaSchemaURL == url {
+ return &config.Version
+ }
+ }
+ return nil
+}
+func (dc draftConfigs) GetSchemaURL(draft Draft) string {
+ for _, config := range dc {
+ if config.Version == draft {
+ return config.MetaSchemaURL
+ }
+ }
+ return ""
+}
+
+func parseSchemaURL(documentNode interface{}) (string, *Draft, error) {
+
+ if isKind(documentNode, reflect.Bool) {
+ return "", nil, nil
+ }
+ m := documentNode.(map[string]interface{})
+
+ if existsMapKey(m, KEY_SCHEMA) {
+ if !isKind(m[KEY_SCHEMA], reflect.String) {
+ return "", nil, errors.New(formatErrorDescription(
+ Locale.MustBeOfType(),
+ ErrorDetails{
+ "key": KEY_SCHEMA,
+ "type": TYPE_STRING,
+ },
+ ))
+ }
+
+ schemaReference, err := gojsonreference.NewJsonReference(m[KEY_SCHEMA].(string))
+
+ if err != nil {
+ return "", nil, err
+ }
+
+ schema := schemaReference.String()
+
+ return schema, drafts.GetDraftVersion(schema), nil
+ }
+
+ return "", nil, nil
+}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/errors.go b/vendor/github.com/xeipuuv/gojsonschema/errors.go
index d39f01959..2f01a1c2c 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/errors.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/errors.go
@@ -56,6 +56,11 @@ type (
ResultErrorFields
}
+ // ConstError. ErrorDetails: allowed
+ ConstError struct {
+ ResultErrorFields
+ }
+
// EnumError. ErrorDetails: allowed
EnumError struct {
ResultErrorFields
@@ -76,11 +81,16 @@ type (
ResultErrorFields
}
- // ItemsMustBeUniqueError. ErrorDetails: type
+ // ItemsMustBeUniqueError. ErrorDetails: type, i, j
ItemsMustBeUniqueError struct {
ResultErrorFields
}
+ // ArrayContainsError. ErrorDetails:
+ ArrayContainsError struct {
+ ResultErrorFields
+ }
+
// ArrayMinPropertiesError. ErrorDetails: min
ArrayMinPropertiesError struct {
ResultErrorFields
@@ -101,6 +111,11 @@ type (
ResultErrorFields
}
+ // InvalidPopertyNameError. ErrorDetails: property
+ InvalidPropertyNameError struct {
+ ResultErrorFields
+ }
+
// StringLengthGTEError. ErrorDetails: min
StringLengthGTEError struct {
ResultErrorFields
@@ -145,10 +160,20 @@ type (
NumberLTError struct {
ResultErrorFields
}
+
+ // ConditionThenError. ErrorDetails: -
+ ConditionThenError struct {
+ ResultErrorFields
+ }
+
+ // ConditionElseError. ErrorDetails: -
+ ConditionElseError struct {
+ ResultErrorFields
+ }
)
// newError takes a ResultError type and sets the type, context, description, details, value, and field
-func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
+func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) {
var t string
var d string
switch err.(type) {
@@ -176,6 +201,9 @@ func newError(err ResultError, context *jsonContext, value interface{}, locale l
case *InternalError:
t = "internal"
d = locale.Internal()
+ case *ConstError:
+ t = "const"
+ d = locale.Const()
case *EnumError:
t = "enum"
d = locale.Enum()
@@ -191,6 +219,9 @@ func newError(err ResultError, context *jsonContext, value interface{}, locale l
case *ItemsMustBeUniqueError:
t = "unique"
d = locale.Unique()
+ case *ArrayContainsError:
+ t = "contains"
+ d = locale.ArrayContains()
case *ArrayMinPropertiesError:
t = "array_min_properties"
d = locale.ArrayMinProperties()
@@ -203,6 +234,9 @@ func newError(err ResultError, context *jsonContext, value interface{}, locale l
case *InvalidPropertyPatternError:
t = "invalid_property_pattern"
d = locale.InvalidPropertyPattern()
+ case *InvalidPropertyNameError:
+ t = "invalid_property_name"
+ d = locale.InvalidPropertyName()
case *StringLengthGTEError:
t = "string_gte"
d = locale.StringGTE()
@@ -230,19 +264,26 @@ func newError(err ResultError, context *jsonContext, value interface{}, locale l
case *NumberLTError:
t = "number_lt"
d = locale.NumberLT()
+ case *ConditionThenError:
+ t = "condition_then"
+ d = locale.ConditionThen()
+ case *ConditionElseError:
+ t = "condition_else"
+ d = locale.ConditionElse()
}
err.SetType(t)
err.SetContext(context)
err.SetValue(value)
err.SetDetails(details)
+ err.SetDescriptionFormat(d)
details["field"] = err.Field()
if _, exists := details["context"]; !exists && context != nil {
details["context"] = context.String()
}
- err.SetDescription(formatErrorDescription(d, details))
+ err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
}
// formatErrorDescription takes a string in the default text/template
diff --git a/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go b/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
index c6a07923b..26217fca1 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
@@ -2,9 +2,11 @@ package gojsonschema
import (
"net"
+ "net/mail"
"net/url"
"regexp"
"strings"
+ "sync"
"time"
)
@@ -51,12 +53,19 @@ type (
// http://tools.ietf.org/html/rfc3339#section-5.6
DateTimeFormatChecker struct{}
+ DateFormatChecker struct{}
+
+ TimeFormatChecker struct{}
+
// URIFormatChecker validates a URI with a valid Scheme per RFC3986
URIFormatChecker struct{}
// URIReferenceFormatChecker validates a URI or relative-reference per RFC3986
URIReferenceFormatChecker struct{}
+ // URITemplateFormatChecker validates a URI template per RFC6570
+ URITemplateFormatChecker struct{}
+
// HostnameFormatChecker validates a hostname is in the correct format
HostnameFormatChecker struct{}
@@ -65,6 +74,12 @@ type (
// RegexFormatChecker validates a regex is in the correct format
RegexFormatChecker struct{}
+
+ // JSONPointerFormatChecker validates a JSON Pointer per RFC6901
+ JSONPointerFormatChecker struct{}
+
+ // RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format
+ RelativeJSONPointerFormatChecker struct{}
)
var (
@@ -72,45 +87,65 @@ var (
// so library users can add custom formatters
FormatCheckers = FormatCheckerChain{
formatters: map[string]FormatChecker{
- "date-time": DateTimeFormatChecker{},
- "hostname": HostnameFormatChecker{},
- "email": EmailFormatChecker{},
- "ipv4": IPV4FormatChecker{},
- "ipv6": IPV6FormatChecker{},
- "uri": URIFormatChecker{},
- "uri-reference": URIReferenceFormatChecker{},
- "uuid": UUIDFormatChecker{},
- "regex": RegexFormatChecker{},
+ "date": DateFormatChecker{},
+ "time": TimeFormatChecker{},
+ "date-time": DateTimeFormatChecker{},
+ "hostname": HostnameFormatChecker{},
+ "email": EmailFormatChecker{},
+ "idn-email": EmailFormatChecker{},
+ "ipv4": IPV4FormatChecker{},
+ "ipv6": IPV6FormatChecker{},
+ "uri": URIFormatChecker{},
+ "uri-reference": URIReferenceFormatChecker{},
+ "iri": URIFormatChecker{},
+ "iri-reference": URIReferenceFormatChecker{},
+ "uri-template": URITemplateFormatChecker{},
+ "uuid": UUIDFormatChecker{},
+ "regex": RegexFormatChecker{},
+ "json-pointer": JSONPointerFormatChecker{},
+ "relative-json-pointer": RelativeJSONPointerFormatChecker{},
},
}
- // Regex credit: https://github.com/asaskevich/govalidator
- rxEmail = regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$")
-
// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
+ // Use a regex to make sure curly brackets are balanced properly after validating it as a AURI
+ rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")
+
rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
+
+ rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")
+
+ rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")
+
+ lock = new(sync.Mutex)
)
// Add adds a FormatChecker to the FormatCheckerChain
// The name used will be the value used for the format key in your json schema
func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
+ lock.Lock()
c.formatters[name] = f
+ lock.Unlock()
return c
}
// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
+ lock.Lock()
delete(c.formatters, name)
+ lock.Unlock()
return c
}
// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
func (c *FormatCheckerChain) Has(name string) bool {
+ lock.Lock()
_, ok := c.formatters[name]
+ lock.Unlock()
return ok
}
@@ -134,7 +169,9 @@ func (f EmailFormatChecker) IsFormat(input interface{}) bool {
return false
}
- return rxEmail.MatchString(asString)
+ _, err := mail.ParseAddress(asString)
+
+ return err == nil
}
// Credit: https://github.com/asaskevich/govalidator
@@ -185,6 +222,29 @@ func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
return false
}
+func (f DateFormatChecker) IsFormat(input interface{}) bool {
+ asString, ok := input.(string)
+ if ok == false {
+ return false
+ }
+ _, err := time.Parse("2006-01-02", asString)
+ return err == nil
+}
+
+func (f TimeFormatChecker) IsFormat(input interface{}) bool {
+ asString, ok := input.(string)
+ if ok == false {
+ return false
+ }
+
+ if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
+ return true
+ }
+
+ _, err := time.Parse("15:04:05", asString)
+ return err == nil
+}
+
func (f URIFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string)
@@ -193,11 +253,12 @@ func (f URIFormatChecker) IsFormat(input interface{}) bool {
}
u, err := url.Parse(asString)
+
if err != nil || u.Scheme == "" {
return false
}
- return true
+ return !strings.Contains(asString, `\`)
}
func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
@@ -208,7 +269,21 @@ func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
}
_, err := url.Parse(asString)
- return err == nil
+ return err == nil && !strings.Contains(asString, `\`)
+}
+
+func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
+ asString, ok := input.(string)
+ if ok == false {
+ return false
+ }
+
+ u, err := url.Parse(asString)
+ if err != nil || strings.Contains(asString, `\`) {
+ return false
+ }
+
+ return rxURITemplate.MatchString(u.Path)
}
func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
@@ -248,3 +323,21 @@ func (f RegexFormatChecker) IsFormat(input interface{}) bool {
}
return true
}
+
+func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
+ asString, ok := input.(string)
+ if ok == false {
+ return false
+ }
+
+ return rxJSONPointer.MatchString(asString)
+}
+
+func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
+ asString, ok := input.(string)
+ if ok == false {
+ return false
+ }
+
+ return rxRelJSONPointer.MatchString(asString)
+}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go b/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
index fcc8d9d6f..f40668a74 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
@@ -26,20 +26,20 @@ package gojsonschema
import "bytes"
-// jsonContext implements a persistent linked-list of strings
-type jsonContext struct {
+// JsonContext implements a persistent linked-list of strings
+type JsonContext struct {
head string
- tail *jsonContext
+ tail *JsonContext
}
-func newJsonContext(head string, tail *jsonContext) *jsonContext {
- return &jsonContext{head, tail}
+func NewJsonContext(head string, tail *JsonContext) *JsonContext {
+ return &JsonContext{head, tail}
}
// String displays the context in reverse.
// This plays well with the data structure's persistent nature with
// Cons and a json document's tree structure.
-func (c *jsonContext) String(del ...string) string {
+func (c *JsonContext) String(del ...string) string {
byteArr := make([]byte, 0, c.stringLen())
buf := bytes.NewBuffer(byteArr)
c.writeStringToBuffer(buf, del)
@@ -47,7 +47,7 @@ func (c *jsonContext) String(del ...string) string {
return buf.String()
}
-func (c *jsonContext) stringLen() int {
+func (c *JsonContext) stringLen() int {
length := 0
if c.tail != nil {
length = c.tail.stringLen() + 1 // add 1 for "."
@@ -57,7 +57,7 @@ func (c *jsonContext) stringLen() int {
return length
}
-func (c *jsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) {
+func (c *JsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) {
if c.tail != nil {
c.tail.writeStringToBuffer(buf, del)
diff --git a/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go b/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
index a77a81e40..cfa5f6a3a 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
@@ -38,7 +38,6 @@ import (
"runtime"
"strings"
-
"github.com/xeipuuv/gojsonreference"
)
@@ -108,7 +107,7 @@ func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory {
}
// NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
-func NewReferenceLoader(source string) *jsonReferenceLoader {
+func NewReferenceLoader(source string) JSONLoader {
return &jsonReferenceLoader{
fs: osFS,
source: source,
@@ -116,7 +115,7 @@ func NewReferenceLoader(source string) *jsonReferenceLoader {
}
// NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
-func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) *jsonReferenceLoader {
+func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader {
return &jsonReferenceLoader{
fs: fs,
source: source,
@@ -139,13 +138,11 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
if reference.HasFileScheme {
- filename := strings.Replace(refToUrl.GetUrl().Path, "file://", "", -1)
+ filename := strings.TrimPrefix(refToUrl.String(), "file://")
if runtime.GOOS == "windows" {
// on Windows, a file URL may have an extra leading slash, use slashes
// instead of backslashes, and have spaces escaped
- if strings.HasPrefix(filename, "/") {
- filename = filename[1:]
- }
+ filename = strings.TrimPrefix(filename, "/")
filename = filepath.FromSlash(filename)
}
@@ -169,6 +166,12 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
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))
+ }
+
resp, err := http.Get(address)
if err != nil {
return nil, err
@@ -185,7 +188,6 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error)
}
return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
-
}
func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
@@ -222,7 +224,7 @@ func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory {
return &DefaultJSONLoaderFactory{}
}
-func NewStringLoader(source string) *jsonStringLoader {
+func NewStringLoader(source string) JSONLoader {
return &jsonStringLoader{source: source}
}
@@ -250,7 +252,7 @@ func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory {
return &DefaultJSONLoaderFactory{}
}
-func NewBytesLoader(source []byte) *jsonBytesLoader {
+func NewBytesLoader(source []byte) JSONLoader {
return &jsonBytesLoader{source: source}
}
@@ -277,7 +279,7 @@ func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory {
return &DefaultJSONLoaderFactory{}
}
-func NewGoLoader(source interface{}) *jsonGoLoader {
+func NewGoLoader(source interface{}) JSONLoader {
return &jsonGoLoader{source: source}
}
@@ -298,12 +300,12 @@ type jsonIOLoader struct {
buf *bytes.Buffer
}
-func NewReaderLoader(source io.Reader) (*jsonIOLoader, io.Reader) {
+func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) {
buf := &bytes.Buffer{}
return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
}
-func NewWriterLoader(source io.Writer) (*jsonIOLoader, io.Writer) {
+func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) {
buf := &bytes.Buffer{}
return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
}
@@ -324,6 +326,30 @@ func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory {
return &DefaultJSONLoaderFactory{}
}
+// JSON raw loader
+// In case the JSON is already marshalled to interface{} use this loader
+// This is used for testing as otherwise there is no guarantee the JSON is marshalled
+// "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
+type jsonRawLoader struct {
+ source interface{}
+}
+
+func NewRawLoader(source interface{}) *jsonRawLoader {
+ return &jsonRawLoader{source: source}
+}
+func (l *jsonRawLoader) JsonSource() interface{} {
+ return l.source
+}
+func (l *jsonRawLoader) LoadJSON() (interface{}, error) {
+ return l.source, nil
+}
+func (l *jsonRawLoader) JsonReference() (gojsonreference.JsonReference, error) {
+ return gojsonreference.NewJsonReference("#")
+}
+func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory {
+ return &DefaultJSONLoaderFactory{}
+}
+
func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
var document interface{}
@@ -335,7 +361,7 @@ func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
if err != nil {
return nil, err
}
-
+
return document, nil
}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/locales.go b/vendor/github.com/xeipuuv/gojsonschema/locales.go
index ee41484a7..9b4570f01 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/locales.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/locales.go
@@ -36,16 +36,19 @@ type (
NumberNot() string
MissingDependency() string
Internal() string
+ Const() string
Enum() string
ArrayNotEnoughItems() string
ArrayNoAdditionalItems() string
ArrayMinItems() string
ArrayMaxItems() string
Unique() string
+ ArrayContains() string
ArrayMinProperties() string
ArrayMaxProperties() string
AdditionalPropertyNotAllowed() string
InvalidPropertyPattern() string
+ InvalidPropertyName() string
StringGTE() string
StringLTE() string
DoesNotMatchPattern() string
@@ -76,6 +79,9 @@ type (
HttpBadStatus() string
ParseError() string
+ ConditionThen() string
+ ConditionElse() string
+
// ErrorFormat
ErrorFormat() string
}
@@ -116,6 +122,10 @@ func (l DefaultLocale) Internal() string {
return `Internal Error {{.error}}`
}
+func (l DefaultLocale) Const() string {
+ return `{{.field}} does not match: {{.allowed}}`
+}
+
func (l DefaultLocale) Enum() string {
return `{{.field}} must be one of the following: {{.allowed}}`
}
@@ -137,7 +147,11 @@ func (l DefaultLocale) ArrayMaxItems() string {
}
func (l DefaultLocale) Unique() string {
- return `{{.type}} items must be unique`
+ return `{{.type}} items[{{.i}},{{.j}}] must be unique`
+}
+
+func (l DefaultLocale) ArrayContains() string {
+ return `At least one of the items must match`
}
func (l DefaultLocale) ArrayMinProperties() string {
@@ -156,6 +170,10 @@ func (l DefaultLocale) InvalidPropertyPattern() string {
return `Property "{{.property}}" does not match pattern {{.pattern}}`
}
+func (l DefaultLocale) InvalidPropertyName() string {
+ return `Property name of "{{.property}}" does not match`
+}
+
func (l DefaultLocale) StringGTE() string {
return `String length must be greater than or equal to {{.min}}`
}
@@ -268,14 +286,23 @@ func (l DefaultLocale) ErrorFormat() string {
//Parse error
func (l DefaultLocale) ParseError() string {
- return `Expected: %expected%, given: Invalid JSON`
+ return `Expected: {{.expected}}, given: Invalid JSON`
+}
+
+//If/Else
+func (l DefaultLocale) ConditionThen() string {
+ return `Must validate "then" as "if" was valid`
+}
+
+func (l DefaultLocale) ConditionElse() string {
+ return `Must validate "else" as "if" was not valid`
}
const (
STRING_NUMBER = "number"
STRING_ARRAY_OF_STRINGS = "array of strings"
STRING_ARRAY_OF_SCHEMAS = "array of schemas"
- STRING_SCHEMA = "schema"
+ STRING_SCHEMA = "valid schema"
STRING_SCHEMA_OR_ARRAY_OF_STRINGS = "schema or array of strings"
STRING_PROPERTIES = "properties"
STRING_DEPENDENCY = "dependency"
diff --git a/vendor/github.com/xeipuuv/gojsonschema/result.go b/vendor/github.com/xeipuuv/gojsonschema/result.go
index 6ad56ae86..040d35c8d 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/result.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/result.go
@@ -40,10 +40,12 @@ type (
Field() string
SetType(string)
Type() string
- SetContext(*jsonContext)
- Context() *jsonContext
+ SetContext(*JsonContext)
+ Context() *JsonContext
SetDescription(string)
Description() string
+ SetDescriptionFormat(string)
+ DescriptionFormat() string
SetValue(interface{})
Value() interface{}
SetDetails(ErrorDetails)
@@ -55,11 +57,12 @@ type (
// ResultErrorFields implements the ResultError interface, so custom errors
// can be defined by just embedding this type
ResultErrorFields struct {
- errorType string // A string with the type of error (i.e. invalid_type)
- context *jsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
- description string // A human readable error message
- value interface{} // Value given by the JSON file that is the source of the error
- details ErrorDetails
+ errorType string // A string with the type of error (i.e. invalid_type)
+ context *JsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
+ description string // A human readable error message
+ descriptionFormat string // A format for human readable error message
+ value interface{} // Value given by the JSON file that is the source of the error
+ details ErrorDetails
}
Result struct {
@@ -73,12 +76,6 @@ type (
// Field outputs 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 {
- if p, ok := v.Details()["property"]; ok {
- if str, isString := p.(string); isString {
- return str
- }
- }
-
return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".")
}
@@ -90,11 +87,11 @@ func (v *ResultErrorFields) Type() string {
return v.errorType
}
-func (v *ResultErrorFields) SetContext(context *jsonContext) {
+func (v *ResultErrorFields) SetContext(context *JsonContext) {
v.context = context
}
-func (v *ResultErrorFields) Context() *jsonContext {
+func (v *ResultErrorFields) Context() *JsonContext {
return v.context
}
@@ -106,6 +103,14 @@ func (v *ResultErrorFields) Description() string {
return v.description
}
+func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) {
+ v.descriptionFormat = descriptionFormat
+}
+
+func (v *ResultErrorFields) DescriptionFormat() string {
+ return v.descriptionFormat
+}
+
func (v *ResultErrorFields) SetValue(value interface{}) {
v.value = value
}
@@ -155,7 +160,19 @@ func (v *Result) Errors() []ResultError {
return v.errors
}
-func (v *Result) addError(err ResultError, context *jsonContext, value interface{}, details ErrorDetails) {
+// Add 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 {
+ details["context"] = err.Context().String()
+ }
+
+ err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
+
+ v.errors = append(v.errors, err)
+}
+
+func (v *Result) addInternalError(err ResultError, context *JsonContext, value interface{}, details ErrorDetails) {
newError(err, context, value, Locale, details)
v.errors = append(v.errors, err)
v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function
diff --git a/vendor/github.com/xeipuuv/gojsonschema/schema.go b/vendor/github.com/xeipuuv/gojsonschema/schema.go
index 2cac71e9b..323fe8559 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/schema.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/schema.go
@@ -27,8 +27,8 @@
package gojsonschema
import (
- // "encoding/json"
"errors"
+ "math/big"
"reflect"
"regexp"
"text/template"
@@ -46,39 +46,7 @@ var (
)
func NewSchema(l JSONLoader) (*Schema, error) {
- ref, err := l.JsonReference()
- if err != nil {
- return nil, err
- }
-
- d := Schema{}
- d.pool = newSchemaPool(l.LoaderFactory())
- d.documentReference = ref
- d.referencePool = newSchemaReferencePool()
-
- var doc interface{}
- if ref.String() != "" {
- // Get document from schema pool
- spd, err := d.pool.GetDocument(d.documentReference)
- if err != nil {
- return nil, err
- }
- doc = spd.Document
- } else {
- // Load JSON directly
- doc, err = l.LoadJSON()
- if err != nil {
- return nil, err
- }
- d.pool.SetStandaloneDocument(doc)
- }
-
- err = d.parse(doc)
- if err != nil {
- return nil, err
- }
-
- return &d, nil
+ return NewSchemaLoader().Compile(l)
}
type Schema struct {
@@ -88,8 +56,8 @@ type Schema struct {
referencePool *schemaReferencePool
}
-func (d *Schema) parse(document interface{}) error {
- d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY}
+func (d *Schema) parse(document interface{}, draft Draft) error {
+ d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY, draft: &draft}
return d.parseSchema(document, d.rootSchema)
}
@@ -105,6 +73,23 @@ func (d *Schema) SetRootSchemaName(name string) {
//
func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error {
+ if currentSchema.draft == nil {
+ if currentSchema.parent == nil {
+ return errors.New("Draft not set")
+ }
+ currentSchema.draft = currentSchema.parent.draft
+ }
+
+ // As of draft 6 "true" is equivalent to an empty schema "{}" and false equals "{"not":{}}"
+ if *currentSchema.draft >= Draft6 && isKind(documentNode, reflect.Bool) {
+ b := documentNode.(bool)
+ if b {
+ documentNode = map[string]interface{}{}
+ } else {
+ documentNode = map[string]interface{}{"not": true}
+ }
+ }
+
if !isKind(documentNode, reflect.Map) {
return errors.New(formatErrorDescription(
Locale.ParseError(),
@@ -116,81 +101,67 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
m := documentNode.(map[string]interface{})
- if currentSchema == d.rootSchema {
+ if currentSchema.parent == nil {
currentSchema.ref = &d.documentReference
+ currentSchema.id = &d.documentReference
}
- // $subSchema
- if existsMapKey(m, KEY_SCHEMA) {
- if !isKind(m[KEY_SCHEMA], reflect.String) {
- return errors.New(formatErrorDescription(
- Locale.InvalidType(),
- ErrorDetails{
- "expected": TYPE_STRING,
- "given": KEY_SCHEMA,
- },
- ))
- }
- schemaRef := m[KEY_SCHEMA].(string)
- schemaReference, err := gojsonreference.NewJsonReference(schemaRef)
- currentSchema.subSchema = &schemaReference
- if err != nil {
- return err
- }
+ if currentSchema.id == nil && currentSchema.parent != nil {
+ currentSchema.id = currentSchema.parent.id
}
- // $ref
- if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) {
+ // In draft 6 the id keyword was renamed to $id
+ // Hybrid mode uses the old id by default
+ var keyID string
+
+ switch *currentSchema.draft {
+ case Draft4:
+ keyID = KEY_ID
+ case Hybrid:
+ keyID = KEY_ID_NEW
+ if existsMapKey(m, KEY_ID) {
+ keyID = KEY_ID
+ }
+ default:
+ keyID = KEY_ID_NEW
+ }
+ if existsMapKey(m, keyID) && !isKind(m[keyID], reflect.String) {
return errors.New(formatErrorDescription(
Locale.InvalidType(),
ErrorDetails{
"expected": TYPE_STRING,
- "given": KEY_REF,
+ "given": keyID,
},
))
}
- if k, ok := m[KEY_REF].(string); ok {
-
+ if k, ok := m[keyID].(string); ok {
jsonReference, err := gojsonreference.NewJsonReference(k)
if err != nil {
return err
}
-
- if jsonReference.HasFullUrl {
- currentSchema.ref = &jsonReference
- } else {
- inheritedReference, err := currentSchema.ref.Inherits(jsonReference)
- if err != nil {
- return err
- }
-
- currentSchema.ref = inheritedReference
- }
-
- if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok {
- currentSchema.refSchema = sch
-
+ if currentSchema == d.rootSchema {
+ currentSchema.id = &jsonReference
} else {
- err := d.parseReference(documentNode, currentSchema, k)
+ ref, err := currentSchema.parent.id.Inherits(jsonReference)
if err != nil {
return err
}
-
- return nil
+ currentSchema.id = ref
}
}
// definitions
if existsMapKey(m, KEY_DEFINITIONS) {
- if isKind(m[KEY_DEFINITIONS], reflect.Map) {
- currentSchema.definitions = make(map[string]*subSchema)
- for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
- if isKind(dv, reflect.Map) {
- newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref}
- currentSchema.definitions[dk] = newSchema
+ if isKind(m[KEY_DEFINITIONS], reflect.Map, reflect.Bool) {
+ for _, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
+ if isKind(dv, reflect.Map, reflect.Bool) {
+
+ newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema}
+
err := d.parseSchema(dv, newSchema)
+
if err != nil {
- return errors.New(err.Error())
+ return err
}
} else {
return errors.New(formatErrorDescription(
@@ -214,20 +185,6 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
- // id
- if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) {
- return errors.New(formatErrorDescription(
- Locale.InvalidType(),
- ErrorDetails{
- "expected": TYPE_STRING,
- "given": KEY_ID,
- },
- ))
- }
- if k, ok := m[KEY_ID].(string); ok {
- currentSchema.id = &k
- }
-
// title
if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
return errors.New(formatErrorDescription(
@@ -256,6 +213,39 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
currentSchema.description = &k
}
+ // $ref
+ if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_STRING,
+ "given": KEY_REF,
+ },
+ ))
+ }
+
+ if k, ok := m[KEY_REF].(string); ok {
+
+ jsonReference, err := gojsonreference.NewJsonReference(k)
+ if err != nil {
+ return err
+ }
+
+ currentSchema.ref = &jsonReference
+
+ if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok {
+ currentSchema.refSchema = sch
+ } else {
+ err := d.parseReference(documentNode, currentSchema)
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
+ }
+
// type
if existsMapKey(m, KEY_TYPE) {
if isKind(m[KEY_TYPE], reflect.String) {
@@ -357,6 +347,26 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
}
+ // propertyNames
+ if existsMapKey(m, KEY_PROPERTY_NAMES) && *currentSchema.draft >= Draft6 {
+ if isKind(m[KEY_PROPERTY_NAMES], reflect.Map, reflect.Bool) {
+ newSchema := &subSchema{property: KEY_PROPERTY_NAMES, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.propertyNames = newSchema
+ err := d.parseSchema(m[KEY_PROPERTY_NAMES], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": STRING_SCHEMA,
+ "given": KEY_PATTERN_PROPERTIES,
+ },
+ ))
+ }
+ }
+
// dependencies
if existsMapKey(m, KEY_DEPENDENCIES) {
err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema)
@@ -369,7 +379,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
if existsMapKey(m, KEY_ITEMS) {
if isKind(m[KEY_ITEMS], reflect.Slice) {
for _, itemElement := range m[KEY_ITEMS].([]interface{}) {
- if isKind(itemElement, reflect.Map) {
+ if isKind(itemElement, reflect.Map, reflect.Bool) {
newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
newSchema.ref = currentSchema.ref
currentSchema.AddItemsChild(newSchema)
@@ -388,7 +398,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
currentSchema.itemsChildrenIsSingleSchema = false
}
- } else if isKind(m[KEY_ITEMS], reflect.Map) {
+ } else if isKind(m[KEY_ITEMS], reflect.Map, reflect.Bool) {
newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
newSchema.ref = currentSchema.ref
currentSchema.AddItemsChild(newSchema)
@@ -443,7 +453,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
},
))
}
- if *multipleOfValue <= 0 {
+ if multipleOfValue.Cmp(big.NewRat(0, 1)) <= 0 {
return errors.New(formatErrorDescription(
Locale.GreaterThanZero(),
ErrorDetails{"number": KEY_MULTIPLE_OF},
@@ -464,20 +474,62 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) {
- if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
+ switch *currentSchema.draft {
+ case Draft4:
+ if !isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_BOOLEAN,
+ "given": KEY_EXCLUSIVE_MINIMUM,
+ },
+ ))
+ }
if currentSchema.minimum == nil {
return errors.New(formatErrorDescription(
Locale.CannotBeUsedWithout(),
ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
))
}
- exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool)
- currentSchema.exclusiveMinimum = exclusiveMinimumValue
- } else {
- return errors.New(formatErrorDescription(
- Locale.MustBeOfA(),
- ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN},
- ))
+ if m[KEY_EXCLUSIVE_MINIMUM].(bool) {
+ currentSchema.exclusiveMinimum = currentSchema.minimum
+ currentSchema.minimum = nil
+ }
+ case Hybrid:
+ if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
+ if currentSchema.minimum == nil {
+ return errors.New(formatErrorDescription(
+ Locale.CannotBeUsedWithout(),
+ ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
+ ))
+ }
+ if m[KEY_EXCLUSIVE_MINIMUM].(bool) {
+ currentSchema.exclusiveMinimum = currentSchema.minimum
+ currentSchema.minimum = nil
+ }
+ } else if isJsonNumber(m[KEY_EXCLUSIVE_MINIMUM]) {
+ currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM])
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER,
+ "given": KEY_EXCLUSIVE_MINIMUM,
+ },
+ ))
+ }
+ default:
+ if isJsonNumber(m[KEY_EXCLUSIVE_MINIMUM]) {
+ currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM])
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_NUMBER,
+ "given": KEY_EXCLUSIVE_MINIMUM,
+ },
+ ))
+ }
}
}
@@ -493,29 +545,62 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) {
- if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
+ switch *currentSchema.draft {
+ case Draft4:
+ if !isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_BOOLEAN,
+ "given": KEY_EXCLUSIVE_MAXIMUM,
+ },
+ ))
+ }
if currentSchema.maximum == nil {
return errors.New(formatErrorDescription(
Locale.CannotBeUsedWithout(),
ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
))
}
- exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool)
- currentSchema.exclusiveMaximum = exclusiveMaximumValue
- } else {
- return errors.New(formatErrorDescription(
- Locale.MustBeOfA(),
- ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER},
- ))
- }
- }
-
- if currentSchema.minimum != nil && currentSchema.maximum != nil {
- if *currentSchema.minimum > *currentSchema.maximum {
- return errors.New(formatErrorDescription(
- Locale.CannotBeGT(),
- ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM},
- ))
+ if m[KEY_EXCLUSIVE_MAXIMUM].(bool) {
+ currentSchema.exclusiveMaximum = currentSchema.maximum
+ currentSchema.maximum = nil
+ }
+ case Hybrid:
+ if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
+ if currentSchema.maximum == nil {
+ return errors.New(formatErrorDescription(
+ Locale.CannotBeUsedWithout(),
+ ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
+ ))
+ }
+ if m[KEY_EXCLUSIVE_MAXIMUM].(bool) {
+ currentSchema.exclusiveMaximum = currentSchema.maximum
+ currentSchema.maximum = nil
+ }
+ } else if isJsonNumber(m[KEY_EXCLUSIVE_MAXIMUM]) {
+ currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM])
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER,
+ "given": KEY_EXCLUSIVE_MAXIMUM,
+ },
+ ))
+ }
+ default:
+ if isJsonNumber(m[KEY_EXCLUSIVE_MAXIMUM]) {
+ currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM])
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.InvalidType(),
+ ErrorDetails{
+ "expected": TYPE_NUMBER,
+ "given": KEY_EXCLUSIVE_MAXIMUM,
+ },
+ ))
+ }
}
}
@@ -705,8 +790,24 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
}
+ if existsMapKey(m, KEY_CONTAINS) && *currentSchema.draft >= Draft6 {
+ newSchema := &subSchema{property: KEY_CONTAINS, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.contains = newSchema
+ err := d.parseSchema(m[KEY_CONTAINS], newSchema)
+ if err != nil {
+ return err
+ }
+ }
+
// validation : all
+ if existsMapKey(m, KEY_CONST) && *currentSchema.draft >= Draft6 {
+ err := currentSchema.AddConst(m[KEY_CONST])
+ if err != nil {
+ return err
+ }
+ }
+
if existsMapKey(m, KEY_ENUM) {
if isKind(m[KEY_ENUM], reflect.Slice) {
for _, v := range m[KEY_ENUM].([]interface{}) {
@@ -780,7 +881,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
if existsMapKey(m, KEY_NOT) {
- if isKind(m[KEY_NOT], reflect.Map) {
+ if isKind(m[KEY_NOT], reflect.Map, reflect.Bool) {
newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref}
currentSchema.SetNot(newSchema)
err := d.parseSchema(m[KEY_NOT], newSchema)
@@ -795,48 +896,91 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
}
}
+ if *currentSchema.draft >= Draft7 {
+ if existsMapKey(m, KEY_IF) {
+ if isKind(m[KEY_IF], reflect.Map, reflect.Bool) {
+ newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.SetIf(newSchema)
+ err := d.parseSchema(m[KEY_IF], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.MustBeOfAn(),
+ ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT},
+ ))
+ }
+ }
+
+ if existsMapKey(m, KEY_THEN) {
+ if isKind(m[KEY_THEN], reflect.Map, reflect.Bool) {
+ newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.SetThen(newSchema)
+ err := d.parseSchema(m[KEY_THEN], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.MustBeOfAn(),
+ ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT},
+ ))
+ }
+ }
+
+ if existsMapKey(m, KEY_ELSE) {
+ if isKind(m[KEY_ELSE], reflect.Map, reflect.Bool) {
+ newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.SetElse(newSchema)
+ err := d.parseSchema(m[KEY_ELSE], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.MustBeOfAn(),
+ ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT},
+ ))
+ }
+ }
+ }
+
return nil
}
-func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error {
- var refdDocumentNode interface{}
- jsonPointer := currentSchema.ref.GetPointer()
- standaloneDocument := d.pool.GetStandaloneDocument()
+func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error {
+ var (
+ refdDocumentNode interface{}
+ dsp *schemaPoolDocument
+ err error
+ )
- if standaloneDocument != nil {
+ newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
- var err error
- refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument)
- if err != nil {
- return err
- }
+ d.referencePool.Add(currentSchema.ref.String(), newSchema)
- } else {
- dsp, err := d.pool.GetDocument(*currentSchema.ref)
- if err != nil {
- return err
- }
+ dsp, err = d.pool.GetDocument(*currentSchema.ref)
+ if err != nil {
+ return err
+ }
+ newSchema.id = currentSchema.ref
- refdDocumentNode, _, err = jsonPointer.Get(dsp.Document)
- if err != nil {
- return err
- }
+ refdDocumentNode = dsp.Document
+ newSchema.draft = dsp.Draft
+ if err != nil {
+ return err
}
- if !isKind(refdDocumentNode, reflect.Map) {
+ if !isKind(refdDocumentNode, reflect.Map, reflect.Bool) {
return errors.New(formatErrorDescription(
Locale.MustBeOfType(),
ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT},
))
}
- // returns the loaded referenced subSchema for the caller to update its current subSchema
- newSchemaDocument := refdDocumentNode.(map[string]interface{})
- newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
- d.referencePool.Add(currentSchema.ref.String()+reference, newSchema)
-
- err := d.parseSchema(newSchemaDocument, newSchema)
+ err = d.parseSchema(refdDocumentNode, newSchema)
if err != nil {
return err
}
@@ -904,7 +1048,7 @@ func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subS
currentSchema.dependencies[k] = valuesToRegister
}
- case reflect.Map:
+ case reflect.Map, reflect.Bool:
depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
err := d.parseSchema(m[k], depSchema)
if err != nil {
diff --git a/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go b/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go
new file mode 100644
index 000000000..95e0568ab
--- /dev/null
+++ b/vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go
@@ -0,0 +1,203 @@
+// Copyright 2018 johandorland ( https://github.com/johandorland )
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package gojsonschema
+
+import (
+ "bytes"
+ "errors"
+
+ "github.com/xeipuuv/gojsonreference"
+)
+
+type SchemaLoader struct {
+ pool *schemaPool
+ AutoDetect bool
+ Validate bool
+ Draft Draft
+}
+
+func NewSchemaLoader() *SchemaLoader {
+
+ ps := &SchemaLoader{
+ pool: &schemaPool{
+ schemaPoolDocuments: make(map[string]*schemaPoolDocument),
+ },
+ AutoDetect: true,
+ Validate: false,
+ Draft: Hybrid,
+ }
+ ps.pool.autoDetect = &ps.AutoDetect
+
+ return ps
+}
+
+func (sl *SchemaLoader) validateMetaschema(documentNode interface{}) error {
+
+ var (
+ schema string
+ err error
+ )
+ if sl.AutoDetect {
+ schema, _, err = parseSchemaURL(documentNode)
+ if err != nil {
+ return err
+ }
+ }
+
+ // If no explicit "$schema" is used, use the default metaschema associated with the draft used
+ if schema == "" {
+ if sl.Draft == Hybrid {
+ return nil
+ }
+ schema = drafts.GetSchemaURL(sl.Draft)
+ }
+
+ //Disable validation when loading the metaschema to prevent an infinite recursive loop
+ sl.Validate = false
+
+ metaSchema, err := sl.Compile(NewReferenceLoader(schema))
+
+ if err != nil {
+ return err
+ }
+
+ sl.Validate = true
+
+ result := metaSchema.validateDocument(documentNode)
+
+ if !result.Valid() {
+ var res bytes.Buffer
+ for _, err := range result.Errors() {
+ res.WriteString(err.String())
+ res.WriteString("\n")
+ }
+ return errors.New(res.String())
+ }
+
+ return nil
+}
+
+// AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require
+// an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema
+func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error {
+ emptyRef, _ := gojsonreference.NewJsonReference("")
+
+ for _, loader := range loaders {
+ doc, err := loader.LoadJSON()
+
+ if err != nil {
+ return err
+ }
+
+ if sl.Validate {
+ if err := sl.validateMetaschema(doc); err != nil {
+ return err
+ }
+ }
+
+ // Directly use the Recursive function, so that it get only added to the schema pool by $id
+ // and not by the ref of the document as it's empty
+ if err = sl.pool.parseReferences(doc, emptyRef, false); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+//AddSchema adds a schema under the provided URL to the schema cache
+func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error {
+
+ ref, err := gojsonreference.NewJsonReference(url)
+
+ if err != nil {
+ return err
+ }
+
+ doc, err := loader.LoadJSON()
+
+ if err != nil {
+ return err
+ }
+
+ if sl.Validate {
+ if err := sl.validateMetaschema(doc); err != nil {
+ return err
+ }
+ }
+
+ return sl.pool.parseReferences(doc, ref, true)
+}
+
+func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) {
+
+ ref, err := rootSchema.JsonReference()
+
+ if err != nil {
+ return nil, err
+ }
+
+ d := Schema{}
+ d.pool = sl.pool
+ d.pool.jsonLoaderFactory = rootSchema.LoaderFactory()
+ d.documentReference = ref
+ d.referencePool = newSchemaReferencePool()
+
+ var doc interface{}
+ if ref.String() != "" {
+ // Get document from schema pool
+ spd, err := d.pool.GetDocument(d.documentReference)
+ if err != nil {
+ return nil, err
+ }
+ doc = spd.Document
+ } else {
+ // Load JSON directly
+ doc, err = rootSchema.LoadJSON()
+ if err != nil {
+ return nil, err
+ }
+ // References need only be parsed if loading JSON directly
+ // as pool.GetDocument already does this for us if loading by reference
+ err = sl.pool.parseReferences(doc, ref, true)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if sl.Validate {
+ if err := sl.validateMetaschema(doc); err != nil {
+ return nil, err
+ }
+ }
+
+ draft := sl.Draft
+ if sl.AutoDetect {
+ _, detectedDraft, err := parseSchemaURL(doc)
+ if err != nil {
+ return nil, err
+ }
+ if detectedDraft != nil {
+ draft = *detectedDraft
+ }
+ }
+
+ err = d.parse(doc, draft)
+ if err != nil {
+ return nil, err
+ }
+
+ return &d, nil
+}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go b/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
index f2ad641af..f124e038d 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
@@ -28,82 +28,188 @@ package gojsonschema
import (
"errors"
+ "fmt"
+ "reflect"
"github.com/xeipuuv/gojsonreference"
)
type schemaPoolDocument struct {
Document interface{}
+ Draft *Draft
}
type schemaPool struct {
schemaPoolDocuments map[string]*schemaPoolDocument
- standaloneDocument interface{}
jsonLoaderFactory JSONLoaderFactory
+ autoDetect *bool
}
-func newSchemaPool(f JSONLoaderFactory) *schemaPool {
+func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.JsonReference, pooled bool) error {
- p := &schemaPool{}
- p.schemaPoolDocuments = make(map[string]*schemaPoolDocument)
- p.standaloneDocument = nil
- p.jsonLoaderFactory = f
+ var (
+ draft *Draft
+ err error
+ reference = ref.String()
+ )
+ // Only the root document should be added to the schema pool if pooled is true
+ if _, ok := p.schemaPoolDocuments[reference]; pooled && ok {
+ return fmt.Errorf("Reference already exists: \"%s\"", reference)
+ }
- return p
-}
+ if *p.autoDetect {
+ _, draft, err = parseSchemaURL(document)
+ if err != nil {
+ return err
+ }
+ }
+
+ err = p.parseReferencesRecursive(document, ref, draft)
-func (p *schemaPool) SetStandaloneDocument(document interface{}) {
- p.standaloneDocument = document
+ if pooled {
+ p.schemaPoolDocuments[reference] = &schemaPoolDocument{Document: document, Draft: draft}
+ }
+
+ return err
}
-func (p *schemaPool) GetStandaloneDocument() (document interface{}) {
- return p.standaloneDocument
+func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference, draft *Draft) error {
+ // parseReferencesRecursive parses a JSON document and resolves all $id and $ref references.
+ // For $ref references it takes into account the $id scope it is in and replaces
+ // the reference by the absolute resolved reference
+
+ // When encountering errors it fails silently. Error handling is done when the schema
+ // is syntactically parsed and any error encountered here should also come up there.
+ switch m := document.(type) {
+ case []interface{}:
+ for _, v := range m {
+ p.parseReferencesRecursive(v, ref, draft)
+ }
+ case map[string]interface{}:
+ localRef := &ref
+
+ keyID := KEY_ID_NEW
+ if existsMapKey(m, KEY_ID) {
+ keyID = KEY_ID
+ }
+ if existsMapKey(m, keyID) && isKind(m[keyID], reflect.String) {
+ jsonReference, err := gojsonreference.NewJsonReference(m[keyID].(string))
+ if err == nil {
+ localRef, err = ref.Inherits(jsonReference)
+ if err == nil {
+ if _, ok := p.schemaPoolDocuments[localRef.String()]; ok {
+ return fmt.Errorf("Reference already exists: \"%s\"", localRef.String())
+ }
+ p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document, Draft: draft}
+ }
+ }
+ }
+
+ if existsMapKey(m, KEY_REF) && isKind(m[KEY_REF], reflect.String) {
+ jsonReference, err := gojsonreference.NewJsonReference(m[KEY_REF].(string))
+ if err == nil {
+ absoluteRef, err := localRef.Inherits(jsonReference)
+ if err == nil {
+ m[KEY_REF] = absoluteRef.String()
+ }
+ }
+ }
+
+ for k, v := range m {
+ // const and enums should be interpreted literally, so ignore them
+ if k == KEY_CONST || k == KEY_ENUM {
+ continue
+ }
+ // Something like a property or a dependency is not a valid schema, as it might describe properties named "$ref", "$id" or "const", etc
+ // Therefore don't treat it like a schema.
+ if k == KEY_PROPERTIES || k == KEY_DEPENDENCIES || k == KEY_PATTERN_PROPERTIES {
+ if child, ok := v.(map[string]interface{}); ok {
+ for _, v := range child {
+ p.parseReferencesRecursive(v, *localRef, draft)
+ }
+ }
+ } else {
+ p.parseReferencesRecursive(v, *localRef, draft)
+ }
+ }
+ }
+ return nil
}
func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) {
+ var (
+ spd *schemaPoolDocument
+ draft *Draft
+ ok bool
+ err error
+ )
+
if internalLogEnabled {
internalLog("Get Document ( %s )", reference.String())
}
- var err error
+ // Create a deep copy, so we can remove the fragment part later on without altering the original
+ refToUrl, _ := gojsonreference.NewJsonReference(reference.String())
- // It is not possible to load anything that is not canonical...
- if !reference.IsCanonical() {
- return nil, errors.New(formatErrorDescription(
- Locale.ReferenceMustBeCanonical(),
- ErrorDetails{"reference": reference},
- ))
+ // 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 internalLogEnabled {
+ internalLog(" From pool")
+ }
+ return spd, nil
}
- refToUrl := reference
+ // 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 = ""
- var spd *schemaPoolDocument
+ if cachedSpd, ok := p.schemaPoolDocuments[refToUrl.String()]; ok {
+ document, _, err := reference.GetPointer().Get(cachedSpd.Document)
- // Try to find the requested document in the pool
- for k := range p.schemaPoolDocuments {
- if k == refToUrl.String() {
- spd = p.schemaPoolDocuments[k]
+ if err != nil {
+ return nil, err
}
- }
- if spd != nil {
if internalLogEnabled {
internalLog(" From pool")
}
+
+ spd = &schemaPoolDocument{Document: document, Draft: cachedSpd.Draft}
+ p.schemaPoolDocuments[reference.String()] = spd
+
return spd, nil
}
+ // It is not possible to load anything remotely that is not canonical...
+ if !reference.IsCanonical() {
+ return nil, errors.New(formatErrorDescription(
+ Locale.ReferenceMustBeCanonical(),
+ ErrorDetails{"reference": reference.String()},
+ ))
+ }
+
jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String())
document, err := jsonReferenceLoader.LoadJSON()
+
if err != nil {
return nil, err
}
- spd = &schemaPoolDocument{Document: document}
- // add the document to the pool for potential later use
- p.schemaPoolDocuments[refToUrl.String()] = spd
+ // add the whole document to the pool for potential re-use
+ p.parseReferences(document, refToUrl, true)
+
+ _, draft, _ = parseSchemaURL(document)
+
+ // resolve the potential fragment and also cache it
+ document, _, err = reference.GetPointer().Get(document)
+
+ if err != nil {
+ return nil, err
+ }
- return spd, nil
+ return &schemaPoolDocument{Document: document, Draft: draft}, nil
}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go b/vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
index 294e36a73..6e5e1b5cd 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
@@ -62,6 +62,7 @@ func (p *schemaReferencePool) Add(ref string, sch *subSchema) {
if internalLogEnabled {
internalLog(fmt.Sprintf("Add Schema Reference %s to pool", ref))
}
-
- p.documents[ref] = sch
+ if _, ok := p.documents[ref]; !ok {
+ p.documents[ref] = sch
+ }
}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/subSchema.go b/vendor/github.com/xeipuuv/gojsonschema/subSchema.go
index 9ddbb5fc1..362d86ca9 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/subSchema.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/subSchema.go
@@ -28,6 +28,7 @@ package gojsonschema
import (
"errors"
+ "math/big"
"regexp"
"strings"
@@ -35,8 +36,9 @@ import (
)
const (
- KEY_SCHEMA = "$subSchema"
- KEY_ID = "$id"
+ KEY_SCHEMA = "$schema"
+ KEY_ID = "id"
+ KEY_ID_NEW = "$id"
KEY_REF = "$ref"
KEY_TITLE = "title"
KEY_DESCRIPTION = "description"
@@ -46,6 +48,7 @@ const (
KEY_PROPERTIES = "properties"
KEY_PATTERN_PROPERTIES = "patternProperties"
KEY_ADDITIONAL_PROPERTIES = "additionalProperties"
+ KEY_PROPERTY_NAMES = "propertyNames"
KEY_DEFINITIONS = "definitions"
KEY_MULTIPLE_OF = "multipleOf"
KEY_MINIMUM = "minimum"
@@ -63,17 +66,23 @@ const (
KEY_MIN_ITEMS = "minItems"
KEY_MAX_ITEMS = "maxItems"
KEY_UNIQUE_ITEMS = "uniqueItems"
+ KEY_CONTAINS = "contains"
+ KEY_CONST = "const"
KEY_ENUM = "enum"
KEY_ONE_OF = "oneOf"
KEY_ANY_OF = "anyOf"
KEY_ALL_OF = "allOf"
KEY_NOT = "not"
+ KEY_IF = "if"
+ KEY_THEN = "then"
+ KEY_ELSE = "else"
)
type subSchema struct {
+ draft *Draft
// basic subSchema meta properties
- id *string
+ id *gojsonreference.JsonReference
title *string
description *string
@@ -86,23 +95,19 @@ type subSchema struct {
ref *gojsonreference.JsonReference
// Schema referenced
refSchema *subSchema
- // Json reference
- subSchema *gojsonreference.JsonReference
// hierarchy
parent *subSchema
- definitions map[string]*subSchema
- definitionsChildren []*subSchema
itemsChildren []*subSchema
itemsChildrenIsSingleSchema bool
propertiesChildren []*subSchema
// validation : number / integer
- multipleOf *float64
- maximum *float64
- exclusiveMaximum bool
- minimum *float64
- exclusiveMinimum bool
+ multipleOf *big.Rat
+ maximum *big.Rat
+ exclusiveMaximum *big.Rat
+ minimum *big.Rat
+ exclusiveMinimum *big.Rat
// validation : string
minLength *int
@@ -118,27 +123,43 @@ type subSchema struct {
dependencies map[string]interface{}
additionalProperties interface{}
patternProperties map[string]*subSchema
+ propertyNames *subSchema
// validation : array
minItems *int
maxItems *int
uniqueItems bool
+ contains *subSchema
additionalItems interface{}
// validation : all
- enum []string
+ _const *string //const is a golang keyword
+ enum []string
// validation : subSchema
oneOf []*subSchema
anyOf []*subSchema
allOf []*subSchema
not *subSchema
+ _if *subSchema // if/else are golang keywords
+ _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 := marshalToJsonString(i)
+ is, err := marshalWithoutNumber(i)
if err != nil {
return err
}
@@ -157,7 +178,7 @@ func (s *subSchema) AddEnum(i interface{}) error {
func (s *subSchema) ContainsEnum(i interface{}) (bool, error) {
- is, err := marshalToJsonString(i)
+ is, err := marshalWithoutNumber(i)
if err != nil {
return false, err
}
@@ -181,6 +202,18 @@ 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) {
@@ -195,10 +228,6 @@ func (s *subSchema) AddRequired(value string) error {
return nil
}
-func (s *subSchema) AddDefinitionChild(child *subSchema) {
- s.definitionsChildren = append(s.definitionsChildren, child)
-}
-
func (s *subSchema) AddItemsChild(child *subSchema) {
s.itemsChildren = append(s.itemsChildren, child)
}
diff --git a/vendor/github.com/xeipuuv/gojsonschema/utils.go b/vendor/github.com/xeipuuv/gojsonschema/utils.go
index 26cf75ebf..88d223fbf 100644
--- a/vendor/github.com/xeipuuv/gojsonschema/utils.go
+++ b/vendor/github.com/xeipuuv/gojsonschema/utils.go
@@ -29,17 +29,23 @@ import (
"encoding/json"
"fmt"
"math"
+ "math/big"
"reflect"
- "strconv"
)
-func isKind(what interface{}, kind reflect.Kind) bool {
+func isKind(what interface{}, kinds ...reflect.Kind) bool {
target := what
if isJsonNumber(what) {
// JSON Numbers are strings!
target = *mustBeNumber(what)
}
- return reflect.ValueOf(target).Kind() == kind
+ targetKind := reflect.ValueOf(target).Kind()
+ for _, kind := range kinds {
+ if targetKind == kind {
+ return true
+ }
+ }
+ return false
}
func existsMapKey(m map[string]interface{}, k string) bool {
@@ -56,6 +62,16 @@ func isStringInSlice(s []string, what string) bool {
return false
}
+// indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s.
+func indexStringInSlice(s []string, what string) int {
+ for i := range s {
+ if s[i] == what {
+ return i
+ }
+ }
+ return -1
+}
+
func marshalToJsonString(value interface{}) (*string, error) {
mBytes, err := json.Marshal(value)
@@ -67,6 +83,28 @@ func marshalToJsonString(value interface{}) (*string, error) {
return &sBytes, nil
}
+func marshalWithoutNumber(value interface{}) (*string, error) {
+
+ // The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
+ // This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1
+ // 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)
+ if err != nil {
+ return nil, err
+ }
+
+ var document interface{}
+
+ err = json.Unmarshal([]byte(*jsonString), &document)
+ if err != nil {
+ return nil, err
+ }
+
+ return marshalToJsonString(document)
+}
+
func isJsonNumber(what interface{}) bool {
switch what.(type) {
@@ -78,21 +116,13 @@ func isJsonNumber(what interface{}) bool {
return false
}
-func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool, isValidInt32 bool) {
+func checkJsonInteger(what interface{}) (isInt bool) {
jsonNumber := what.(json.Number)
- f64, errFloat64 := jsonNumber.Float64()
- s64 := strconv.FormatFloat(f64, 'f', -1, 64)
- _, errInt64 := strconv.ParseInt(s64, 10, 64)
-
- isValidFloat64 = errFloat64 == nil
- isValidInt64 = errInt64 == nil
+ bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber))
- _, errInt32 := strconv.ParseInt(s64, 10, 32)
- isValidInt32 = isValidInt64 && errInt32 == nil
-
- return
+ return isValidNumber && bigFloat.IsInt()
}
@@ -117,9 +147,9 @@ func mustBeInteger(what interface{}) *int {
number := what.(json.Number)
- _, _, isValidInt32 := checkJsonNumber(number)
+ isInt := checkJsonInteger(number)
- if isValidInt32 {
+ if isInt {
int64Value, err := number.Int64()
if err != nil {
@@ -138,15 +168,13 @@ func mustBeInteger(what interface{}) *int {
return nil
}
-func mustBeNumber(what interface{}) *float64 {
+func mustBeNumber(what interface{}) *big.Rat {
if isJsonNumber(what) {
-
number := what.(json.Number)
- float64Value, err := number.Float64()
-
- if err == nil {
- return &float64Value
+ float64Value, success := new(big.Rat).SetString(string(number))
+ if success {
+ return float64Value
} else {
return nil
}
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,