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