mirror of
https://github.com/containers/skopeo.git
synced 2025-05-03 21:46:27 +00:00
fix(deps): update module github.com/opencontainers/image-spec to v1.1.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
parent
1de2d3bad9
commit
547141ce57
go.modgo.summodules.txt
vendor
github.com
opencontainers/image-spec
santhosh-tekuri/jsonschema/v5
.gitignore.gitmodulesLICENSEREADME.mdcompiler.gocontent.godoc.godraft.goerrors.goextension.goformat.goloader.gooutput.goresource.goschema.go
xeipuuv
gojsonpointer
gojsonreference
gojsonschema
6
go.mod
6
go.mod
@ -14,7 +14,7 @@ require (
|
|||||||
github.com/docker/distribution v2.8.3+incompatible
|
github.com/docker/distribution v2.8.3+incompatible
|
||||||
github.com/moby/sys/capability v0.4.0
|
github.com/moby/sys/capability v0.4.0
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.1.0
|
github.com/opencontainers/image-spec v1.1.1
|
||||||
github.com/opencontainers/image-tools v1.0.0-rc3
|
github.com/opencontainers/image-tools v1.0.0-rc3
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
@ -102,6 +102,7 @@ require (
|
|||||||
github.com/proglottis/gpgme v0.1.4 // indirect
|
github.com/proglottis/gpgme v0.1.4 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/russross/blackfriday v2.0.0+incompatible // indirect
|
github.com/russross/blackfriday v2.0.0+incompatible // indirect
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
|
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
|
||||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
@ -117,9 +118,6 @@ require (
|
|||||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||||
github.com/vbatts/tar-split v0.11.7 // indirect
|
github.com/vbatts/tar-split v0.11.7 // indirect
|
||||||
github.com/vbauerster/mpb/v8 v8.9.1 // indirect
|
github.com/vbauerster/mpb/v8 v8.9.1 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
|
||||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||||
|
7
go.sum
7
go.sum
@ -243,8 +243,8 @@ github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
|||||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||||
github.com/opencontainers/image-tools v1.0.0-rc3 h1:ZR837lBIxq6mmwEqfYrbLMuf75eBSHhccVHy6lsBeM4=
|
github.com/opencontainers/image-tools v1.0.0-rc3 h1:ZR837lBIxq6mmwEqfYrbLMuf75eBSHhccVHy6lsBeM4=
|
||||||
github.com/opencontainers/image-tools v1.0.0-rc3/go.mod h1:A9btVpZLzttF4iFaKNychhPyrhfOjJ1OF5KrA8GcLj4=
|
github.com/opencontainers/image-tools v1.0.0-rc3/go.mod h1:A9btVpZLzttF4iFaKNychhPyrhfOjJ1OF5KrA8GcLj4=
|
||||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||||
@ -279,6 +279,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
|
|||||||
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
||||||
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
||||||
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
|
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
|
||||||
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
|
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
|
||||||
@ -330,7 +332,6 @@ github.com/vbatts/tar-split v0.11.7 h1:ixZ93pO/GmvaZw4Vq9OwmfZK/kc2zKdPfu0B+gYqs
|
|||||||
github.com/vbatts/tar-split v0.11.7/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
github.com/vbatts/tar-split v0.11.7/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||||
github.com/vbauerster/mpb/v8 v8.9.1 h1:LH5R3lXPfE2e3lIGxN7WNWv3Hl5nWO6LRi2B0L0ERHw=
|
github.com/vbauerster/mpb/v8 v8.9.1 h1:LH5R3lXPfE2e3lIGxN7WNWv3Hl5nWO6LRi2B0L0ERHw=
|
||||||
github.com/vbauerster/mpb/v8 v8.9.1/go.mod h1:4XMvznPh8nfe2NpnDo1QTPvW9MVkUhbG90mPWvmOzcQ=
|
github.com/vbauerster/mpb/v8 v8.9.1/go.mod h1:4XMvznPh8nfe2NpnDo1QTPvW9MVkUhbG90mPWvmOzcQ=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
2
vendor/github.com/opencontainers/image-spec/schema/defs-descriptor.json
generated
vendored
2
vendor/github.com/opencontainers/image-spec/schema/defs-descriptor.json
generated
vendored
@ -4,7 +4,7 @@
|
|||||||
"mediaType": {
|
"mediaType": {
|
||||||
"id": "https://opencontainers.org/schema/image/descriptor/mediaType",
|
"id": "https://opencontainers.org/schema/image/descriptor/mediaType",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}$"
|
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}$"
|
||||||
},
|
},
|
||||||
"digest": {
|
"digest": {
|
||||||
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
|
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
|
||||||
|
4
vendor/github.com/opencontainers/image-spec/schema/error.go
generated
vendored
4
vendor/github.com/opencontainers/image-spec/schema/error.go
generated
vendored
@ -23,6 +23,8 @@ import (
|
|||||||
|
|
||||||
// A SyntaxError is a description of a JSON syntax error
|
// A SyntaxError is a description of a JSON syntax error
|
||||||
// including line, column and offset in the JSON file.
|
// including line, column and offset in the JSON file.
|
||||||
|
//
|
||||||
|
// Deprecated: SyntaxError is no longer returned from Validator.
|
||||||
type SyntaxError struct {
|
type SyntaxError struct {
|
||||||
msg string
|
msg string
|
||||||
Line, Col int
|
Line, Col int
|
||||||
@ -34,6 +36,8 @@ func (e *SyntaxError) Error() string { return e.msg }
|
|||||||
// WrapSyntaxError checks whether the given error is a *json.SyntaxError
|
// WrapSyntaxError checks whether the given error is a *json.SyntaxError
|
||||||
// and converts it into a *schema.SyntaxError containing line/col information using the given reader.
|
// and converts it into a *schema.SyntaxError containing line/col information using the given reader.
|
||||||
// If the given error is not a *json.SyntaxError it is returned unchanged.
|
// If the given error is not a *json.SyntaxError it is returned unchanged.
|
||||||
|
//
|
||||||
|
// Deprecated: WrapSyntaxError is no longer returned by Validator.
|
||||||
func WrapSyntaxError(r io.Reader, err error) error {
|
func WrapSyntaxError(r io.Reader, err error) error {
|
||||||
var serr *json.SyntaxError
|
var serr *json.SyntaxError
|
||||||
if errors.As(err, &serr) {
|
if errors.As(err, &serr) {
|
||||||
|
125
vendor/github.com/opencontainers/image-spec/schema/loader.go
generated
vendored
125
vendor/github.com/opencontainers/image-spec/schema/loader.go
generated
vendored
@ -1,125 +0,0 @@
|
|||||||
// Copyright 2018 The Linux Foundation
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonreference"
|
|
||||||
"github.com/xeipuuv/gojsonschema"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fsLoaderFactory implements gojsonschema.JSONLoaderFactory by reading files under the specified namespaces from the root of fs.
|
|
||||||
type fsLoaderFactory struct {
|
|
||||||
namespaces []string
|
|
||||||
fs http.FileSystem
|
|
||||||
}
|
|
||||||
|
|
||||||
// newFSLoaderFactory returns a fsLoaderFactory reading files under the specified namespaces from the root of fs.
|
|
||||||
func newFSLoaderFactory(namespaces []string, fs http.FileSystem) *fsLoaderFactory {
|
|
||||||
return &fsLoaderFactory{
|
|
||||||
namespaces: namespaces,
|
|
||||||
fs: fs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (factory *fsLoaderFactory) New(source string) gojsonschema.JSONLoader {
|
|
||||||
return &fsLoader{
|
|
||||||
factory: factory,
|
|
||||||
source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// refContents returns the contents of ref, if available in fsLoaderFactory.
|
|
||||||
func (factory *fsLoaderFactory) refContents(ref gojsonreference.JsonReference) ([]byte, error) {
|
|
||||||
refStr := ref.String()
|
|
||||||
path := ""
|
|
||||||
for _, ns := range factory.namespaces {
|
|
||||||
if strings.HasPrefix(refStr, ns) {
|
|
||||||
path = "/" + strings.TrimPrefix(refStr, ns)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if path == "" {
|
|
||||||
return nil, fmt.Errorf("schema reference %#v unexpectedly not available in fsLoaderFactory with namespaces %#v", path, factory.namespaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := factory.fs.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
return io.ReadAll(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fsLoader implements gojsonschema.JSONLoader by reading the document named by source from a fsLoaderFactory.
|
|
||||||
type fsLoader struct {
|
|
||||||
factory *fsLoaderFactory
|
|
||||||
source string
|
|
||||||
}
|
|
||||||
|
|
||||||
// JsonSource implements gojsonschema.JSONLoader.JsonSource. The "Json" capitalization needs to be maintained to conform to the interface.
|
|
||||||
func (l *fsLoader) JsonSource() interface{} { // revive:disable-line:var-naming
|
|
||||||
return l.source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *fsLoader) LoadJSON() (interface{}, error) {
|
|
||||||
// Based on gojsonschema.jsonReferenceLoader.LoadJSON.
|
|
||||||
reference, err := gojsonreference.NewJsonReference(l.source)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
refToURL := reference
|
|
||||||
refToURL.GetUrl().Fragment = ""
|
|
||||||
|
|
||||||
body, err := l.factory.refContents(refToURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeJSONUsingNumber(bytes.NewReader(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeJSONUsingNumber returns JSON parsed from an io.Reader
|
|
||||||
func decodeJSONUsingNumber(r io.Reader) (interface{}, error) {
|
|
||||||
// Copied from gojsonschema.
|
|
||||||
var document interface{}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(r)
|
|
||||||
decoder.UseNumber()
|
|
||||||
|
|
||||||
err := decoder.Decode(&document)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return document, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// JsonReference implements gojsonschema.JSONLoader.JsonReference. The "Json" capitalization needs to be maintained to conform to the interface.
|
|
||||||
func (l *fsLoader) JsonReference() (gojsonreference.JsonReference, error) { // revive:disable-line:var-naming
|
|
||||||
return gojsonreference.NewJsonReference(l.JsonSource().(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *fsLoader) LoaderFactory() gojsonschema.JSONLoaderFactory {
|
|
||||||
return l.factory
|
|
||||||
}
|
|
84
vendor/github.com/opencontainers/image-spec/schema/schema.go
generated
vendored
84
vendor/github.com/opencontainers/image-spec/schema/schema.go
generated
vendored
@ -23,56 +23,62 @@ import (
|
|||||||
|
|
||||||
// Media types for the OCI image formats
|
// Media types for the OCI image formats
|
||||||
const (
|
const (
|
||||||
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
|
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
|
||||||
ValidatorMediaTypeLayoutHeader Validator = v1.MediaTypeLayoutHeader
|
ValidatorMediaTypeLayoutHeader Validator = v1.MediaTypeLayoutHeader
|
||||||
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
|
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
|
||||||
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
|
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
|
||||||
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
|
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
|
||||||
ValidatorMediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer
|
ValidatorMediaTypeImageLayer Validator = v1.MediaTypeImageLayer
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// fs stores the embedded http.FileSystem
|
// specFS stores the embedded http.FileSystem having the OCI JSON schema files in root "/".
|
||||||
// having the OCI JSON schema files in root "/".
|
|
||||||
//go:embed *.json
|
//go:embed *.json
|
||||||
fs embed.FS
|
specFS embed.FS
|
||||||
|
|
||||||
// schemaNamespaces is a set of URI prefixes which are treated as containing the schema files of fs.
|
// specsOrig maps OCI schema media types to schema files.
|
||||||
// This is necessary because *.json schema files in this directory use "id" and "$ref" attributes which evaluate to such URIs, e.g.
|
specs = map[Validator]string{
|
||||||
// ./image-manifest-schema.json URI contains
|
ValidatorMediaTypeDescriptor: "content-descriptor.json",
|
||||||
// "id": "https://opencontainers.org/schema/image/manifest",
|
ValidatorMediaTypeLayoutHeader: "image-layout-schema.json",
|
||||||
// and
|
ValidatorMediaTypeManifest: "image-manifest-schema.json",
|
||||||
// "$ref": "content-descriptor.json"
|
ValidatorMediaTypeImageIndex: "image-index-schema.json",
|
||||||
// which evaluates as a link to https://opencontainers.org/schema/image/content-descriptor.json .
|
ValidatorMediaTypeImageConfig: "config-schema.json",
|
||||||
//
|
|
||||||
// To support such links without accessing the network (and trying to load content which is not hosted at these URIs),
|
|
||||||
// fsLoaderFactory accepts any URI starting with one of the schemaNamespaces below,
|
|
||||||
// and uses _escFS to load them from the root of its in-memory filesystem tree.
|
|
||||||
//
|
|
||||||
// (Note that this must contain subdirectories before its parent directories for fsLoaderFactory.refContents to work.)
|
|
||||||
schemaNamespaces = []string{
|
|
||||||
"https://opencontainers.org/schema/image/descriptor/",
|
|
||||||
"https://opencontainers.org/schema/image/index/",
|
|
||||||
"https://opencontainers.org/schema/image/manifest/",
|
|
||||||
"https://opencontainers.org/schema/image/",
|
|
||||||
"https://opencontainers.org/schema/descriptor/",
|
|
||||||
"https://opencontainers.org/schema/",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// specs maps OCI schema media types to schema URIs.
|
// specURLs lists the various URLs a given spec may be known by.
|
||||||
// These URIs are expected to be used only by fsLoaderFactory (which trims schemaNamespaces defined above)
|
// This is generated from the "id" value in each spec and relative ref values they contain.
|
||||||
// and should never cause a network access.
|
specURLs = map[string][]string{
|
||||||
specs = map[Validator]string{
|
"config-schema.json": {
|
||||||
ValidatorMediaTypeDescriptor: "https://opencontainers.org/schema/content-descriptor.json",
|
"https://opencontainers.org/schema/image/config",
|
||||||
ValidatorMediaTypeLayoutHeader: "https://opencontainers.org/schema/image/image-layout-schema.json",
|
},
|
||||||
ValidatorMediaTypeManifest: "https://opencontainers.org/schema/image/image-manifest-schema.json",
|
"content-descriptor.json": {
|
||||||
ValidatorMediaTypeImageIndex: "https://opencontainers.org/schema/image/image-index-schema.json",
|
"https://opencontainers.org/schema/descriptor",
|
||||||
ValidatorMediaTypeImageConfig: "https://opencontainers.org/schema/image/config-schema.json",
|
"https://opencontainers.org/schema/image/content-descriptor.json",
|
||||||
|
},
|
||||||
|
"defs-descriptor.json": {
|
||||||
|
"https://opencontainers.org/schema/image/descriptor/mediaType",
|
||||||
|
"https://opencontainers.org/schema/defs-descriptor.json",
|
||||||
|
"https://opencontainers.org/schema/image/defs-descriptor.json",
|
||||||
|
},
|
||||||
|
"defs.json": {
|
||||||
|
"https://opencontainers.org/schema/defs.json",
|
||||||
|
"https://opencontainers.org/schema/image/defs.json",
|
||||||
|
"https://opencontainers.org/schema/image/descriptor/defs.json",
|
||||||
|
},
|
||||||
|
"image-index-schema.json": {
|
||||||
|
"https://opencontainers.org/schema/image/index",
|
||||||
|
},
|
||||||
|
"image-layout-schema.json": {
|
||||||
|
"https://opencontainers.org/schema/image/layout",
|
||||||
|
},
|
||||||
|
"image-manifest-schema.json": {
|
||||||
|
"https://opencontainers.org/schema/image/manifest",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileSystem returns an in-memory filesystem including the schema files.
|
// FileSystem returns an in-memory filesystem including the schema files.
|
||||||
// The schema files are located at the root directory.
|
// The schema files are located at the root directory.
|
||||||
func FileSystem() http.FileSystem {
|
func FileSystem() http.FileSystem {
|
||||||
return http.FS(fs)
|
return http.FS(specFS)
|
||||||
}
|
}
|
||||||
|
244
vendor/github.com/opencontainers/image-spec/schema/validator.go
generated
vendored
244
vendor/github.com/opencontainers/image-spec/schema/validator.go
generated
vendored
@ -24,116 +24,128 @@ import (
|
|||||||
|
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/xeipuuv/gojsonschema"
|
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validator wraps a media type string identifier
|
// Validator wraps a media type string identifier and implements validation against a JSON schema.
|
||||||
// and implements validation against a JSON schema.
|
|
||||||
type Validator string
|
type Validator string
|
||||||
|
|
||||||
type validateFunc func(r io.Reader) error
|
|
||||||
|
|
||||||
var mapValidate = map[Validator]validateFunc{
|
|
||||||
ValidatorMediaTypeImageConfig: validateConfig,
|
|
||||||
ValidatorMediaTypeDescriptor: validateDescriptor,
|
|
||||||
ValidatorMediaTypeImageIndex: validateIndex,
|
|
||||||
ValidatorMediaTypeManifest: validateManifest,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidationError contains all the errors that happened during validation.
|
// ValidationError contains all the errors that happened during validation.
|
||||||
|
//
|
||||||
|
// Deprecated: this is no longer used by [Validator].
|
||||||
type ValidationError struct {
|
type ValidationError struct {
|
||||||
Errs []error
|
Errs []error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error returns the error message.
|
||||||
|
//
|
||||||
|
// Deprecated: this is no longer used by [Validator].
|
||||||
func (e ValidationError) Error() string {
|
func (e ValidationError) Error() string {
|
||||||
return fmt.Sprintf("%v", e.Errs)
|
return fmt.Sprintf("%v", e.Errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the given reader against the schema of the wrapped media type.
|
// Validate validates the given reader against the schema of the wrapped media type.
|
||||||
func (v Validator) Validate(src io.Reader) error {
|
func (v Validator) Validate(src io.Reader) error {
|
||||||
buf, err := io.ReadAll(src)
|
// run the media type specific validation
|
||||||
if err != nil {
|
if fn, ok := validateByMediaType[v]; ok {
|
||||||
return fmt.Errorf("unable to read the document file: %w", err)
|
if fn == nil {
|
||||||
}
|
return fmt.Errorf("internal error: mapValidate is nil for %s", string(v))
|
||||||
|
|
||||||
if f, ok := mapValidate[v]; ok {
|
|
||||||
if f == nil {
|
|
||||||
return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
|
|
||||||
}
|
}
|
||||||
err = f(bytes.NewReader(buf))
|
// buffer the src so the media type validation and the schema validation can both read it
|
||||||
|
buf, err := io.ReadAll(src)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read input: %w", err)
|
||||||
|
}
|
||||||
|
src = bytes.NewReader(buf)
|
||||||
|
err = fn(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sl := newFSLoaderFactory(schemaNamespaces, FileSystem()).New(specs[v])
|
// json schema validation
|
||||||
ml := gojsonschema.NewStringLoader(string(buf))
|
return v.validateSchema(src)
|
||||||
|
|
||||||
result, err := gojsonschema.Validate(sl, ml)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("schema %s: unable to validate: %w", v,
|
|
||||||
WrapSyntaxError(bytes.NewReader(buf), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Valid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
errs := make([]error, 0, len(result.Errors()))
|
|
||||||
for _, desc := range result.Errors() {
|
|
||||||
errs = append(errs, fmt.Errorf("%s", desc))
|
|
||||||
}
|
|
||||||
|
|
||||||
return ValidationError{
|
|
||||||
Errs: errs,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type unimplemented string
|
func (v Validator) validateSchema(src io.Reader) error {
|
||||||
|
if _, ok := specs[v]; !ok {
|
||||||
|
return fmt.Errorf("no validator available for %s", string(v))
|
||||||
|
}
|
||||||
|
|
||||||
func (v unimplemented) Validate(_ io.Reader) error {
|
c := jsonschema.NewCompiler()
|
||||||
return fmt.Errorf("%s: unimplemented", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateManifest(r io.Reader) error {
|
// load the schema files from the embedded FS
|
||||||
header := v1.Manifest{}
|
dir, err := specFS.ReadDir(".")
|
||||||
|
|
||||||
buf, err := io.ReadAll(r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading the io stream: %w", err)
|
return fmt.Errorf("spec embedded directory could not be loaded: %w", err)
|
||||||
}
|
}
|
||||||
|
for _, file := range dir {
|
||||||
err = json.Unmarshal(buf, &header)
|
if file.IsDir() {
|
||||||
if err != nil {
|
continue
|
||||||
return fmt.Errorf("manifest format mismatch: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
|
|
||||||
fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, layer := range header.Layers {
|
|
||||||
if layer.MediaType != string(v1.MediaTypeImageLayer) &&
|
|
||||||
layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
|
|
||||||
layer.MediaType != string(v1.MediaTypeImageLayerZstd) &&
|
|
||||||
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) && //nolint:staticcheck
|
|
||||||
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) && //nolint:staticcheck
|
|
||||||
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableZstd) { //nolint:staticcheck
|
|
||||||
fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
|
|
||||||
}
|
}
|
||||||
|
specBuf, err := specFS.ReadFile(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read spec file %s: %w", file.Name(), err)
|
||||||
|
}
|
||||||
|
err = c.AddResource(file.Name(), bytes.NewReader(specBuf))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add spec file %s: %w", file.Name(), err)
|
||||||
|
}
|
||||||
|
if len(specURLs[file.Name()]) == 0 {
|
||||||
|
// this would be a bug in the validation code itself, add any missing entry to schema.go
|
||||||
|
return fmt.Errorf("spec file has no aliases: %s", file.Name())
|
||||||
|
}
|
||||||
|
for _, specURL := range specURLs[file.Name()] {
|
||||||
|
err = c.AddResource(specURL, bytes.NewReader(specBuf))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add spec file %s as url %s: %w", file.Name(), specURL, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile based on the type of validator
|
||||||
|
schema, err := c.Compile(specs[v])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to compile schema %s: %w", string(v), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read in the user input and validate
|
||||||
|
var input interface{}
|
||||||
|
err = json.NewDecoder(src).Decode(&input)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse json to validate: %w", err)
|
||||||
|
}
|
||||||
|
err = schema.Validate(input)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validation failed: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateDescriptor(r io.Reader) error {
|
type validateFunc func([]byte) error
|
||||||
header := v1.Descriptor{}
|
|
||||||
|
|
||||||
buf, err := io.ReadAll(r)
|
var validateByMediaType = map[Validator]validateFunc{
|
||||||
|
ValidatorMediaTypeImageConfig: validateConfig,
|
||||||
|
ValidatorMediaTypeDescriptor: validateDescriptor,
|
||||||
|
ValidatorMediaTypeImageIndex: validateIndex,
|
||||||
|
ValidatorMediaTypeManifest: validateManifest,
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateManifest(buf []byte) error {
|
||||||
|
header := v1.Manifest{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(buf, &header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading the io stream: %w", err)
|
return fmt.Errorf("manifest format mismatch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(buf, &header)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDescriptor(buf []byte) error {
|
||||||
|
header := v1.Descriptor{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(buf, &header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("descriptor format mismatch: %w", err)
|
return fmt.Errorf("descriptor format mismatch: %w", err)
|
||||||
}
|
}
|
||||||
@ -141,55 +153,30 @@ func validateDescriptor(r io.Reader) error {
|
|||||||
err = header.Digest.Validate()
|
err = header.Digest.Validate()
|
||||||
if errors.Is(err, digest.ErrDigestUnsupported) {
|
if errors.Is(err, digest.ErrDigestUnsupported) {
|
||||||
// we ignore unsupported algorithms
|
// we ignore unsupported algorithms
|
||||||
fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateIndex(r io.Reader) error {
|
func validateIndex(buf []byte) error {
|
||||||
header := v1.Index{}
|
header := v1.Index{}
|
||||||
|
|
||||||
buf, err := io.ReadAll(r)
|
err := json.Unmarshal(buf, &header)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading the io stream: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(buf, &header)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("index format mismatch: %w", err)
|
return fmt.Errorf("index format mismatch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, manifest := range header.Manifests {
|
|
||||||
if manifest.MediaType != string(v1.MediaTypeImageManifest) {
|
|
||||||
fmt.Printf("warning: manifest %s has an unknown media type: %s\n", manifest.Digest, manifest.MediaType)
|
|
||||||
}
|
|
||||||
if manifest.Platform != nil {
|
|
||||||
checkPlatform(manifest.Platform.OS, manifest.Platform.Architecture)
|
|
||||||
checkArchitecture(manifest.Platform.Architecture, manifest.Platform.Variant)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateConfig(r io.Reader) error {
|
func validateConfig(buf []byte) error {
|
||||||
header := v1.Image{}
|
header := v1.Image{}
|
||||||
|
|
||||||
buf, err := io.ReadAll(r)
|
err := json.Unmarshal(buf, &header)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading the io stream: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(buf, &header)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("config format mismatch: %w", err)
|
return fmt.Errorf("config format mismatch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPlatform(header.OS, header.Architecture)
|
|
||||||
checkArchitecture(header.Architecture, header.Variant)
|
|
||||||
|
|
||||||
envRegexp := regexp.MustCompile(`^[^=]+=.*$`)
|
envRegexp := regexp.MustCompile(`^[^=]+=.*$`)
|
||||||
for _, e := range header.Config.Env {
|
for _, e := range header.Config.Env {
|
||||||
if !envRegexp.MatchString(e) {
|
if !envRegexp.MatchString(e) {
|
||||||
@ -199,54 +186,3 @@ func validateConfig(r io.Reader) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkArchitecture(Architecture string, Variant string) {
|
|
||||||
validCombins := map[string][]string{
|
|
||||||
"arm": {"", "v6", "v7", "v8"},
|
|
||||||
"arm64": {"", "v8"},
|
|
||||||
"386": {""},
|
|
||||||
"amd64": {""},
|
|
||||||
"ppc64": {""},
|
|
||||||
"ppc64le": {""},
|
|
||||||
"mips64": {""},
|
|
||||||
"mips64le": {""},
|
|
||||||
"s390x": {""},
|
|
||||||
"riscv64": {""},
|
|
||||||
}
|
|
||||||
for arch, variants := range validCombins {
|
|
||||||
if arch == Architecture {
|
|
||||||
for _, variant := range variants {
|
|
||||||
if variant == Variant {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("warning: combination of architecture %q and variant %q is not valid.\n", Architecture, Variant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("warning: architecture %q is not supported yet.\n", Architecture)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPlatform(OS string, Architecture string) {
|
|
||||||
validCombins := map[string][]string{
|
|
||||||
"android": {"arm"},
|
|
||||||
"darwin": {"386", "amd64", "arm", "arm64"},
|
|
||||||
"dragonfly": {"amd64"},
|
|
||||||
"freebsd": {"386", "amd64", "arm"},
|
|
||||||
"linux": {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "riscv64"},
|
|
||||||
"netbsd": {"386", "amd64", "arm"},
|
|
||||||
"openbsd": {"386", "amd64", "arm"},
|
|
||||||
"plan9": {"386", "amd64"},
|
|
||||||
"solaris": {"amd64"},
|
|
||||||
"windows": {"386", "amd64"}}
|
|
||||||
for os, archs := range validCombins {
|
|
||||||
if os == OS {
|
|
||||||
for _, arch := range archs {
|
|
||||||
if arch == Architecture {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("warning: combination of os %q and architecture %q is invalid.\n", OS, Architecture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("warning: operating system %q of the bundle is not supported yet.\n", OS)
|
|
||||||
}
|
|
||||||
|
2
vendor/github.com/opencontainers/image-spec/specs-go/version.go
generated
vendored
2
vendor/github.com/opencontainers/image-spec/specs-go/version.go
generated
vendored
@ -22,7 +22,7 @@ const (
|
|||||||
// VersionMinor is for functionality in a backwards-compatible manner
|
// VersionMinor is for functionality in a backwards-compatible manner
|
||||||
VersionMinor = 1
|
VersionMinor = 1
|
||||||
// VersionPatch is for backwards-compatible bug fixes
|
// VersionPatch is for backwards-compatible bug fixes
|
||||||
VersionPatch = 0
|
VersionPatch = 1
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = ""
|
VersionDev = ""
|
||||||
|
4
vendor/github.com/santhosh-tekuri/jsonschema/v5/.gitignore
generated
vendored
Normal file
4
vendor/github.com/santhosh-tekuri/jsonschema/v5/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
cmd/jv/jv
|
3
vendor/github.com/santhosh-tekuri/jsonschema/v5/.gitmodules
generated
vendored
Normal file
3
vendor/github.com/santhosh-tekuri/jsonschema/v5/.gitmodules
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "testdata/JSON-Schema-Test-Suite"]
|
||||||
|
path = testdata/JSON-Schema-Test-Suite
|
||||||
|
url = https://github.com/json-schema-org/JSON-Schema-Test-Suite.git
|
@ -172,31 +172,4 @@
|
|||||||
of any other Contributor, and only if You agree to indemnify,
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
defend, and hold each Contributor harmless for any liability
|
defend, and hold each Contributor harmless for any liability
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
of your accepting any such warranty or additional liability.
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2015 xeipuuv
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
220
vendor/github.com/santhosh-tekuri/jsonschema/v5/README.md
generated
vendored
Normal file
220
vendor/github.com/santhosh-tekuri/jsonschema/v5/README.md
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
# jsonschema v5.3.1
|
||||||
|
|
||||||
|
[](https://opensource.org/licenses/Apache-2.0)
|
||||||
|
[](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5)
|
||||||
|
[](https://goreportcard.com/report/github.com/santhosh-tekuri/jsonschema/v5)
|
||||||
|
[](https://github.com/santhosh-tekuri/jsonschema/actions/workflows/go.yaml)
|
||||||
|
[](https://codecov.io/gh/santhosh-tekuri/jsonschema)
|
||||||
|
|
||||||
|
Package jsonschema provides json-schema compilation and validation.
|
||||||
|
|
||||||
|
[Benchmarks](https://dev.to/vearutop/benchmarking-correctness-and-performance-of-go-json-schema-validators-3247)
|
||||||
|
|
||||||
|
### Features:
|
||||||
|
- implements
|
||||||
|
[draft 2020-12](https://json-schema.org/specification-links.html#2020-12),
|
||||||
|
[draft 2019-09](https://json-schema.org/specification-links.html#draft-2019-09-formerly-known-as-draft-8),
|
||||||
|
[draft-7](https://json-schema.org/specification-links.html#draft-7),
|
||||||
|
[draft-6](https://json-schema.org/specification-links.html#draft-6),
|
||||||
|
[draft-4](https://json-schema.org/specification-links.html#draft-4)
|
||||||
|
- fully compliant with [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite), (excluding some optional)
|
||||||
|
- list of optional tests that are excluded can be found in schema_test.go(variable [skipTests](https://github.com/santhosh-tekuri/jsonschema/blob/master/schema_test.go#L24))
|
||||||
|
- validates schemas against meta-schema
|
||||||
|
- full support of remote references
|
||||||
|
- support of recursive references between schemas
|
||||||
|
- detects infinite loop in schemas
|
||||||
|
- thread safe validation
|
||||||
|
- rich, intuitive hierarchial error messages with json-pointers to exact location
|
||||||
|
- supports output formats flag, basic and detailed
|
||||||
|
- supports enabling format and content Assertions in draft2019-09 or above
|
||||||
|
- change `Compiler.AssertFormat`, `Compiler.AssertContent` to `true`
|
||||||
|
- compiled schema can be introspected. easier to develop tools like generating go structs given schema
|
||||||
|
- supports user-defined keywords via [extensions](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5/#example-package-Extension)
|
||||||
|
- implements following formats (supports [user-defined](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5/#example-package-UserDefinedFormat))
|
||||||
|
- date-time, date, time, duration, period (supports leap-second)
|
||||||
|
- uuid, hostname, email
|
||||||
|
- ip-address, ipv4, ipv6
|
||||||
|
- uri, uriref, uri-template(limited validation)
|
||||||
|
- json-pointer, relative-json-pointer
|
||||||
|
- regex, format
|
||||||
|
- implements following contentEncoding (supports [user-defined](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5/#example-package-UserDefinedContent))
|
||||||
|
- base64
|
||||||
|
- implements following contentMediaType (supports [user-defined](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5/#example-package-UserDefinedContent))
|
||||||
|
- application/json
|
||||||
|
- can load from files/http/https/[string](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5/#example-package-FromString)/[]byte/io.Reader (supports [user-defined](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5/#example-package-UserDefinedLoader))
|
||||||
|
|
||||||
|
|
||||||
|
see examples in [godoc](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5)
|
||||||
|
|
||||||
|
The schema is compiled against the version specified in `$schema` property.
|
||||||
|
If "$schema" property is missing, it uses latest draft which currently implemented
|
||||||
|
by this library.
|
||||||
|
|
||||||
|
You can force to use specific version, when `$schema` is missing, as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
compiler := jsonschema.NewCompiler()
|
||||||
|
compiler.Draft = jsonschema.Draft4
|
||||||
|
```
|
||||||
|
|
||||||
|
This package supports loading json-schema from filePath and fileURL.
|
||||||
|
|
||||||
|
To load json-schema from HTTPURL, add following import:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import _ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rich Errors
|
||||||
|
|
||||||
|
The ValidationError returned by Validate method contains detailed context to understand why and where the error is.
|
||||||
|
|
||||||
|
schema.json:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$ref": "t.json#/definitions/employee"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
t.json:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"definitions": {
|
||||||
|
"employee": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
doc.json:
|
||||||
|
```json
|
||||||
|
1
|
||||||
|
```
|
||||||
|
|
||||||
|
assuming `err` is the ValidationError returned when `doc.json` validated with `schema.json`,
|
||||||
|
```go
|
||||||
|
fmt.Printf("%#v\n", err) // using %#v prints errors hierarchy
|
||||||
|
```
|
||||||
|
Prints:
|
||||||
|
```
|
||||||
|
[I#] [S#] doesn't validate with file:///Users/santhosh/jsonschema/schema.json#
|
||||||
|
[I#] [S#/$ref] doesn't validate with 'file:///Users/santhosh/jsonschema/t.json#/definitions/employee'
|
||||||
|
[I#] [S#/definitions/employee/type] expected string, but got number
|
||||||
|
```
|
||||||
|
|
||||||
|
Here `I` stands for instance document and `S` stands for schema document.
|
||||||
|
The json-fragments that caused error in instance and schema documents are represented using json-pointer notation.
|
||||||
|
Nested causes are printed with indent.
|
||||||
|
|
||||||
|
To output `err` in `flag` output format:
|
||||||
|
```go
|
||||||
|
b, _ := json.MarshalIndent(err.FlagOutput(), "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
```
|
||||||
|
Prints:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"valid": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
To output `err` in `basic` output format:
|
||||||
|
```go
|
||||||
|
b, _ := json.MarshalIndent(err.BasicOutput(), "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
```
|
||||||
|
Prints:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"valid": false,
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"keywordLocation": "",
|
||||||
|
"absoluteKeywordLocation": "file:///Users/santhosh/jsonschema/schema.json#",
|
||||||
|
"instanceLocation": "",
|
||||||
|
"error": "doesn't validate with file:///Users/santhosh/jsonschema/schema.json#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keywordLocation": "/$ref",
|
||||||
|
"absoluteKeywordLocation": "file:///Users/santhosh/jsonschema/schema.json#/$ref",
|
||||||
|
"instanceLocation": "",
|
||||||
|
"error": "doesn't validate with 'file:///Users/santhosh/jsonschema/t.json#/definitions/employee'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keywordLocation": "/$ref/type",
|
||||||
|
"absoluteKeywordLocation": "file:///Users/santhosh/jsonschema/t.json#/definitions/employee/type",
|
||||||
|
"instanceLocation": "",
|
||||||
|
"error": "expected string, but got number"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
To output `err` in `detailed` output format:
|
||||||
|
```go
|
||||||
|
b, _ := json.MarshalIndent(err.DetailedOutput(), "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
```
|
||||||
|
Prints:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"valid": false,
|
||||||
|
"keywordLocation": "",
|
||||||
|
"absoluteKeywordLocation": "file:///Users/santhosh/jsonschema/schema.json#",
|
||||||
|
"instanceLocation": "",
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"valid": false,
|
||||||
|
"keywordLocation": "/$ref",
|
||||||
|
"absoluteKeywordLocation": "file:///Users/santhosh/jsonschema/schema.json#/$ref",
|
||||||
|
"instanceLocation": "",
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"valid": false,
|
||||||
|
"keywordLocation": "/$ref/type",
|
||||||
|
"absoluteKeywordLocation": "file:///Users/santhosh/jsonschema/t.json#/definitions/employee/type",
|
||||||
|
"instanceLocation": "",
|
||||||
|
"error": "expected string, but got number"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI
|
||||||
|
|
||||||
|
to install `go install github.com/santhosh-tekuri/jsonschema/cmd/jv@latest`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jv [-draft INT] [-output FORMAT] [-assertformat] [-assertcontent] <json-schema> [<json-or-yaml-doc>]...
|
||||||
|
-assertcontent
|
||||||
|
enable content assertions with draft >= 2019
|
||||||
|
-assertformat
|
||||||
|
enable format assertions with draft >= 2019
|
||||||
|
-draft int
|
||||||
|
draft used when '$schema' attribute is missing. valid values 4, 5, 7, 2019, 2020 (default 2020)
|
||||||
|
-output string
|
||||||
|
output format. valid values flag, basic, detailed
|
||||||
|
```
|
||||||
|
|
||||||
|
if no `<json-or-yaml-doc>` arguments are passed, it simply validates the `<json-schema>`.
|
||||||
|
if `$schema` attribute is missing in schema, it uses latest version. this can be overridden by passing `-draft` flag
|
||||||
|
|
||||||
|
exit-code is 1, if there are any validation errors
|
||||||
|
|
||||||
|
`jv` can also validate yaml files. It also accepts schema from yaml files.
|
||||||
|
|
||||||
|
## Validating YAML Documents
|
||||||
|
|
||||||
|
since yaml supports non-string keys, such yaml documents are rendered as invalid json documents.
|
||||||
|
|
||||||
|
most yaml parser use `map[interface{}]interface{}` for object,
|
||||||
|
whereas json parser uses `map[string]interface{}`.
|
||||||
|
|
||||||
|
so we need to manually convert them to `map[string]interface{}`.
|
||||||
|
below code shows such conversion by `toStringKeys` function.
|
||||||
|
|
||||||
|
https://play.golang.org/p/Hhax3MrtD8r
|
||||||
|
|
||||||
|
NOTE: if you are using `gopkg.in/yaml.v3`, then you do not need such conversion. since this library
|
||||||
|
returns `map[string]interface{}` if all keys are strings.
|
812
vendor/github.com/santhosh-tekuri/jsonschema/v5/compiler.go
generated
vendored
Normal file
812
vendor/github.com/santhosh-tekuri/jsonschema/v5/compiler.go
generated
vendored
Normal file
@ -0,0 +1,812 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Compiler represents a json-schema compiler.
|
||||||
|
type Compiler struct {
|
||||||
|
// Draft represents the draft used when '$schema' attribute is missing.
|
||||||
|
//
|
||||||
|
// This defaults to latest supported draft (currently 2020-12).
|
||||||
|
Draft *Draft
|
||||||
|
resources map[string]*resource
|
||||||
|
|
||||||
|
// Extensions is used to register extensions.
|
||||||
|
extensions map[string]extension
|
||||||
|
|
||||||
|
// ExtractAnnotations tells whether schema annotations has to be extracted
|
||||||
|
// in compiled Schema or not.
|
||||||
|
ExtractAnnotations bool
|
||||||
|
|
||||||
|
// LoadURL loads the document at given absolute URL.
|
||||||
|
//
|
||||||
|
// If nil, package global LoadURL is used.
|
||||||
|
LoadURL func(s string) (io.ReadCloser, error)
|
||||||
|
|
||||||
|
// Formats can be registered by adding to this map. Key is format name,
|
||||||
|
// value is function that knows how to validate that format.
|
||||||
|
Formats map[string]func(interface{}) bool
|
||||||
|
|
||||||
|
// AssertFormat for specifications >= draft2019-09.
|
||||||
|
AssertFormat bool
|
||||||
|
|
||||||
|
// Decoders can be registered by adding to this map. Key is encoding name,
|
||||||
|
// value is function that knows how to decode string in that format.
|
||||||
|
Decoders map[string]func(string) ([]byte, error)
|
||||||
|
|
||||||
|
// MediaTypes can be registered by adding to this map. Key is mediaType name,
|
||||||
|
// value is function that knows how to validate that mediaType.
|
||||||
|
MediaTypes map[string]func([]byte) error
|
||||||
|
|
||||||
|
// AssertContent for specifications >= draft2019-09.
|
||||||
|
AssertContent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile parses json-schema at given url returns, if successful,
|
||||||
|
// a Schema object that can be used to match against json.
|
||||||
|
//
|
||||||
|
// Returned error can be *SchemaError
|
||||||
|
func Compile(url string) (*Schema, error) {
|
||||||
|
return NewCompiler().Compile(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCompile is like Compile but panics if the url cannot be compiled to *Schema.
|
||||||
|
// It simplifies safe initialization of global variables holding compiled Schemas.
|
||||||
|
func MustCompile(url string) *Schema {
|
||||||
|
return NewCompiler().MustCompile(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompileString parses and compiles the given schema with given base url.
|
||||||
|
func CompileString(url, schema string) (*Schema, error) {
|
||||||
|
c := NewCompiler()
|
||||||
|
if err := c.AddResource(url, strings.NewReader(schema)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Compile(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCompileString is like CompileString but panics on error.
|
||||||
|
// It simplified safe initialization of global variables holding compiled Schema.
|
||||||
|
func MustCompileString(url, schema string) *Schema {
|
||||||
|
c := NewCompiler()
|
||||||
|
if err := c.AddResource(url, strings.NewReader(schema)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.MustCompile(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCompiler returns a json-schema Compiler object.
|
||||||
|
// if '$schema' attribute is missing, it is treated as draft7. to change this
|
||||||
|
// behavior change Compiler.Draft value
|
||||||
|
func NewCompiler() *Compiler {
|
||||||
|
return &Compiler{
|
||||||
|
Draft: latest,
|
||||||
|
resources: make(map[string]*resource),
|
||||||
|
Formats: make(map[string]func(interface{}) bool),
|
||||||
|
Decoders: make(map[string]func(string) ([]byte, error)),
|
||||||
|
MediaTypes: make(map[string]func([]byte) error),
|
||||||
|
extensions: make(map[string]extension),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddResource adds in-memory resource to the compiler.
|
||||||
|
//
|
||||||
|
// Note that url must not have fragment
|
||||||
|
func (c *Compiler) AddResource(url string, r io.Reader) error {
|
||||||
|
res, err := newResource(url, r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.resources[res.url] = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCompile is like Compile but panics if the url cannot be compiled to *Schema.
|
||||||
|
// It simplifies safe initialization of global variables holding compiled Schemas.
|
||||||
|
func (c *Compiler) MustCompile(url string) *Schema {
|
||||||
|
s, err := c.Compile(url)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("jsonschema: %#v", err))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile parses json-schema at given url returns, if successful,
|
||||||
|
// a Schema object that can be used to match against json.
|
||||||
|
//
|
||||||
|
// error returned will be of type *SchemaError
|
||||||
|
func (c *Compiler) Compile(url string) (*Schema, error) {
|
||||||
|
// make url absolute
|
||||||
|
u, err := toAbs(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &SchemaError{url, err}
|
||||||
|
}
|
||||||
|
url = u
|
||||||
|
|
||||||
|
sch, err := c.compileURL(url, nil, "#")
|
||||||
|
if err != nil {
|
||||||
|
err = &SchemaError{url, err}
|
||||||
|
}
|
||||||
|
return sch, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) findResource(url string) (*resource, error) {
|
||||||
|
if _, ok := c.resources[url]; !ok {
|
||||||
|
// load resource
|
||||||
|
var rdr io.Reader
|
||||||
|
if sch, ok := vocabSchemas[url]; ok {
|
||||||
|
rdr = strings.NewReader(sch)
|
||||||
|
} else {
|
||||||
|
loadURL := LoadURL
|
||||||
|
if c.LoadURL != nil {
|
||||||
|
loadURL = c.LoadURL
|
||||||
|
}
|
||||||
|
r, err := loadURL(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
rdr = r
|
||||||
|
}
|
||||||
|
if err := c.AddResource(url, rdr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := c.resources[url]
|
||||||
|
if r.draft != nil {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// set draft
|
||||||
|
r.draft = c.Draft
|
||||||
|
if m, ok := r.doc.(map[string]interface{}); ok {
|
||||||
|
if sch, ok := m["$schema"]; ok {
|
||||||
|
sch, ok := sch.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("jsonschema: invalid $schema in %s", url)
|
||||||
|
}
|
||||||
|
if !isURI(sch) {
|
||||||
|
return nil, fmt.Errorf("jsonschema: $schema must be uri in %s", url)
|
||||||
|
}
|
||||||
|
r.draft = findDraft(sch)
|
||||||
|
if r.draft == nil {
|
||||||
|
sch, _ := split(sch)
|
||||||
|
if sch == url {
|
||||||
|
return nil, fmt.Errorf("jsonschema: unsupported draft in %s", url)
|
||||||
|
}
|
||||||
|
mr, err := c.findResource(sch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.draft = mr.draft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := r.draft.resolveID(r.url, r.doc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if id != "" {
|
||||||
|
r.url = id
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.fillSubschemas(c, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compileURL(url string, stack []schemaRef, ptr string) (*Schema, error) {
|
||||||
|
// if url points to a draft, return Draft.meta
|
||||||
|
if d := findDraft(url); d != nil && d.meta != nil {
|
||||||
|
return d.meta, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b, f := split(url)
|
||||||
|
r, err := c.findResource(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.compileRef(r, stack, ptr, r, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compileRef(r *resource, stack []schemaRef, refPtr string, res *resource, ref string) (*Schema, error) {
|
||||||
|
base := r.baseURL(res.floc)
|
||||||
|
ref, err := resolveURL(base, ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, f := split(ref)
|
||||||
|
sr := r.findResource(u)
|
||||||
|
if sr == nil {
|
||||||
|
// external resource
|
||||||
|
return c.compileURL(ref, stack, refPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure root resource is always compiled first.
|
||||||
|
// this is required to get schema.meta from root resource
|
||||||
|
if r.schema == nil {
|
||||||
|
r.schema = newSchema(r.url, r.floc, r.draft, r.doc)
|
||||||
|
if _, err := c.compile(r, nil, schemaRef{"#", r.schema, false}, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sr, err = r.resolveFragment(c, sr, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sr == nil {
|
||||||
|
return nil, fmt.Errorf("jsonschema: %s not found", ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.schema != nil {
|
||||||
|
if err := checkLoop(stack, schemaRef{refPtr, sr.schema, false}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sr.schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sr.schema = newSchema(r.url, sr.floc, r.draft, sr.doc)
|
||||||
|
return c.compile(r, stack, schemaRef{refPtr, sr.schema, false}, sr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compileDynamicAnchors(r *resource, res *resource) error {
|
||||||
|
if r.draft.version < 2020 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rr := r.listResources(res)
|
||||||
|
rr = append(rr, res)
|
||||||
|
for _, sr := range rr {
|
||||||
|
if m, ok := sr.doc.(map[string]interface{}); ok {
|
||||||
|
if _, ok := m["$dynamicAnchor"]; ok {
|
||||||
|
sch, err := c.compileRef(r, nil, "IGNORED", r, sr.floc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.schema.dynamicAnchors = append(res.schema.dynamicAnchors, sch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compile(r *resource, stack []schemaRef, sref schemaRef, res *resource) (*Schema, error) {
|
||||||
|
if err := c.compileDynamicAnchors(r, res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := res.doc.(type) {
|
||||||
|
case bool:
|
||||||
|
res.schema.Always = &v
|
||||||
|
return res.schema, nil
|
||||||
|
default:
|
||||||
|
return res.schema, c.compileMap(r, stack, sref, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compileMap(r *resource, stack []schemaRef, sref schemaRef, res *resource) error {
|
||||||
|
m := res.doc.(map[string]interface{})
|
||||||
|
|
||||||
|
if err := checkLoop(stack, sref); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stack = append(stack, sref)
|
||||||
|
|
||||||
|
var s = res.schema
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if r == res { // root schema
|
||||||
|
if sch, ok := m["$schema"]; ok {
|
||||||
|
sch := sch.(string)
|
||||||
|
if d := findDraft(sch); d != nil {
|
||||||
|
s.meta = d.meta
|
||||||
|
} else {
|
||||||
|
if s.meta, err = c.compileRef(r, stack, "$schema", res, sch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ref, ok := m["$ref"]; ok {
|
||||||
|
s.Ref, err = c.compileRef(r, stack, "$ref", res, ref.(string))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.draft.version < 2019 {
|
||||||
|
// All other properties in a "$ref" object MUST be ignored
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 2019 {
|
||||||
|
if r == res { // root schema
|
||||||
|
if vocab, ok := m["$vocabulary"]; ok {
|
||||||
|
for url, reqd := range vocab.(map[string]interface{}) {
|
||||||
|
if reqd, ok := reqd.(bool); ok && !reqd {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !r.draft.isVocab(url) {
|
||||||
|
return fmt.Errorf("jsonschema: unsupported vocab %q in %s", url, res)
|
||||||
|
}
|
||||||
|
s.vocab = append(s.vocab, url)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.vocab = r.draft.defaultVocab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ref, ok := m["$recursiveRef"]; ok {
|
||||||
|
s.RecursiveRef, err = c.compileRef(r, stack, "$recursiveRef", res, ref.(string))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.draft.version >= 2020 {
|
||||||
|
if dref, ok := m["$dynamicRef"]; ok {
|
||||||
|
s.DynamicRef, err = c.compileRef(r, stack, "$dynamicRef", res, dref.(string))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dref, ok := dref.(string); ok {
|
||||||
|
_, frag := split(dref)
|
||||||
|
if frag != "#" && !strings.HasPrefix(frag, "#/") {
|
||||||
|
// frag is anchor
|
||||||
|
s.dynamicRefAnchor = frag[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadInt := func(pname string) int {
|
||||||
|
if num, ok := m[pname]; ok {
|
||||||
|
i, _ := num.(json.Number).Float64()
|
||||||
|
return int(i)
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRat := func(pname string) *big.Rat {
|
||||||
|
if num, ok := m[pname]; ok {
|
||||||
|
r, _ := new(big.Rat).SetString(string(num.(json.Number)))
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version < 2019 || r.schema.meta.hasVocab("validation") {
|
||||||
|
if t, ok := m["type"]; ok {
|
||||||
|
switch t := t.(type) {
|
||||||
|
case string:
|
||||||
|
s.Types = []string{t}
|
||||||
|
case []interface{}:
|
||||||
|
s.Types = toStrings(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := m["enum"]; ok {
|
||||||
|
s.Enum = e.([]interface{})
|
||||||
|
allPrimitives := true
|
||||||
|
for _, item := range s.Enum {
|
||||||
|
switch jsonType(item) {
|
||||||
|
case "object", "array":
|
||||||
|
allPrimitives = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.enumError = "enum failed"
|
||||||
|
if allPrimitives {
|
||||||
|
if len(s.Enum) == 1 {
|
||||||
|
s.enumError = fmt.Sprintf("value must be %#v", s.Enum[0])
|
||||||
|
} else {
|
||||||
|
strEnum := make([]string, len(s.Enum))
|
||||||
|
for i, item := range s.Enum {
|
||||||
|
strEnum[i] = fmt.Sprintf("%#v", item)
|
||||||
|
}
|
||||||
|
s.enumError = fmt.Sprintf("value must be one of %s", strings.Join(strEnum, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Minimum = loadRat("minimum")
|
||||||
|
if exclusive, ok := m["exclusiveMinimum"]; ok {
|
||||||
|
if exclusive, ok := exclusive.(bool); ok {
|
||||||
|
if exclusive {
|
||||||
|
s.Minimum, s.ExclusiveMinimum = nil, s.Minimum
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.ExclusiveMinimum = loadRat("exclusiveMinimum")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Maximum = loadRat("maximum")
|
||||||
|
if exclusive, ok := m["exclusiveMaximum"]; ok {
|
||||||
|
if exclusive, ok := exclusive.(bool); ok {
|
||||||
|
if exclusive {
|
||||||
|
s.Maximum, s.ExclusiveMaximum = nil, s.Maximum
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.ExclusiveMaximum = loadRat("exclusiveMaximum")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.MultipleOf = loadRat("multipleOf")
|
||||||
|
|
||||||
|
s.MinProperties, s.MaxProperties = loadInt("minProperties"), loadInt("maxProperties")
|
||||||
|
|
||||||
|
if req, ok := m["required"]; ok {
|
||||||
|
s.Required = toStrings(req.([]interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.MinItems, s.MaxItems = loadInt("minItems"), loadInt("maxItems")
|
||||||
|
|
||||||
|
if unique, ok := m["uniqueItems"]; ok {
|
||||||
|
s.UniqueItems = unique.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.MinLength, s.MaxLength = loadInt("minLength"), loadInt("maxLength")
|
||||||
|
|
||||||
|
if pattern, ok := m["pattern"]; ok {
|
||||||
|
s.Pattern = regexp.MustCompile(pattern.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 2019 {
|
||||||
|
s.MinContains, s.MaxContains = loadInt("minContains"), loadInt("maxContains")
|
||||||
|
if s.MinContains == -1 {
|
||||||
|
s.MinContains = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if deps, ok := m["dependentRequired"]; ok {
|
||||||
|
deps := deps.(map[string]interface{})
|
||||||
|
s.DependentRequired = make(map[string][]string, len(deps))
|
||||||
|
for pname, pvalue := range deps {
|
||||||
|
s.DependentRequired[pname] = toStrings(pvalue.([]interface{}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compile := func(stack []schemaRef, ptr string) (*Schema, error) {
|
||||||
|
return c.compileRef(r, stack, ptr, res, r.url+res.floc+"/"+ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSchema := func(pname string, stack []schemaRef) (*Schema, error) {
|
||||||
|
if _, ok := m[pname]; ok {
|
||||||
|
return compile(stack, escape(pname))
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSchemas := func(pname string, stack []schemaRef) ([]*Schema, error) {
|
||||||
|
if pvalue, ok := m[pname]; ok {
|
||||||
|
pvalue := pvalue.([]interface{})
|
||||||
|
schemas := make([]*Schema, len(pvalue))
|
||||||
|
for i := range pvalue {
|
||||||
|
sch, err := compile(stack, escape(pname)+"/"+strconv.Itoa(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
schemas[i] = sch
|
||||||
|
}
|
||||||
|
return schemas, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version < 2019 || r.schema.meta.hasVocab("applicator") {
|
||||||
|
if s.Not, err = loadSchema("not", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.AllOf, err = loadSchemas("allOf", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.AnyOf, err = loadSchemas("anyOf", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.OneOf, err = loadSchemas("oneOf", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if props, ok := m["properties"]; ok {
|
||||||
|
props := props.(map[string]interface{})
|
||||||
|
s.Properties = make(map[string]*Schema, len(props))
|
||||||
|
for pname := range props {
|
||||||
|
s.Properties[pname], err = compile(nil, "properties/"+escape(pname))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if regexProps, ok := m["regexProperties"]; ok {
|
||||||
|
s.RegexProperties = regexProps.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if patternProps, ok := m["patternProperties"]; ok {
|
||||||
|
patternProps := patternProps.(map[string]interface{})
|
||||||
|
s.PatternProperties = make(map[*regexp.Regexp]*Schema, len(patternProps))
|
||||||
|
for pattern := range patternProps {
|
||||||
|
s.PatternProperties[regexp.MustCompile(pattern)], err = compile(nil, "patternProperties/"+escape(pattern))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if additionalProps, ok := m["additionalProperties"]; ok {
|
||||||
|
switch additionalProps := additionalProps.(type) {
|
||||||
|
case bool:
|
||||||
|
s.AdditionalProperties = additionalProps
|
||||||
|
case map[string]interface{}:
|
||||||
|
s.AdditionalProperties, err = compile(nil, "additionalProperties")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if deps, ok := m["dependencies"]; ok {
|
||||||
|
deps := deps.(map[string]interface{})
|
||||||
|
s.Dependencies = make(map[string]interface{}, len(deps))
|
||||||
|
for pname, pvalue := range deps {
|
||||||
|
switch pvalue := pvalue.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
s.Dependencies[pname] = toStrings(pvalue)
|
||||||
|
default:
|
||||||
|
s.Dependencies[pname], err = compile(stack, "dependencies/"+escape(pname))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 6 {
|
||||||
|
if s.PropertyNames, err = loadSchema("propertyNames", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.Contains, err = loadSchema("contains", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 7 {
|
||||||
|
if m["if"] != nil {
|
||||||
|
if s.If, err = loadSchema("if", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.Then, err = loadSchema("then", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.Else, err = loadSchema("else", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.draft.version >= 2019 {
|
||||||
|
if deps, ok := m["dependentSchemas"]; ok {
|
||||||
|
deps := deps.(map[string]interface{})
|
||||||
|
s.DependentSchemas = make(map[string]*Schema, len(deps))
|
||||||
|
for pname := range deps {
|
||||||
|
s.DependentSchemas[pname], err = compile(stack, "dependentSchemas/"+escape(pname))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 2020 {
|
||||||
|
if s.PrefixItems, err = loadSchemas("prefixItems", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.Items2020, err = loadSchema("items", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if items, ok := m["items"]; ok {
|
||||||
|
switch items.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
s.Items, err = loadSchemas("items", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if additionalItems, ok := m["additionalItems"]; ok {
|
||||||
|
switch additionalItems := additionalItems.(type) {
|
||||||
|
case bool:
|
||||||
|
s.AdditionalItems = additionalItems
|
||||||
|
case map[string]interface{}:
|
||||||
|
s.AdditionalItems, err = compile(nil, "additionalItems")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
s.Items, err = compile(nil, "items")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// unevaluatedXXX keywords were in "applicator" vocab in 2019, but moved to new vocab "unevaluated" in 2020
|
||||||
|
if (r.draft.version == 2019 && r.schema.meta.hasVocab("applicator")) || (r.draft.version >= 2020 && r.schema.meta.hasVocab("unevaluated")) {
|
||||||
|
if s.UnevaluatedProperties, err = loadSchema("unevaluatedProperties", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.UnevaluatedItems, err = loadSchema("unevaluatedItems", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.draft.version >= 2020 {
|
||||||
|
// any item in an array that passes validation of the contains schema is considered "evaluated"
|
||||||
|
s.ContainsEval = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if format, ok := m["format"]; ok {
|
||||||
|
s.Format = format.(string)
|
||||||
|
if r.draft.version < 2019 || c.AssertFormat || r.schema.meta.hasVocab("format-assertion") {
|
||||||
|
if format, ok := c.Formats[s.Format]; ok {
|
||||||
|
s.format = format
|
||||||
|
} else {
|
||||||
|
s.format, _ = Formats[s.Format]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ExtractAnnotations {
|
||||||
|
if title, ok := m["title"]; ok {
|
||||||
|
s.Title = title.(string)
|
||||||
|
}
|
||||||
|
if description, ok := m["description"]; ok {
|
||||||
|
s.Description = description.(string)
|
||||||
|
}
|
||||||
|
s.Default = m["default"]
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 6 {
|
||||||
|
if c, ok := m["const"]; ok {
|
||||||
|
s.Constant = []interface{}{c}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 7 {
|
||||||
|
if encoding, ok := m["contentEncoding"]; ok {
|
||||||
|
s.ContentEncoding = encoding.(string)
|
||||||
|
if decoder, ok := c.Decoders[s.ContentEncoding]; ok {
|
||||||
|
s.decoder = decoder
|
||||||
|
} else {
|
||||||
|
s.decoder, _ = Decoders[s.ContentEncoding]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mediaType, ok := m["contentMediaType"]; ok {
|
||||||
|
s.ContentMediaType = mediaType.(string)
|
||||||
|
if mediaType, ok := c.MediaTypes[s.ContentMediaType]; ok {
|
||||||
|
s.mediaType = mediaType
|
||||||
|
} else {
|
||||||
|
s.mediaType, _ = MediaTypes[s.ContentMediaType]
|
||||||
|
}
|
||||||
|
if s.ContentSchema, err = loadSchema("contentSchema", stack); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.ExtractAnnotations {
|
||||||
|
if comment, ok := m["$comment"]; ok {
|
||||||
|
s.Comment = comment.(string)
|
||||||
|
}
|
||||||
|
if readOnly, ok := m["readOnly"]; ok {
|
||||||
|
s.ReadOnly = readOnly.(bool)
|
||||||
|
}
|
||||||
|
if writeOnly, ok := m["writeOnly"]; ok {
|
||||||
|
s.WriteOnly = writeOnly.(bool)
|
||||||
|
}
|
||||||
|
if examples, ok := m["examples"]; ok {
|
||||||
|
s.Examples = examples.([]interface{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.draft.version >= 2019 {
|
||||||
|
if !c.AssertContent {
|
||||||
|
s.decoder = nil
|
||||||
|
s.mediaType = nil
|
||||||
|
s.ContentSchema = nil
|
||||||
|
}
|
||||||
|
if c.ExtractAnnotations {
|
||||||
|
if deprecated, ok := m["deprecated"]; ok {
|
||||||
|
s.Deprecated = deprecated.(bool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, ext := range c.extensions {
|
||||||
|
es, err := ext.compiler.Compile(CompilerContext{c, r, stack, res}, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if es != nil {
|
||||||
|
if s.Extensions == nil {
|
||||||
|
s.Extensions = make(map[string]ExtSchema)
|
||||||
|
}
|
||||||
|
s.Extensions[name] = es
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) validateSchema(r *resource, v interface{}, vloc string) error {
|
||||||
|
validate := func(meta *Schema) error {
|
||||||
|
if meta == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return meta.validateValue(v, vloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validate(r.draft.meta); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, ext := range c.extensions {
|
||||||
|
if err := validate(ext.meta); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toStrings(arr []interface{}) []string {
|
||||||
|
s := make([]string, len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
s[i] = v.(string)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaRef captures schema and the path referring to it.
|
||||||
|
type schemaRef struct {
|
||||||
|
path string // relative-json-pointer to schema
|
||||||
|
schema *Schema // target schema
|
||||||
|
discard bool // true when scope left
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr schemaRef) String() string {
|
||||||
|
return fmt.Sprintf("(%s)%v", sr.path, sr.schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLoop(stack []schemaRef, sref schemaRef) error {
|
||||||
|
for _, ref := range stack {
|
||||||
|
if ref.schema == sref.schema {
|
||||||
|
return infiniteLoopError(stack, sref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func keywordLocation(stack []schemaRef, path string) string {
|
||||||
|
var loc string
|
||||||
|
for _, ref := range stack[1:] {
|
||||||
|
loc += "/" + ref.path
|
||||||
|
}
|
||||||
|
if path != "" {
|
||||||
|
loc = loc + "/" + path
|
||||||
|
}
|
||||||
|
return loc
|
||||||
|
}
|
29
vendor/github.com/santhosh-tekuri/jsonschema/v5/content.go
generated
vendored
Normal file
29
vendor/github.com/santhosh-tekuri/jsonschema/v5/content.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decoders is a registry of functions, which know how to decode
|
||||||
|
// string encoded in specific format.
|
||||||
|
//
|
||||||
|
// New Decoders can be registered by adding to this map. Key is encoding name,
|
||||||
|
// value is function that knows how to decode string in that format.
|
||||||
|
var Decoders = map[string]func(string) ([]byte, error){
|
||||||
|
"base64": base64.StdEncoding.DecodeString,
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaTypes is a registry of functions, which know how to validate
|
||||||
|
// whether the bytes represent data of that mediaType.
|
||||||
|
//
|
||||||
|
// New mediaTypes can be registered by adding to this map. Key is mediaType name,
|
||||||
|
// value is function that knows how to validate that mediaType.
|
||||||
|
var MediaTypes = map[string]func([]byte) error{
|
||||||
|
"application/json": validateJSON,
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateJSON(b []byte) error {
|
||||||
|
var v interface{}
|
||||||
|
return json.Unmarshal(b, &v)
|
||||||
|
}
|
49
vendor/github.com/santhosh-tekuri/jsonschema/v5/doc.go
generated
vendored
Normal file
49
vendor/github.com/santhosh-tekuri/jsonschema/v5/doc.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
Package jsonschema provides json-schema compilation and validation.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- implements draft 2020-12, 2019-09, draft-7, draft-6, draft-4
|
||||||
|
- fully compliant with JSON-Schema-Test-Suite, (excluding some optional)
|
||||||
|
- list of optional tests that are excluded can be found in schema_test.go(variable skipTests)
|
||||||
|
- validates schemas against meta-schema
|
||||||
|
- full support of remote references
|
||||||
|
- support of recursive references between schemas
|
||||||
|
- detects infinite loop in schemas
|
||||||
|
- thread safe validation
|
||||||
|
- rich, intuitive hierarchial error messages with json-pointers to exact location
|
||||||
|
- supports output formats flag, basic and detailed
|
||||||
|
- supports enabling format and content Assertions in draft2019-09 or above
|
||||||
|
- change Compiler.AssertFormat, Compiler.AssertContent to true
|
||||||
|
- compiled schema can be introspected. easier to develop tools like generating go structs given schema
|
||||||
|
- supports user-defined keywords via extensions
|
||||||
|
- implements following formats (supports user-defined)
|
||||||
|
- date-time, date, time, duration (supports leap-second)
|
||||||
|
- uuid, hostname, email
|
||||||
|
- ip-address, ipv4, ipv6
|
||||||
|
- uri, uriref, uri-template(limited validation)
|
||||||
|
- json-pointer, relative-json-pointer
|
||||||
|
- regex, format
|
||||||
|
- implements following contentEncoding (supports user-defined)
|
||||||
|
- base64
|
||||||
|
- implements following contentMediaType (supports user-defined)
|
||||||
|
- application/json
|
||||||
|
- can load from files/http/https/string/[]byte/io.Reader (supports user-defined)
|
||||||
|
|
||||||
|
The schema is compiled against the version specified in "$schema" property.
|
||||||
|
If "$schema" property is missing, it uses latest draft which currently implemented
|
||||||
|
by this library.
|
||||||
|
|
||||||
|
You can force to use specific draft, when "$schema" is missing, as follows:
|
||||||
|
|
||||||
|
compiler := jsonschema.NewCompiler()
|
||||||
|
compiler.Draft = jsonschema.Draft4
|
||||||
|
|
||||||
|
This package supports loading json-schema from filePath and fileURL.
|
||||||
|
|
||||||
|
To load json-schema from HTTPURL, add following import:
|
||||||
|
|
||||||
|
import _ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
|
|
||||||
|
you can validate yaml documents. see https://play.golang.org/p/sJy1qY7dXgA
|
||||||
|
*/
|
||||||
|
package jsonschema
|
1454
vendor/github.com/santhosh-tekuri/jsonschema/v5/draft.go
generated
vendored
Normal file
1454
vendor/github.com/santhosh-tekuri/jsonschema/v5/draft.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
129
vendor/github.com/santhosh-tekuri/jsonschema/v5/errors.go
generated
vendored
Normal file
129
vendor/github.com/santhosh-tekuri/jsonschema/v5/errors.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InvalidJSONTypeError is the error type returned by ValidateInterface.
|
||||||
|
// this tells that specified go object is not valid jsonType.
|
||||||
|
type InvalidJSONTypeError string
|
||||||
|
|
||||||
|
func (e InvalidJSONTypeError) Error() string {
|
||||||
|
return fmt.Sprintf("jsonschema: invalid jsonType: %s", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfiniteLoopError is returned by Compile/Validate.
|
||||||
|
// this gives url#keywordLocation that lead to infinity loop.
|
||||||
|
type InfiniteLoopError string
|
||||||
|
|
||||||
|
func (e InfiniteLoopError) Error() string {
|
||||||
|
return "jsonschema: infinite loop " + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func infiniteLoopError(stack []schemaRef, sref schemaRef) InfiniteLoopError {
|
||||||
|
var path string
|
||||||
|
for _, ref := range stack {
|
||||||
|
if path == "" {
|
||||||
|
path += ref.schema.Location
|
||||||
|
} else {
|
||||||
|
path += "/" + ref.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InfiniteLoopError(path + "/" + sref.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaError is the error type returned by Compile.
|
||||||
|
type SchemaError struct {
|
||||||
|
// SchemaURL is the url to json-schema that filed to compile.
|
||||||
|
// This is helpful, if your schema refers to external schemas
|
||||||
|
SchemaURL string
|
||||||
|
|
||||||
|
// Err is the error that occurred during compilation.
|
||||||
|
// It could be ValidationError, because compilation validates
|
||||||
|
// given schema against the json meta-schema
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *SchemaError) Unwrap() error {
|
||||||
|
return se.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *SchemaError) Error() string {
|
||||||
|
s := fmt.Sprintf("jsonschema %s compilation failed", se.SchemaURL)
|
||||||
|
if se.Err != nil {
|
||||||
|
return fmt.Sprintf("%s: %v", s, strings.TrimPrefix(se.Err.Error(), "jsonschema: "))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *SchemaError) GoString() string {
|
||||||
|
if _, ok := se.Err.(*ValidationError); ok {
|
||||||
|
return fmt.Sprintf("jsonschema %s compilation failed\n%#v", se.SchemaURL, se.Err)
|
||||||
|
}
|
||||||
|
return se.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationError is the error type returned by Validate.
|
||||||
|
type ValidationError struct {
|
||||||
|
KeywordLocation string // validation path of validating keyword or schema
|
||||||
|
AbsoluteKeywordLocation string // absolute location of validating keyword or schema
|
||||||
|
InstanceLocation string // location of the json value within the instance being validated
|
||||||
|
Message string // describes error
|
||||||
|
Causes []*ValidationError // nested validation errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ve *ValidationError) add(causes ...error) error {
|
||||||
|
for _, cause := range causes {
|
||||||
|
ve.Causes = append(ve.Causes, cause.(*ValidationError))
|
||||||
|
}
|
||||||
|
return ve
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ve *ValidationError) causes(err error) error {
|
||||||
|
if err := err.(*ValidationError); err.Message == "" {
|
||||||
|
ve.Causes = err.Causes
|
||||||
|
} else {
|
||||||
|
ve.add(err)
|
||||||
|
}
|
||||||
|
return ve
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ve *ValidationError) Error() string {
|
||||||
|
leaf := ve
|
||||||
|
for len(leaf.Causes) > 0 {
|
||||||
|
leaf = leaf.Causes[0]
|
||||||
|
}
|
||||||
|
u, _ := split(ve.AbsoluteKeywordLocation)
|
||||||
|
return fmt.Sprintf("jsonschema: %s does not validate with %s: %s", quote(leaf.InstanceLocation), u+"#"+leaf.KeywordLocation, leaf.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ve *ValidationError) GoString() string {
|
||||||
|
sloc := ve.AbsoluteKeywordLocation
|
||||||
|
sloc = sloc[strings.IndexByte(sloc, '#')+1:]
|
||||||
|
msg := fmt.Sprintf("[I#%s] [S#%s] %s", ve.InstanceLocation, sloc, ve.Message)
|
||||||
|
for _, c := range ve.Causes {
|
||||||
|
for _, line := range strings.Split(c.GoString(), "\n") {
|
||||||
|
msg += "\n " + line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinPtr(ptr1, ptr2 string) string {
|
||||||
|
if len(ptr1) == 0 {
|
||||||
|
return ptr2
|
||||||
|
}
|
||||||
|
if len(ptr2) == 0 {
|
||||||
|
return ptr1
|
||||||
|
}
|
||||||
|
return ptr1 + "/" + ptr2
|
||||||
|
}
|
||||||
|
|
||||||
|
// quote returns single-quoted string
|
||||||
|
func quote(s string) string {
|
||||||
|
s = fmt.Sprintf("%q", s)
|
||||||
|
s = strings.ReplaceAll(s, `\"`, `"`)
|
||||||
|
s = strings.ReplaceAll(s, `'`, `\'`)
|
||||||
|
return "'" + s[1:len(s)-1] + "'"
|
||||||
|
}
|
116
vendor/github.com/santhosh-tekuri/jsonschema/v5/extension.go
generated
vendored
Normal file
116
vendor/github.com/santhosh-tekuri/jsonschema/v5/extension.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
// ExtCompiler compiles custom keyword(s) into ExtSchema.
|
||||||
|
type ExtCompiler interface {
|
||||||
|
// Compile compiles the custom keywords in schema m and returns its compiled representation.
|
||||||
|
// if the schema m does not contain the keywords defined by this extension,
|
||||||
|
// compiled representation nil should be returned.
|
||||||
|
Compile(ctx CompilerContext, m map[string]interface{}) (ExtSchema, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtSchema is schema representation of custom keyword(s)
|
||||||
|
type ExtSchema interface {
|
||||||
|
// Validate validates the json value v with this ExtSchema.
|
||||||
|
// Returned error must be *ValidationError.
|
||||||
|
Validate(ctx ValidationContext, v interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type extension struct {
|
||||||
|
meta *Schema
|
||||||
|
compiler ExtCompiler
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExtension registers custom keyword(s) into this compiler.
|
||||||
|
//
|
||||||
|
// name is extension name, used only to avoid name collisions.
|
||||||
|
// meta captures the metaschema for the new keywords.
|
||||||
|
// This is used to validate the schema before calling ext.Compile.
|
||||||
|
func (c *Compiler) RegisterExtension(name string, meta *Schema, ext ExtCompiler) {
|
||||||
|
c.extensions[name] = extension{meta, ext}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompilerContext ---
|
||||||
|
|
||||||
|
// CompilerContext provides additional context required in compiling for extension.
|
||||||
|
type CompilerContext struct {
|
||||||
|
c *Compiler
|
||||||
|
r *resource
|
||||||
|
stack []schemaRef
|
||||||
|
res *resource
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile compiles given value at ptr into *Schema. This is useful in implementing
|
||||||
|
// keyword like allOf/not/patternProperties.
|
||||||
|
//
|
||||||
|
// schPath is the relative-json-pointer to the schema to be compiled from parent schema.
|
||||||
|
//
|
||||||
|
// applicableOnSameInstance tells whether current schema and the given schema
|
||||||
|
// are applied on same instance value. this is used to detect infinite loop in schema.
|
||||||
|
func (ctx CompilerContext) Compile(schPath string, applicableOnSameInstance bool) (*Schema, error) {
|
||||||
|
var stack []schemaRef
|
||||||
|
if applicableOnSameInstance {
|
||||||
|
stack = ctx.stack
|
||||||
|
}
|
||||||
|
return ctx.c.compileRef(ctx.r, stack, schPath, ctx.res, ctx.r.url+ctx.res.floc+"/"+schPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompileRef compiles the schema referenced by ref uri
|
||||||
|
//
|
||||||
|
// refPath is the relative-json-pointer to ref.
|
||||||
|
//
|
||||||
|
// applicableOnSameInstance tells whether current schema and the given schema
|
||||||
|
// are applied on same instance value. this is used to detect infinite loop in schema.
|
||||||
|
func (ctx CompilerContext) CompileRef(ref string, refPath string, applicableOnSameInstance bool) (*Schema, error) {
|
||||||
|
var stack []schemaRef
|
||||||
|
if applicableOnSameInstance {
|
||||||
|
stack = ctx.stack
|
||||||
|
}
|
||||||
|
return ctx.c.compileRef(ctx.r, stack, refPath, ctx.res, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationContext ---
|
||||||
|
|
||||||
|
// ValidationContext provides additional context required in validating for extension.
|
||||||
|
type ValidationContext struct {
|
||||||
|
result validationResult
|
||||||
|
validate func(sch *Schema, schPath string, v interface{}, vpath string) error
|
||||||
|
validateInplace func(sch *Schema, schPath string) error
|
||||||
|
validationError func(keywordPath string, format string, a ...interface{}) *ValidationError
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvaluatedProp marks given property of object as evaluated.
|
||||||
|
func (ctx ValidationContext) EvaluatedProp(prop string) {
|
||||||
|
delete(ctx.result.unevalProps, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvaluatedItem marks given index of array as evaluated.
|
||||||
|
func (ctx ValidationContext) EvaluatedItem(index int) {
|
||||||
|
delete(ctx.result.unevalItems, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates schema s with value v. Extension must use this method instead of
|
||||||
|
// *Schema.ValidateInterface method. This will be useful in implementing keywords like
|
||||||
|
// allOf/oneOf
|
||||||
|
//
|
||||||
|
// spath is relative-json-pointer to s
|
||||||
|
// vpath is relative-json-pointer to v.
|
||||||
|
func (ctx ValidationContext) Validate(s *Schema, spath string, v interface{}, vpath string) error {
|
||||||
|
if vpath == "" {
|
||||||
|
return ctx.validateInplace(s, spath)
|
||||||
|
}
|
||||||
|
return ctx.validate(s, spath, v, vpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error used to construct validation error by extensions.
|
||||||
|
//
|
||||||
|
// keywordPath is relative-json-pointer to keyword.
|
||||||
|
func (ctx ValidationContext) Error(keywordPath string, format string, a ...interface{}) *ValidationError {
|
||||||
|
return ctx.validationError(keywordPath, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group is used by extensions to group multiple errors as causes to parent error.
|
||||||
|
// This is useful in implementing keywords like allOf where each schema specified
|
||||||
|
// in allOf can result a validationError.
|
||||||
|
func (ValidationError) Group(parent *ValidationError, causes ...error) error {
|
||||||
|
return parent.add(causes...)
|
||||||
|
}
|
567
vendor/github.com/santhosh-tekuri/jsonschema/v5/format.go
generated
vendored
Normal file
567
vendor/github.com/santhosh-tekuri/jsonschema/v5/format.go
generated
vendored
Normal file
@ -0,0 +1,567 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/mail"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Formats is a registry of functions, which know how to validate
|
||||||
|
// a specific format.
|
||||||
|
//
|
||||||
|
// New Formats can be registered by adding to this map. Key is format name,
|
||||||
|
// value is function that knows how to validate that format.
|
||||||
|
var Formats = map[string]func(interface{}) bool{
|
||||||
|
"date-time": isDateTime,
|
||||||
|
"date": isDate,
|
||||||
|
"time": isTime,
|
||||||
|
"duration": isDuration,
|
||||||
|
"period": isPeriod,
|
||||||
|
"hostname": isHostname,
|
||||||
|
"email": isEmail,
|
||||||
|
"ip-address": isIPV4,
|
||||||
|
"ipv4": isIPV4,
|
||||||
|
"ipv6": isIPV6,
|
||||||
|
"uri": isURI,
|
||||||
|
"iri": isURI,
|
||||||
|
"uri-reference": isURIReference,
|
||||||
|
"uriref": isURIReference,
|
||||||
|
"iri-reference": isURIReference,
|
||||||
|
"uri-template": isURITemplate,
|
||||||
|
"regex": isRegex,
|
||||||
|
"json-pointer": isJSONPointer,
|
||||||
|
"relative-json-pointer": isRelativeJSONPointer,
|
||||||
|
"uuid": isUUID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDateTime tells whether given string is a valid date representation
|
||||||
|
// as defined by RFC 3339, section 5.6.
|
||||||
|
//
|
||||||
|
// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6, for details
|
||||||
|
func isDateTime(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(s) < 20 { // yyyy-mm-ddThh:mm:ssZ
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[10] != 'T' && s[10] != 't' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isDate(s[:10]) && isTime(s[11:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDate tells whether given string is a valid full-date production
|
||||||
|
// as defined by RFC 3339, section 5.6.
|
||||||
|
//
|
||||||
|
// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6, for details
|
||||||
|
func isDate(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err := time.Parse("2006-01-02", s)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTime tells whether given string is a valid full-time production
|
||||||
|
// as defined by RFC 3339, section 5.6.
|
||||||
|
//
|
||||||
|
// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6, for details
|
||||||
|
func isTime(v interface{}) bool {
|
||||||
|
str, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// golang time package does not support leap seconds.
|
||||||
|
// so we are parsing it manually here.
|
||||||
|
|
||||||
|
// hh:mm:ss
|
||||||
|
// 01234567
|
||||||
|
if len(str) < 9 || str[2] != ':' || str[5] != ':' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
isInRange := func(str string, min, max int) (int, bool) {
|
||||||
|
n, err := strconv.Atoi(str)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if n < min || n > max {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
var h, m, s int
|
||||||
|
if h, ok = isInRange(str[0:2], 0, 23); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if m, ok = isInRange(str[3:5], 0, 59); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s, ok = isInRange(str[6:8], 0, 60); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
str = str[8:]
|
||||||
|
|
||||||
|
// parse secfrac if present
|
||||||
|
if str[0] == '.' {
|
||||||
|
// dot following more than one digit
|
||||||
|
str = str[1:]
|
||||||
|
var numDigits int
|
||||||
|
for str != "" {
|
||||||
|
if str[0] < '0' || str[0] > '9' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
numDigits++
|
||||||
|
str = str[1:]
|
||||||
|
}
|
||||||
|
if numDigits == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if str[0] == 'z' || str[0] == 'Z' {
|
||||||
|
if len(str) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// time-numoffset
|
||||||
|
// +hh:mm
|
||||||
|
// 012345
|
||||||
|
if len(str) != 6 || str[3] != ':' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var sign int
|
||||||
|
if str[0] == '+' {
|
||||||
|
sign = -1
|
||||||
|
} else if str[0] == '-' {
|
||||||
|
sign = +1
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var zh, zm int
|
||||||
|
if zh, ok = isInRange(str[1:3], 0, 23); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if zm, ok = isInRange(str[4:6], 0, 59); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply timezone offset
|
||||||
|
hm := (h*60 + m) + sign*(zh*60+zm)
|
||||||
|
if hm < 0 {
|
||||||
|
hm += 24 * 60
|
||||||
|
}
|
||||||
|
h, m = hm/60, hm%60
|
||||||
|
}
|
||||||
|
|
||||||
|
// check leapsecond
|
||||||
|
if s == 60 { // leap second
|
||||||
|
if h != 23 || m != 59 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDuration tells whether given string is a valid duration format
|
||||||
|
// from the ISO 8601 ABNF as given in Appendix A of RFC 3339.
|
||||||
|
//
|
||||||
|
// see https://datatracker.ietf.org/doc/html/rfc3339#appendix-A, for details
|
||||||
|
func isDuration(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(s) == 0 || s[0] != 'P' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
parseUnits := func() (units string, ok bool) {
|
||||||
|
for len(s) > 0 && s[0] != 'T' {
|
||||||
|
digits := false
|
||||||
|
for {
|
||||||
|
if len(s) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s[0] < '0' || s[0] > '9' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
digits = true
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
if !digits || len(s) == 0 {
|
||||||
|
return units, false
|
||||||
|
}
|
||||||
|
units += s[:1]
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
return units, true
|
||||||
|
}
|
||||||
|
units, ok := parseUnits()
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if units == "W" {
|
||||||
|
return len(s) == 0 // P_W
|
||||||
|
}
|
||||||
|
if len(units) > 0 {
|
||||||
|
if strings.Index("YMD", units) == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(s) == 0 {
|
||||||
|
return true // "P" dur-date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s) == 0 || s[0] != 'T' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
units, ok = parseUnits()
|
||||||
|
return ok && len(s) == 0 && len(units) > 0 && strings.Index("HMS", units) != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// isPeriod tells whether given string is a valid period format
|
||||||
|
// from the ISO 8601 ABNF as given in Appendix A of RFC 3339.
|
||||||
|
//
|
||||||
|
// see https://datatracker.ietf.org/doc/html/rfc3339#appendix-A, for details
|
||||||
|
func isPeriod(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
slash := strings.IndexByte(s, '/')
|
||||||
|
if slash == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
start, end := s[:slash], s[slash+1:]
|
||||||
|
if isDateTime(start) {
|
||||||
|
return isDateTime(end) || isDuration(end)
|
||||||
|
}
|
||||||
|
return isDuration(start) && isDateTime(end)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isHostname tells whether given string is a valid representation
|
||||||
|
// for an Internet host name, as defined by RFC 1034 section 3.1 and
|
||||||
|
// RFC 1123 section 2.1.
|
||||||
|
//
|
||||||
|
// See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names, for details.
|
||||||
|
func isHostname(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// entire hostname (including the delimiting dots but not a trailing dot) has a maximum of 253 ASCII characters
|
||||||
|
s = strings.TrimSuffix(s, ".")
|
||||||
|
if len(s) > 253 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostnames are composed of series of labels concatenated with dots, as are all domain names
|
||||||
|
for _, label := range strings.Split(s, ".") {
|
||||||
|
// Each label must be from 1 to 63 characters long
|
||||||
|
if labelLen := len(label); labelLen < 1 || labelLen > 63 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// labels must not start with a hyphen
|
||||||
|
// RFC 1123 section 2.1: restriction on the first character
|
||||||
|
// is relaxed to allow either a letter or a digit
|
||||||
|
if first := s[0]; first == '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// must not end with a hyphen
|
||||||
|
if label[len(label)-1] == '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// labels may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner),
|
||||||
|
// the digits '0' through '9', and the hyphen ('-')
|
||||||
|
for _, c := range label {
|
||||||
|
if valid := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '-'); !valid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmail tells whether given string is a valid Internet email address
|
||||||
|
// as defined by RFC 5322, section 3.4.1.
|
||||||
|
//
|
||||||
|
// See https://en.wikipedia.org/wiki/Email_address, for details.
|
||||||
|
func isEmail(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// entire email address to be no more than 254 characters long
|
||||||
|
if len(s) > 254 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// email address is generally recognized as having two parts joined with an at-sign
|
||||||
|
at := strings.LastIndexByte(s, '@')
|
||||||
|
if at == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
local := s[0:at]
|
||||||
|
domain := s[at+1:]
|
||||||
|
|
||||||
|
// local part may be up to 64 characters long
|
||||||
|
if len(local) > 64 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// domain if enclosed in brackets, must match an IP address
|
||||||
|
if len(domain) >= 2 && domain[0] == '[' && domain[len(domain)-1] == ']' {
|
||||||
|
ip := domain[1 : len(domain)-1]
|
||||||
|
if strings.HasPrefix(ip, "IPv6:") {
|
||||||
|
return isIPV6(strings.TrimPrefix(ip, "IPv6:"))
|
||||||
|
}
|
||||||
|
return isIPV4(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// domain must match the requirements for a hostname
|
||||||
|
if !isHostname(domain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := mail.ParseAddress(s)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIPV4 tells whether given string is a valid representation of an IPv4 address
|
||||||
|
// according to the "dotted-quad" ABNF syntax as defined in RFC 2673, section 3.2.
|
||||||
|
func isIPV4(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
groups := strings.Split(s, ".")
|
||||||
|
if len(groups) != 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
n, err := strconv.Atoi(group)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n < 0 || n > 255 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n != 0 && group[0] == '0' {
|
||||||
|
return false // leading zeroes should be rejected, as they are treated as octals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIPV6 tells whether given string is a valid representation of an IPv6 address
|
||||||
|
// as defined in RFC 2373, section 2.2.
|
||||||
|
func isIPV6(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !strings.Contains(s, ":") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return net.ParseIP(s) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isURI tells whether given string is valid URI, according to RFC 3986.
|
||||||
|
func isURI(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
u, err := urlParse(s)
|
||||||
|
return err == nil && u.IsAbs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlParse(s string) (*url.URL, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if hostname is ipv6, validate it
|
||||||
|
hostname := u.Hostname()
|
||||||
|
if strings.IndexByte(hostname, ':') != -1 {
|
||||||
|
if strings.IndexByte(u.Host, '[') == -1 || strings.IndexByte(u.Host, ']') == -1 {
|
||||||
|
return nil, errors.New("ipv6 address is not enclosed in brackets")
|
||||||
|
}
|
||||||
|
if !isIPV6(hostname) {
|
||||||
|
return nil, errors.New("invalid ipv6 address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isURIReference tells whether given string is a valid URI Reference
|
||||||
|
// (either a URI or a relative-reference), according to RFC 3986.
|
||||||
|
func isURIReference(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err := urlParse(s)
|
||||||
|
return err == nil && !strings.Contains(s, `\`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isURITemplate tells whether given string is a valid URI Template
|
||||||
|
// according to RFC6570.
|
||||||
|
//
|
||||||
|
// Current implementation does minimal validation.
|
||||||
|
func isURITemplate(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
u, err := urlParse(s)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, item := range strings.Split(u.RawPath, "/") {
|
||||||
|
depth := 0
|
||||||
|
for _, ch := range item {
|
||||||
|
switch ch {
|
||||||
|
case '{':
|
||||||
|
depth++
|
||||||
|
if depth != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case '}':
|
||||||
|
depth--
|
||||||
|
if depth != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if depth != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRegex tells whether given string is a valid regular expression,
|
||||||
|
// according to the ECMA 262 regular expression dialect.
|
||||||
|
//
|
||||||
|
// The implementation uses go-lang regexp package.
|
||||||
|
func isRegex(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err := regexp.Compile(s)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isJSONPointer tells whether given string is a valid JSON Pointer.
|
||||||
|
//
|
||||||
|
// Note: It returns false for JSON Pointer URI fragments.
|
||||||
|
func isJSONPointer(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s != "" && !strings.HasPrefix(s, "/") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, item := range strings.Split(s, "/") {
|
||||||
|
for i := 0; i < len(item); i++ {
|
||||||
|
if item[i] == '~' {
|
||||||
|
if i == len(item)-1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch item[i+1] {
|
||||||
|
case '0', '1':
|
||||||
|
// valid
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRelativeJSONPointer tells whether given string is a valid Relative JSON Pointer.
|
||||||
|
//
|
||||||
|
// see https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
|
||||||
|
func isRelativeJSONPointer(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] == '0' {
|
||||||
|
s = s[1:]
|
||||||
|
} else if s[0] >= '0' && s[0] <= '9' {
|
||||||
|
for s != "" && s[0] >= '0' && s[0] <= '9' {
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s == "#" || isJSONPointer(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUUID tells whether given string is a valid uuid format
|
||||||
|
// as specified in RFC4122.
|
||||||
|
//
|
||||||
|
// see https://datatracker.ietf.org/doc/html/rfc4122#page-4, for details
|
||||||
|
func isUUID(v interface{}) bool {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
parseHex := func(n int) bool {
|
||||||
|
for n > 0 {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hex := (s[0] >= '0' && s[0] <= '9') || (s[0] >= 'a' && s[0] <= 'f') || (s[0] >= 'A' && s[0] <= 'F')
|
||||||
|
if !hex {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
groups := []int{8, 4, 4, 4, 12}
|
||||||
|
for i, numDigits := range groups {
|
||||||
|
if !parseHex(numDigits) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if i == len(groups)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(s) == 0 || s[0] != '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
return len(s) == 0
|
||||||
|
}
|
60
vendor/github.com/santhosh-tekuri/jsonschema/v5/loader.go
generated
vendored
Normal file
60
vendor/github.com/santhosh-tekuri/jsonschema/v5/loader.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadFileURL(s string) (io.ReadCloser, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := u.Path
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
f = strings.TrimPrefix(f, "/")
|
||||||
|
f = filepath.FromSlash(f)
|
||||||
|
}
|
||||||
|
return os.Open(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loaders is a registry of functions, which know how to load
|
||||||
|
// absolute url of specific schema.
|
||||||
|
//
|
||||||
|
// New loaders can be registered by adding to this map. Key is schema,
|
||||||
|
// value is function that knows how to load url of that schema
|
||||||
|
var Loaders = map[string]func(url string) (io.ReadCloser, error){
|
||||||
|
"file": loadFileURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoaderNotFoundError is the error type returned by Load function.
|
||||||
|
// It tells that no Loader is registered for that URL Scheme.
|
||||||
|
type LoaderNotFoundError string
|
||||||
|
|
||||||
|
func (e LoaderNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("jsonschema: no Loader found for %s", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadURL loads document at given absolute URL. The default implementation
|
||||||
|
// uses Loaders registry to lookup by schema and uses that loader.
|
||||||
|
//
|
||||||
|
// Users can change this variable, if they would like to take complete
|
||||||
|
// responsibility of loading given URL. Used by Compiler if its LoadURL
|
||||||
|
// field is nil.
|
||||||
|
var LoadURL = func(s string) (io.ReadCloser, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
loader, ok := Loaders[u.Scheme]
|
||||||
|
if !ok {
|
||||||
|
return nil, LoaderNotFoundError(s)
|
||||||
|
|
||||||
|
}
|
||||||
|
return loader(s)
|
||||||
|
}
|
77
vendor/github.com/santhosh-tekuri/jsonschema/v5/output.go
generated
vendored
Normal file
77
vendor/github.com/santhosh-tekuri/jsonschema/v5/output.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
// Flag is output format with simple boolean property valid.
|
||||||
|
type Flag struct {
|
||||||
|
Valid bool `json:"valid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlagOutput returns output in flag format
|
||||||
|
func (ve *ValidationError) FlagOutput() Flag {
|
||||||
|
return Flag{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic ---
|
||||||
|
|
||||||
|
// Basic is output format with flat list of output units.
|
||||||
|
type Basic struct {
|
||||||
|
Valid bool `json:"valid"`
|
||||||
|
Errors []BasicError `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicError is output unit in basic format.
|
||||||
|
type BasicError struct {
|
||||||
|
KeywordLocation string `json:"keywordLocation"`
|
||||||
|
AbsoluteKeywordLocation string `json:"absoluteKeywordLocation"`
|
||||||
|
InstanceLocation string `json:"instanceLocation"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicOutput returns output in basic format
|
||||||
|
func (ve *ValidationError) BasicOutput() Basic {
|
||||||
|
var errors []BasicError
|
||||||
|
var flatten func(*ValidationError)
|
||||||
|
flatten = func(ve *ValidationError) {
|
||||||
|
errors = append(errors, BasicError{
|
||||||
|
KeywordLocation: ve.KeywordLocation,
|
||||||
|
AbsoluteKeywordLocation: ve.AbsoluteKeywordLocation,
|
||||||
|
InstanceLocation: ve.InstanceLocation,
|
||||||
|
Error: ve.Message,
|
||||||
|
})
|
||||||
|
for _, cause := range ve.Causes {
|
||||||
|
flatten(cause)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flatten(ve)
|
||||||
|
return Basic{Errors: errors}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detailed ---
|
||||||
|
|
||||||
|
// Detailed is output format based on structure of schema.
|
||||||
|
type Detailed struct {
|
||||||
|
Valid bool `json:"valid"`
|
||||||
|
KeywordLocation string `json:"keywordLocation"`
|
||||||
|
AbsoluteKeywordLocation string `json:"absoluteKeywordLocation"`
|
||||||
|
InstanceLocation string `json:"instanceLocation"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
Errors []Detailed `json:"errors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetailedOutput returns output in detailed format
|
||||||
|
func (ve *ValidationError) DetailedOutput() Detailed {
|
||||||
|
var errors []Detailed
|
||||||
|
for _, cause := range ve.Causes {
|
||||||
|
errors = append(errors, cause.DetailedOutput())
|
||||||
|
}
|
||||||
|
var message = ve.Message
|
||||||
|
if len(ve.Causes) > 0 {
|
||||||
|
message = ""
|
||||||
|
}
|
||||||
|
return Detailed{
|
||||||
|
KeywordLocation: ve.KeywordLocation,
|
||||||
|
AbsoluteKeywordLocation: ve.AbsoluteKeywordLocation,
|
||||||
|
InstanceLocation: ve.InstanceLocation,
|
||||||
|
Error: message,
|
||||||
|
Errors: errors,
|
||||||
|
}
|
||||||
|
}
|
280
vendor/github.com/santhosh-tekuri/jsonschema/v5/resource.go
generated
vendored
Normal file
280
vendor/github.com/santhosh-tekuri/jsonschema/v5/resource.go
generated
vendored
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type resource struct {
|
||||||
|
url string // base url of resource. can be empty
|
||||||
|
floc string // fragment with json-pointer from root resource
|
||||||
|
doc interface{}
|
||||||
|
draft *Draft
|
||||||
|
subresources map[string]*resource // key is floc. only applicable for root resource
|
||||||
|
schema *Schema
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resource) String() string {
|
||||||
|
return r.url + r.floc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResource(url string, r io.Reader) (*resource, error) {
|
||||||
|
if strings.IndexByte(url, '#') != -1 {
|
||||||
|
panic(fmt.Sprintf("BUG: newResource(%q)", url))
|
||||||
|
}
|
||||||
|
doc, err := unmarshal(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("jsonschema: invalid json %s: %v", url, err)
|
||||||
|
}
|
||||||
|
url, err = toAbs(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resource{
|
||||||
|
url: url,
|
||||||
|
floc: "#",
|
||||||
|
doc: doc,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillSubschemas fills subschemas in res into r.subresources
|
||||||
|
func (r *resource) fillSubschemas(c *Compiler, res *resource) error {
|
||||||
|
if err := c.validateSchema(r, res.doc, res.floc[1:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.subresources == nil {
|
||||||
|
r.subresources = make(map[string]*resource)
|
||||||
|
}
|
||||||
|
if err := r.draft.listSubschemas(res, r.baseURL(res.floc), r.subresources); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure subresource.url uniqueness
|
||||||
|
url2floc := make(map[string]string)
|
||||||
|
for _, sr := range r.subresources {
|
||||||
|
if sr.url != "" {
|
||||||
|
if floc, ok := url2floc[sr.url]; ok {
|
||||||
|
return fmt.Errorf("jsonschema: %q and %q in %s have same canonical-uri", floc[1:], sr.floc[1:], r.url)
|
||||||
|
}
|
||||||
|
url2floc[sr.url] = sr.floc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// listResources lists all subresources in res
|
||||||
|
func (r *resource) listResources(res *resource) []*resource {
|
||||||
|
var result []*resource
|
||||||
|
prefix := res.floc + "/"
|
||||||
|
for _, sr := range r.subresources {
|
||||||
|
if strings.HasPrefix(sr.floc, prefix) {
|
||||||
|
result = append(result, sr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resource) findResource(url string) *resource {
|
||||||
|
if r.url == url {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
for _, res := range r.subresources {
|
||||||
|
if res.url == url {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve fragment f with sr as base
|
||||||
|
func (r *resource) resolveFragment(c *Compiler, sr *resource, f string) (*resource, error) {
|
||||||
|
if f == "#" || f == "#/" {
|
||||||
|
return sr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve by anchor
|
||||||
|
if !strings.HasPrefix(f, "#/") {
|
||||||
|
// check in given resource
|
||||||
|
for _, anchor := range r.draft.anchors(sr.doc) {
|
||||||
|
if anchor == f[1:] {
|
||||||
|
return sr, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check in subresources that has same base url
|
||||||
|
prefix := sr.floc + "/"
|
||||||
|
for _, res := range r.subresources {
|
||||||
|
if strings.HasPrefix(res.floc, prefix) && r.baseURL(res.floc) == sr.url {
|
||||||
|
for _, anchor := range r.draft.anchors(res.doc) {
|
||||||
|
if anchor == f[1:] {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve by ptr
|
||||||
|
floc := sr.floc + f[1:]
|
||||||
|
if res, ok := r.subresources[floc]; ok {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-standrad location
|
||||||
|
doc := r.doc
|
||||||
|
for _, item := range strings.Split(floc[2:], "/") {
|
||||||
|
item = strings.Replace(item, "~1", "/", -1)
|
||||||
|
item = strings.Replace(item, "~0", "~", -1)
|
||||||
|
item, err := url.PathUnescape(item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch d := doc.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
if _, ok := d[item]; !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
doc = d[item]
|
||||||
|
case []interface{}:
|
||||||
|
index, err := strconv.Atoi(item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if index < 0 || index >= len(d) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
doc = d[index]
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := r.draft.resolveID(r.baseURL(floc), doc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := &resource{url: id, floc: floc, doc: doc}
|
||||||
|
r.subresources[floc] = res
|
||||||
|
if err := r.fillSubschemas(c, res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resource) baseURL(floc string) string {
|
||||||
|
for {
|
||||||
|
if sr, ok := r.subresources[floc]; ok {
|
||||||
|
if sr.url != "" {
|
||||||
|
return sr.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slash := strings.LastIndexByte(floc, '/')
|
||||||
|
if slash == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
floc = floc[:slash]
|
||||||
|
}
|
||||||
|
return r.url
|
||||||
|
}
|
||||||
|
|
||||||
|
// url helpers ---
|
||||||
|
|
||||||
|
func toAbs(s string) (string, error) {
|
||||||
|
// if windows absolute file path, convert to file url
|
||||||
|
// because: net/url parses driver name as scheme
|
||||||
|
if runtime.GOOS == "windows" && len(s) >= 3 && s[1:3] == `:\` {
|
||||||
|
s = "file:///" + filepath.ToSlash(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if u.IsAbs() {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// s is filepath
|
||||||
|
if s, err = filepath.Abs(s); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
s = "file:///" + filepath.ToSlash(s)
|
||||||
|
} else {
|
||||||
|
s = "file://" + s
|
||||||
|
}
|
||||||
|
u, err = url.Parse(s) // to fix spaces in filepath
|
||||||
|
return u.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveURL(base, ref string) (string, error) {
|
||||||
|
if ref == "" {
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(ref, "urn:") {
|
||||||
|
return ref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
refURL, err := url.Parse(ref)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if refURL.IsAbs() {
|
||||||
|
return ref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(base, "urn:") {
|
||||||
|
base, _ = split(base)
|
||||||
|
return base + ref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
baseURL, err := url.Parse(base)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return baseURL.ResolveReference(refURL).String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func split(uri string) (string, string) {
|
||||||
|
hash := strings.IndexByte(uri, '#')
|
||||||
|
if hash == -1 {
|
||||||
|
return uri, "#"
|
||||||
|
}
|
||||||
|
f := uri[hash:]
|
||||||
|
if f == "#/" {
|
||||||
|
f = "#"
|
||||||
|
}
|
||||||
|
return uri[0:hash], f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) url() string {
|
||||||
|
u, _ := split(s.Location)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) loc() string {
|
||||||
|
_, f := split(s.Location)
|
||||||
|
return f[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(r io.Reader) (interface{}, error) {
|
||||||
|
decoder := json.NewDecoder(r)
|
||||||
|
decoder.UseNumber()
|
||||||
|
var doc interface{}
|
||||||
|
if err := decoder.Decode(&doc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if t, _ := decoder.Token(); t != nil {
|
||||||
|
return nil, fmt.Errorf("invalid character %v after top-level value", t)
|
||||||
|
}
|
||||||
|
return doc, nil
|
||||||
|
}
|
900
vendor/github.com/santhosh-tekuri/jsonschema/v5/schema.go
generated
vendored
Normal file
900
vendor/github.com/santhosh-tekuri/jsonschema/v5/schema.go
generated
vendored
Normal file
@ -0,0 +1,900 @@
|
|||||||
|
package jsonschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"hash/maphash"
|
||||||
|
"math/big"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Schema represents compiled version of json-schema.
|
||||||
|
type Schema struct {
|
||||||
|
Location string // absolute location
|
||||||
|
|
||||||
|
Draft *Draft // draft used by schema.
|
||||||
|
meta *Schema
|
||||||
|
vocab []string
|
||||||
|
dynamicAnchors []*Schema
|
||||||
|
|
||||||
|
// type agnostic validations
|
||||||
|
Format string
|
||||||
|
format func(interface{}) bool
|
||||||
|
Always *bool // always pass/fail. used when booleans are used as schemas in draft-07.
|
||||||
|
Ref *Schema
|
||||||
|
RecursiveAnchor bool
|
||||||
|
RecursiveRef *Schema
|
||||||
|
DynamicAnchor string
|
||||||
|
DynamicRef *Schema
|
||||||
|
dynamicRefAnchor string
|
||||||
|
Types []string // allowed types.
|
||||||
|
Constant []interface{} // first element in slice is constant value. note: slice is used to capture nil constant.
|
||||||
|
Enum []interface{} // allowed values.
|
||||||
|
enumError string // error message for enum fail. captured here to avoid constructing error message every time.
|
||||||
|
Not *Schema
|
||||||
|
AllOf []*Schema
|
||||||
|
AnyOf []*Schema
|
||||||
|
OneOf []*Schema
|
||||||
|
If *Schema
|
||||||
|
Then *Schema // nil, when If is nil.
|
||||||
|
Else *Schema // nil, when If is nil.
|
||||||
|
|
||||||
|
// object validations
|
||||||
|
MinProperties int // -1 if not specified.
|
||||||
|
MaxProperties int // -1 if not specified.
|
||||||
|
Required []string // list of required properties.
|
||||||
|
Properties map[string]*Schema
|
||||||
|
PropertyNames *Schema
|
||||||
|
RegexProperties bool // property names must be valid regex. used only in draft4 as workaround in metaschema.
|
||||||
|
PatternProperties map[*regexp.Regexp]*Schema
|
||||||
|
AdditionalProperties interface{} // nil or bool or *Schema.
|
||||||
|
Dependencies map[string]interface{} // map value is *Schema or []string.
|
||||||
|
DependentRequired map[string][]string
|
||||||
|
DependentSchemas map[string]*Schema
|
||||||
|
UnevaluatedProperties *Schema
|
||||||
|
|
||||||
|
// array validations
|
||||||
|
MinItems int // -1 if not specified.
|
||||||
|
MaxItems int // -1 if not specified.
|
||||||
|
UniqueItems bool
|
||||||
|
Items interface{} // nil or *Schema or []*Schema
|
||||||
|
AdditionalItems interface{} // nil or bool or *Schema.
|
||||||
|
PrefixItems []*Schema
|
||||||
|
Items2020 *Schema // items keyword reintroduced in draft 2020-12
|
||||||
|
Contains *Schema
|
||||||
|
ContainsEval bool // whether any item in an array that passes validation of the contains schema is considered "evaluated"
|
||||||
|
MinContains int // 1 if not specified
|
||||||
|
MaxContains int // -1 if not specified
|
||||||
|
UnevaluatedItems *Schema
|
||||||
|
|
||||||
|
// string validations
|
||||||
|
MinLength int // -1 if not specified.
|
||||||
|
MaxLength int // -1 if not specified.
|
||||||
|
Pattern *regexp.Regexp
|
||||||
|
ContentEncoding string
|
||||||
|
decoder func(string) ([]byte, error)
|
||||||
|
ContentMediaType string
|
||||||
|
mediaType func([]byte) error
|
||||||
|
ContentSchema *Schema
|
||||||
|
|
||||||
|
// number validators
|
||||||
|
Minimum *big.Rat
|
||||||
|
ExclusiveMinimum *big.Rat
|
||||||
|
Maximum *big.Rat
|
||||||
|
ExclusiveMaximum *big.Rat
|
||||||
|
MultipleOf *big.Rat
|
||||||
|
|
||||||
|
// annotations. captured only when Compiler.ExtractAnnotations is true.
|
||||||
|
Title string
|
||||||
|
Description string
|
||||||
|
Default interface{}
|
||||||
|
Comment string
|
||||||
|
ReadOnly bool
|
||||||
|
WriteOnly bool
|
||||||
|
Examples []interface{}
|
||||||
|
Deprecated bool
|
||||||
|
|
||||||
|
// user defined extensions
|
||||||
|
Extensions map[string]ExtSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) String() string {
|
||||||
|
return s.Location
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSchema(url, floc string, draft *Draft, doc interface{}) *Schema {
|
||||||
|
// fill with default values
|
||||||
|
s := &Schema{
|
||||||
|
Location: url + floc,
|
||||||
|
Draft: draft,
|
||||||
|
MinProperties: -1,
|
||||||
|
MaxProperties: -1,
|
||||||
|
MinItems: -1,
|
||||||
|
MaxItems: -1,
|
||||||
|
MinContains: 1,
|
||||||
|
MaxContains: -1,
|
||||||
|
MinLength: -1,
|
||||||
|
MaxLength: -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if doc, ok := doc.(map[string]interface{}); ok {
|
||||||
|
if ra, ok := doc["$recursiveAnchor"]; ok {
|
||||||
|
if ra, ok := ra.(bool); ok {
|
||||||
|
s.RecursiveAnchor = ra
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if da, ok := doc["$dynamicAnchor"]; ok {
|
||||||
|
if da, ok := da.(string); ok {
|
||||||
|
s.DynamicAnchor = da
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) hasVocab(name string) bool {
|
||||||
|
if s == nil { // during bootstrap
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if name == "core" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, url := range s.vocab {
|
||||||
|
if url == "https://json-schema.org/draft/2019-09/vocab/"+name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if url == "https://json-schema.org/draft/2020-12/vocab/"+name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates given doc, against the json-schema s.
|
||||||
|
//
|
||||||
|
// the v must be the raw json value. for number precision
|
||||||
|
// unmarshal with json.UseNumber().
|
||||||
|
//
|
||||||
|
// returns *ValidationError if v does not confirm with schema s.
|
||||||
|
// returns InfiniteLoopError if it detects loop during validation.
|
||||||
|
// returns InvalidJSONTypeError if it detects any non json value in v.
|
||||||
|
func (s *Schema) Validate(v interface{}) (err error) {
|
||||||
|
return s.validateValue(v, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) validateValue(v interface{}, vloc string) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
switch r := r.(type) {
|
||||||
|
case InfiniteLoopError, InvalidJSONTypeError:
|
||||||
|
err = r.(error)
|
||||||
|
default:
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if _, err := s.validate(nil, 0, "", v, vloc); err != nil {
|
||||||
|
ve := ValidationError{
|
||||||
|
KeywordLocation: "",
|
||||||
|
AbsoluteKeywordLocation: s.Location,
|
||||||
|
InstanceLocation: vloc,
|
||||||
|
Message: fmt.Sprintf("doesn't validate with %s", s.Location),
|
||||||
|
}
|
||||||
|
return ve.causes(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate validates given value v with this schema.
|
||||||
|
func (s *Schema) validate(scope []schemaRef, vscope int, spath string, v interface{}, vloc string) (result validationResult, err error) {
|
||||||
|
validationError := func(keywordPath string, format string, a ...interface{}) *ValidationError {
|
||||||
|
return &ValidationError{
|
||||||
|
KeywordLocation: keywordLocation(scope, keywordPath),
|
||||||
|
AbsoluteKeywordLocation: joinPtr(s.Location, keywordPath),
|
||||||
|
InstanceLocation: vloc,
|
||||||
|
Message: fmt.Sprintf(format, a...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sref := schemaRef{spath, s, false}
|
||||||
|
if err := checkLoop(scope[len(scope)-vscope:], sref); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
scope = append(scope, sref)
|
||||||
|
vscope++
|
||||||
|
|
||||||
|
// populate result
|
||||||
|
switch v := v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
result.unevalProps = make(map[string]struct{})
|
||||||
|
for pname := range v {
|
||||||
|
result.unevalProps[pname] = struct{}{}
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
result.unevalItems = make(map[int]struct{})
|
||||||
|
for i := range v {
|
||||||
|
result.unevalItems[i] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate := func(sch *Schema, schPath string, v interface{}, vpath string) error {
|
||||||
|
vloc := vloc
|
||||||
|
if vpath != "" {
|
||||||
|
vloc += "/" + vpath
|
||||||
|
}
|
||||||
|
_, err := sch.validate(scope, 0, schPath, v, vloc)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
validateInplace := func(sch *Schema, schPath string) error {
|
||||||
|
vr, err := sch.validate(scope, vscope, schPath, v, vloc)
|
||||||
|
if err == nil {
|
||||||
|
// update result
|
||||||
|
for pname := range result.unevalProps {
|
||||||
|
if _, ok := vr.unevalProps[pname]; !ok {
|
||||||
|
delete(result.unevalProps, pname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range result.unevalItems {
|
||||||
|
if _, ok := vr.unevalItems[i]; !ok {
|
||||||
|
delete(result.unevalItems, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Always != nil {
|
||||||
|
if !*s.Always {
|
||||||
|
return result, validationError("", "not allowed")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.Types) > 0 {
|
||||||
|
vType := jsonType(v)
|
||||||
|
matched := false
|
||||||
|
for _, t := range s.Types {
|
||||||
|
if vType == t {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
} else if t == "integer" && vType == "number" {
|
||||||
|
num, _ := new(big.Rat).SetString(fmt.Sprint(v))
|
||||||
|
if num.IsInt() {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return result, validationError("type", "expected %s, but got %s", strings.Join(s.Types, " or "), vType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors []error
|
||||||
|
|
||||||
|
if len(s.Constant) > 0 {
|
||||||
|
if !equals(v, s.Constant[0]) {
|
||||||
|
switch jsonType(s.Constant[0]) {
|
||||||
|
case "object", "array":
|
||||||
|
errors = append(errors, validationError("const", "const failed"))
|
||||||
|
default:
|
||||||
|
errors = append(errors, validationError("const", "value must be %#v", s.Constant[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.Enum) > 0 {
|
||||||
|
matched := false
|
||||||
|
for _, item := range s.Enum {
|
||||||
|
if equals(v, item) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
errors = append(errors, validationError("enum", s.enumError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.format != nil && !s.format(v) {
|
||||||
|
var val = v
|
||||||
|
if v, ok := v.(string); ok {
|
||||||
|
val = quote(v)
|
||||||
|
}
|
||||||
|
errors = append(errors, validationError("format", "%v is not valid %s", val, quote(s.Format)))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
if s.MinProperties != -1 && len(v) < s.MinProperties {
|
||||||
|
errors = append(errors, validationError("minProperties", "minimum %d properties allowed, but found %d properties", s.MinProperties, len(v)))
|
||||||
|
}
|
||||||
|
if s.MaxProperties != -1 && len(v) > s.MaxProperties {
|
||||||
|
errors = append(errors, validationError("maxProperties", "maximum %d properties allowed, but found %d properties", s.MaxProperties, len(v)))
|
||||||
|
}
|
||||||
|
if len(s.Required) > 0 {
|
||||||
|
var missing []string
|
||||||
|
for _, pname := range s.Required {
|
||||||
|
if _, ok := v[pname]; !ok {
|
||||||
|
missing = append(missing, quote(pname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(missing) > 0 {
|
||||||
|
errors = append(errors, validationError("required", "missing properties: %s", strings.Join(missing, ", ")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for pname, sch := range s.Properties {
|
||||||
|
if pvalue, ok := v[pname]; ok {
|
||||||
|
delete(result.unevalProps, pname)
|
||||||
|
if err := validate(sch, "properties/"+escape(pname), pvalue, escape(pname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.PropertyNames != nil {
|
||||||
|
for pname := range v {
|
||||||
|
if err := validate(s.PropertyNames, "propertyNames", pname, escape(pname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.RegexProperties {
|
||||||
|
for pname := range v {
|
||||||
|
if !isRegex(pname) {
|
||||||
|
errors = append(errors, validationError("", "patternProperty %s is not valid regex", quote(pname)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for pattern, sch := range s.PatternProperties {
|
||||||
|
for pname, pvalue := range v {
|
||||||
|
if pattern.MatchString(pname) {
|
||||||
|
delete(result.unevalProps, pname)
|
||||||
|
if err := validate(sch, "patternProperties/"+escape(pattern.String()), pvalue, escape(pname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.AdditionalProperties != nil {
|
||||||
|
if allowed, ok := s.AdditionalProperties.(bool); ok {
|
||||||
|
if !allowed && len(result.unevalProps) > 0 {
|
||||||
|
errors = append(errors, validationError("additionalProperties", "additionalProperties %s not allowed", result.unevalPnames()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schema := s.AdditionalProperties.(*Schema)
|
||||||
|
for pname := range result.unevalProps {
|
||||||
|
if pvalue, ok := v[pname]; ok {
|
||||||
|
if err := validate(schema, "additionalProperties", pvalue, escape(pname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.unevalProps = nil
|
||||||
|
}
|
||||||
|
for dname, dvalue := range s.Dependencies {
|
||||||
|
if _, ok := v[dname]; ok {
|
||||||
|
switch dvalue := dvalue.(type) {
|
||||||
|
case *Schema:
|
||||||
|
if err := validateInplace(dvalue, "dependencies/"+escape(dname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
case []string:
|
||||||
|
for i, pname := range dvalue {
|
||||||
|
if _, ok := v[pname]; !ok {
|
||||||
|
errors = append(errors, validationError("dependencies/"+escape(dname)+"/"+strconv.Itoa(i), "property %s is required, if %s property exists", quote(pname), quote(dname)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for dname, dvalue := range s.DependentRequired {
|
||||||
|
if _, ok := v[dname]; ok {
|
||||||
|
for i, pname := range dvalue {
|
||||||
|
if _, ok := v[pname]; !ok {
|
||||||
|
errors = append(errors, validationError("dependentRequired/"+escape(dname)+"/"+strconv.Itoa(i), "property %s is required, if %s property exists", quote(pname), quote(dname)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for dname, sch := range s.DependentSchemas {
|
||||||
|
if _, ok := v[dname]; ok {
|
||||||
|
if err := validateInplace(sch, "dependentSchemas/"+escape(dname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case []interface{}:
|
||||||
|
if s.MinItems != -1 && len(v) < s.MinItems {
|
||||||
|
errors = append(errors, validationError("minItems", "minimum %d items required, but found %d items", s.MinItems, len(v)))
|
||||||
|
}
|
||||||
|
if s.MaxItems != -1 && len(v) > s.MaxItems {
|
||||||
|
errors = append(errors, validationError("maxItems", "maximum %d items required, but found %d items", s.MaxItems, len(v)))
|
||||||
|
}
|
||||||
|
if s.UniqueItems {
|
||||||
|
if len(v) <= 20 {
|
||||||
|
outer1:
|
||||||
|
for i := 1; i < len(v); i++ {
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
if equals(v[i], v[j]) {
|
||||||
|
errors = append(errors, validationError("uniqueItems", "items at index %d and %d are equal", j, i))
|
||||||
|
break outer1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m := make(map[uint64][]int)
|
||||||
|
var h maphash.Hash
|
||||||
|
outer2:
|
||||||
|
for i, item := range v {
|
||||||
|
h.Reset()
|
||||||
|
hash(item, &h)
|
||||||
|
k := h.Sum64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
arr, ok := m[k]
|
||||||
|
if ok {
|
||||||
|
for _, j := range arr {
|
||||||
|
if equals(v[j], item) {
|
||||||
|
errors = append(errors, validationError("uniqueItems", "items at index %d and %d are equal", j, i))
|
||||||
|
break outer2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr = append(arr, i)
|
||||||
|
m[k] = arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// items + additionalItems
|
||||||
|
switch items := s.Items.(type) {
|
||||||
|
case *Schema:
|
||||||
|
for i, item := range v {
|
||||||
|
if err := validate(items, "items", item, strconv.Itoa(i)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.unevalItems = nil
|
||||||
|
case []*Schema:
|
||||||
|
for i, item := range v {
|
||||||
|
if i < len(items) {
|
||||||
|
delete(result.unevalItems, i)
|
||||||
|
if err := validate(items[i], "items/"+strconv.Itoa(i), item, strconv.Itoa(i)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
} else if sch, ok := s.AdditionalItems.(*Schema); ok {
|
||||||
|
delete(result.unevalItems, i)
|
||||||
|
if err := validate(sch, "additionalItems", item, strconv.Itoa(i)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if additionalItems, ok := s.AdditionalItems.(bool); ok {
|
||||||
|
if additionalItems {
|
||||||
|
result.unevalItems = nil
|
||||||
|
} else if len(v) > len(items) {
|
||||||
|
errors = append(errors, validationError("additionalItems", "only %d items are allowed, but found %d items", len(items), len(v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefixItems + items
|
||||||
|
for i, item := range v {
|
||||||
|
if i < len(s.PrefixItems) {
|
||||||
|
delete(result.unevalItems, i)
|
||||||
|
if err := validate(s.PrefixItems[i], "prefixItems/"+strconv.Itoa(i), item, strconv.Itoa(i)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
} else if s.Items2020 != nil {
|
||||||
|
delete(result.unevalItems, i)
|
||||||
|
if err := validate(s.Items2020, "items", item, strconv.Itoa(i)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains + minContains + maxContains
|
||||||
|
if s.Contains != nil && (s.MinContains != -1 || s.MaxContains != -1) {
|
||||||
|
matched := 0
|
||||||
|
var causes []error
|
||||||
|
for i, item := range v {
|
||||||
|
if err := validate(s.Contains, "contains", item, strconv.Itoa(i)); err != nil {
|
||||||
|
causes = append(causes, err)
|
||||||
|
} else {
|
||||||
|
matched++
|
||||||
|
if s.ContainsEval {
|
||||||
|
delete(result.unevalItems, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.MinContains != -1 && matched < s.MinContains {
|
||||||
|
errors = append(errors, validationError("minContains", "valid must be >= %d, but got %d", s.MinContains, matched).add(causes...))
|
||||||
|
}
|
||||||
|
if s.MaxContains != -1 && matched > s.MaxContains {
|
||||||
|
errors = append(errors, validationError("maxContains", "valid must be <= %d, but got %d", s.MaxContains, matched))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case string:
|
||||||
|
// minLength + maxLength
|
||||||
|
if s.MinLength != -1 || s.MaxLength != -1 {
|
||||||
|
length := utf8.RuneCount([]byte(v))
|
||||||
|
if s.MinLength != -1 && length < s.MinLength {
|
||||||
|
errors = append(errors, validationError("minLength", "length must be >= %d, but got %d", s.MinLength, length))
|
||||||
|
}
|
||||||
|
if s.MaxLength != -1 && length > s.MaxLength {
|
||||||
|
errors = append(errors, validationError("maxLength", "length must be <= %d, but got %d", s.MaxLength, length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Pattern != nil && !s.Pattern.MatchString(v) {
|
||||||
|
errors = append(errors, validationError("pattern", "does not match pattern %s", quote(s.Pattern.String())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// contentEncoding + contentMediaType
|
||||||
|
if s.decoder != nil || s.mediaType != nil {
|
||||||
|
decoded := s.ContentEncoding == ""
|
||||||
|
var content []byte
|
||||||
|
if s.decoder != nil {
|
||||||
|
b, err := s.decoder(v)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, validationError("contentEncoding", "value is not %s encoded", s.ContentEncoding))
|
||||||
|
} else {
|
||||||
|
content, decoded = b, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if decoded && s.mediaType != nil {
|
||||||
|
if s.decoder == nil {
|
||||||
|
content = []byte(v)
|
||||||
|
}
|
||||||
|
if err := s.mediaType(content); err != nil {
|
||||||
|
errors = append(errors, validationError("contentMediaType", "value is not of mediatype %s", quote(s.ContentMediaType)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if decoded && s.ContentSchema != nil {
|
||||||
|
contentJSON, err := unmarshal(bytes.NewReader(content))
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, validationError("contentSchema", "value is not valid json"))
|
||||||
|
} else {
|
||||||
|
err := validate(s.ContentSchema, "contentSchema", contentJSON, "")
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case json.Number, float32, float64, int, int8, int32, int64, uint, uint8, uint32, uint64:
|
||||||
|
// lazy convert to *big.Rat to avoid allocation
|
||||||
|
var numVal *big.Rat
|
||||||
|
num := func() *big.Rat {
|
||||||
|
if numVal == nil {
|
||||||
|
numVal, _ = new(big.Rat).SetString(fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
return numVal
|
||||||
|
}
|
||||||
|
f64 := func(r *big.Rat) float64 {
|
||||||
|
f, _ := r.Float64()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
if s.Minimum != nil && num().Cmp(s.Minimum) < 0 {
|
||||||
|
errors = append(errors, validationError("minimum", "must be >= %v but found %v", f64(s.Minimum), v))
|
||||||
|
}
|
||||||
|
if s.ExclusiveMinimum != nil && num().Cmp(s.ExclusiveMinimum) <= 0 {
|
||||||
|
errors = append(errors, validationError("exclusiveMinimum", "must be > %v but found %v", f64(s.ExclusiveMinimum), v))
|
||||||
|
}
|
||||||
|
if s.Maximum != nil && num().Cmp(s.Maximum) > 0 {
|
||||||
|
errors = append(errors, validationError("maximum", "must be <= %v but found %v", f64(s.Maximum), v))
|
||||||
|
}
|
||||||
|
if s.ExclusiveMaximum != nil && num().Cmp(s.ExclusiveMaximum) >= 0 {
|
||||||
|
errors = append(errors, validationError("exclusiveMaximum", "must be < %v but found %v", f64(s.ExclusiveMaximum), v))
|
||||||
|
}
|
||||||
|
if s.MultipleOf != nil {
|
||||||
|
if q := new(big.Rat).Quo(num(), s.MultipleOf); !q.IsInt() {
|
||||||
|
errors = append(errors, validationError("multipleOf", "%v not multipleOf %v", v, f64(s.MultipleOf)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $ref + $recursiveRef + $dynamicRef
|
||||||
|
validateRef := func(sch *Schema, refPath string) error {
|
||||||
|
if sch != nil {
|
||||||
|
if err := validateInplace(sch, refPath); err != nil {
|
||||||
|
var url = sch.Location
|
||||||
|
if s.url() == sch.url() {
|
||||||
|
url = sch.loc()
|
||||||
|
}
|
||||||
|
return validationError(refPath, "doesn't validate with %s", quote(url)).causes(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := validateRef(s.Ref, "$ref"); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
if s.RecursiveRef != nil {
|
||||||
|
sch := s.RecursiveRef
|
||||||
|
if sch.RecursiveAnchor {
|
||||||
|
// recursiveRef based on scope
|
||||||
|
for _, e := range scope {
|
||||||
|
if e.schema.RecursiveAnchor {
|
||||||
|
sch = e.schema
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := validateRef(sch, "$recursiveRef"); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.DynamicRef != nil {
|
||||||
|
sch := s.DynamicRef
|
||||||
|
if s.dynamicRefAnchor != "" && sch.DynamicAnchor == s.dynamicRefAnchor {
|
||||||
|
// dynamicRef based on scope
|
||||||
|
for i := len(scope) - 1; i >= 0; i-- {
|
||||||
|
sr := scope[i]
|
||||||
|
if sr.discard {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, da := range sr.schema.dynamicAnchors {
|
||||||
|
if da.DynamicAnchor == s.DynamicRef.DynamicAnchor && da != s.DynamicRef {
|
||||||
|
sch = da
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := validateRef(sch, "$dynamicRef"); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Not != nil && validateInplace(s.Not, "not") == nil {
|
||||||
|
errors = append(errors, validationError("not", "not failed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, sch := range s.AllOf {
|
||||||
|
schPath := "allOf/" + strconv.Itoa(i)
|
||||||
|
if err := validateInplace(sch, schPath); err != nil {
|
||||||
|
errors = append(errors, validationError(schPath, "allOf failed").add(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.AnyOf) > 0 {
|
||||||
|
matched := false
|
||||||
|
var causes []error
|
||||||
|
for i, sch := range s.AnyOf {
|
||||||
|
if err := validateInplace(sch, "anyOf/"+strconv.Itoa(i)); err == nil {
|
||||||
|
matched = true
|
||||||
|
} else {
|
||||||
|
causes = append(causes, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
errors = append(errors, validationError("anyOf", "anyOf failed").add(causes...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.OneOf) > 0 {
|
||||||
|
matched := -1
|
||||||
|
var causes []error
|
||||||
|
for i, sch := range s.OneOf {
|
||||||
|
if err := validateInplace(sch, "oneOf/"+strconv.Itoa(i)); err == nil {
|
||||||
|
if matched == -1 {
|
||||||
|
matched = i
|
||||||
|
} else {
|
||||||
|
errors = append(errors, validationError("oneOf", "valid against schemas at indexes %d and %d", matched, i))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
causes = append(causes, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched == -1 {
|
||||||
|
errors = append(errors, validationError("oneOf", "oneOf failed").add(causes...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if + then + else
|
||||||
|
if s.If != nil {
|
||||||
|
err := validateInplace(s.If, "if")
|
||||||
|
// "if" leaves dynamic scope
|
||||||
|
scope[len(scope)-1].discard = true
|
||||||
|
if err == nil {
|
||||||
|
if s.Then != nil {
|
||||||
|
if err := validateInplace(s.Then, "then"); err != nil {
|
||||||
|
errors = append(errors, validationError("then", "if-then failed").add(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s.Else != nil {
|
||||||
|
if err := validateInplace(s.Else, "else"); err != nil {
|
||||||
|
errors = append(errors, validationError("else", "if-else failed").add(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// restore dynamic scope
|
||||||
|
scope[len(scope)-1].discard = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ext := range s.Extensions {
|
||||||
|
if err := ext.Validate(ValidationContext{result, validate, validateInplace, validationError}, v); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unevaluatedProperties + unevaluatedItems
|
||||||
|
switch v := v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
if s.UnevaluatedProperties != nil {
|
||||||
|
for pname := range result.unevalProps {
|
||||||
|
if pvalue, ok := v[pname]; ok {
|
||||||
|
if err := validate(s.UnevaluatedProperties, "unevaluatedProperties", pvalue, escape(pname)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.unevalProps = nil
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
if s.UnevaluatedItems != nil {
|
||||||
|
for i := range result.unevalItems {
|
||||||
|
if err := validate(s.UnevaluatedItems, "unevaluatedItems", v[i], strconv.Itoa(i)); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.unevalItems = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(errors) {
|
||||||
|
case 0:
|
||||||
|
return result, nil
|
||||||
|
case 1:
|
||||||
|
return result, errors[0]
|
||||||
|
default:
|
||||||
|
return result, validationError("", "").add(errors...) // empty message, used just for wrapping
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type validationResult struct {
|
||||||
|
unevalProps map[string]struct{}
|
||||||
|
unevalItems map[int]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vr validationResult) unevalPnames() string {
|
||||||
|
pnames := make([]string, 0, len(vr.unevalProps))
|
||||||
|
for pname := range vr.unevalProps {
|
||||||
|
pnames = append(pnames, quote(pname))
|
||||||
|
}
|
||||||
|
return strings.Join(pnames, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsonType returns the json type of given value v.
|
||||||
|
//
|
||||||
|
// It panics if the given value is not valid json value
|
||||||
|
func jsonType(v interface{}) string {
|
||||||
|
switch v.(type) {
|
||||||
|
case nil:
|
||||||
|
return "null"
|
||||||
|
case bool:
|
||||||
|
return "boolean"
|
||||||
|
case json.Number, float32, float64, int, int8, int32, int64, uint, uint8, uint32, uint64:
|
||||||
|
return "number"
|
||||||
|
case string:
|
||||||
|
return "string"
|
||||||
|
case []interface{}:
|
||||||
|
return "array"
|
||||||
|
case map[string]interface{}:
|
||||||
|
return "object"
|
||||||
|
}
|
||||||
|
panic(InvalidJSONTypeError(fmt.Sprintf("%T", v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// equals tells if given two json values are equal or not.
|
||||||
|
func equals(v1, v2 interface{}) bool {
|
||||||
|
v1Type := jsonType(v1)
|
||||||
|
if v1Type != jsonType(v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch v1Type {
|
||||||
|
case "array":
|
||||||
|
arr1, arr2 := v1.([]interface{}), v2.([]interface{})
|
||||||
|
if len(arr1) != len(arr2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range arr1 {
|
||||||
|
if !equals(arr1[i], arr2[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case "object":
|
||||||
|
obj1, obj2 := v1.(map[string]interface{}), v2.(map[string]interface{})
|
||||||
|
if len(obj1) != len(obj2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for k, v1 := range obj1 {
|
||||||
|
if v2, ok := obj2[k]; ok {
|
||||||
|
if !equals(v1, v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case "number":
|
||||||
|
num1, _ := new(big.Rat).SetString(fmt.Sprint(v1))
|
||||||
|
num2, _ := new(big.Rat).SetString(fmt.Sprint(v2))
|
||||||
|
return num1.Cmp(num2) == 0
|
||||||
|
default:
|
||||||
|
return v1 == v2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(v interface{}, h *maphash.Hash) {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
h.WriteByte(0)
|
||||||
|
case bool:
|
||||||
|
h.WriteByte(1)
|
||||||
|
if v {
|
||||||
|
h.WriteByte(1)
|
||||||
|
} else {
|
||||||
|
h.WriteByte(0)
|
||||||
|
}
|
||||||
|
case json.Number, float32, float64, int, int8, int32, int64, uint, uint8, uint32, uint64:
|
||||||
|
h.WriteByte(2)
|
||||||
|
num, _ := new(big.Rat).SetString(fmt.Sprint(v))
|
||||||
|
h.Write(num.Num().Bytes())
|
||||||
|
h.Write(num.Denom().Bytes())
|
||||||
|
case string:
|
||||||
|
h.WriteByte(3)
|
||||||
|
h.WriteString(v)
|
||||||
|
case []interface{}:
|
||||||
|
h.WriteByte(4)
|
||||||
|
for _, item := range v {
|
||||||
|
hash(item, h)
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
h.WriteByte(5)
|
||||||
|
props := make([]string, 0, len(v))
|
||||||
|
for prop := range v {
|
||||||
|
props = append(props, prop)
|
||||||
|
}
|
||||||
|
sort.Slice(props, func(i, j int) bool {
|
||||||
|
return props[i] < props[j]
|
||||||
|
})
|
||||||
|
for _, prop := range props {
|
||||||
|
hash(prop, h)
|
||||||
|
hash(v[prop], h)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(InvalidJSONTypeError(fmt.Sprintf("%T", v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape converts given token to valid json-pointer token
|
||||||
|
func escape(token string) string {
|
||||||
|
token = strings.ReplaceAll(token, "~", "~0")
|
||||||
|
token = strings.ReplaceAll(token, "/", "~1")
|
||||||
|
return url.PathEscape(token)
|
||||||
|
}
|
202
vendor/github.com/xeipuuv/gojsonpointer/LICENSE-APACHE-2.0.txt
generated
vendored
202
vendor/github.com/xeipuuv/gojsonpointer/LICENSE-APACHE-2.0.txt
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2015 xeipuuv
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
41
vendor/github.com/xeipuuv/gojsonpointer/README.md
generated
vendored
41
vendor/github.com/xeipuuv/gojsonpointer/README.md
generated
vendored
@ -1,41 +0,0 @@
|
|||||||
# gojsonpointer
|
|
||||||
An implementation of JSON Pointer - Go language
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
jsonText := `{
|
|
||||||
"name": "Bobby B",
|
|
||||||
"occupation": {
|
|
||||||
"title" : "King",
|
|
||||||
"years" : 15,
|
|
||||||
"heir" : "Joffrey B"
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
var jsonDocument map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(jsonText), &jsonDocument)
|
|
||||||
|
|
||||||
//create a JSON pointer
|
|
||||||
pointerString := "/occupation/title"
|
|
||||||
pointer, _ := NewJsonPointer(pointerString)
|
|
||||||
|
|
||||||
//SET a new value for the "title" in the document
|
|
||||||
pointer.Set(jsonDocument, "Supreme Leader of Westeros")
|
|
||||||
|
|
||||||
//GET the new "title" from the document
|
|
||||||
title, _, _ := pointer.Get(jsonDocument)
|
|
||||||
fmt.Println(title) //outputs "Supreme Leader of Westeros"
|
|
||||||
|
|
||||||
//DELETE the "heir" from the document
|
|
||||||
deletePointer := NewJsonPointer("/occupation/heir")
|
|
||||||
deletePointer.Delete(jsonDocument)
|
|
||||||
|
|
||||||
b, _ := json.Marshal(jsonDocument)
|
|
||||||
fmt.Println(string(b))
|
|
||||||
//outputs `{"name":"Bobby B","occupation":{"title":"Supreme Leader of Westeros","years":15}}`
|
|
||||||
|
|
||||||
|
|
||||||
## References
|
|
||||||
https://tools.ietf.org/html/rfc6901
|
|
||||||
|
|
||||||
### Note
|
|
||||||
The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented.
|
|
211
vendor/github.com/xeipuuv/gojsonpointer/pointer.go
generated
vendored
211
vendor/github.com/xeipuuv/gojsonpointer/pointer.go
generated
vendored
@ -1,211 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonpointer
|
|
||||||
// repository-desc An implementation of JSON Pointer - Go language
|
|
||||||
//
|
|
||||||
// description Main and unique file.
|
|
||||||
//
|
|
||||||
// created 25-02-2013
|
|
||||||
|
|
||||||
package gojsonpointer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
const_empty_pointer = ``
|
|
||||||
const_pointer_separator = `/`
|
|
||||||
|
|
||||||
const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
|
|
||||||
)
|
|
||||||
|
|
||||||
type implStruct struct {
|
|
||||||
mode string // "SET" or "GET"
|
|
||||||
|
|
||||||
inDocument interface{}
|
|
||||||
|
|
||||||
setInValue interface{}
|
|
||||||
|
|
||||||
getOutNode interface{}
|
|
||||||
getOutKind reflect.Kind
|
|
||||||
outError error
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonPointer struct {
|
|
||||||
referenceTokens []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewJsonPointer parses the given string JSON pointer and returns an object
|
|
||||||
func NewJsonPointer(jsonPointerString string) (p JsonPointer, err error) {
|
|
||||||
|
|
||||||
// Pointer to the root of the document
|
|
||||||
if len(jsonPointerString) == 0 {
|
|
||||||
// Keep referenceTokens nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if jsonPointerString[0] != '/' {
|
|
||||||
return p, errors.New(const_invalid_start)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.referenceTokens = strings.Split(jsonPointerString[1:], const_pointer_separator)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the pointer to retrieve a value from a JSON document
|
|
||||||
func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
|
|
||||||
|
|
||||||
is := &implStruct{mode: "GET", inDocument: document}
|
|
||||||
p.implementation(is)
|
|
||||||
return is.getOutNode, is.getOutKind, is.outError
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the pointer to update a value from a JSON document
|
|
||||||
func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
|
|
||||||
|
|
||||||
is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
|
|
||||||
p.implementation(is)
|
|
||||||
return document, is.outError
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the pointer to delete a value from a JSON document
|
|
||||||
func (p *JsonPointer) Delete(document interface{}) (interface{}, error) {
|
|
||||||
is := &implStruct{mode: "DEL", inDocument: document}
|
|
||||||
p.implementation(is)
|
|
||||||
return document, is.outError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both Get and Set functions use the same implementation to avoid code duplication
|
|
||||||
func (p *JsonPointer) implementation(i *implStruct) {
|
|
||||||
|
|
||||||
kind := reflect.Invalid
|
|
||||||
|
|
||||||
// Full document when empty
|
|
||||||
if len(p.referenceTokens) == 0 {
|
|
||||||
i.getOutNode = i.inDocument
|
|
||||||
i.outError = nil
|
|
||||||
i.getOutKind = kind
|
|
||||||
i.outError = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
node := i.inDocument
|
|
||||||
|
|
||||||
previousNodes := make([]interface{}, len(p.referenceTokens))
|
|
||||||
previousTokens := make([]string, len(p.referenceTokens))
|
|
||||||
|
|
||||||
for ti, token := range p.referenceTokens {
|
|
||||||
|
|
||||||
isLastToken := ti == len(p.referenceTokens)-1
|
|
||||||
previousNodes[ti] = node
|
|
||||||
previousTokens[ti] = token
|
|
||||||
|
|
||||||
switch v := node.(type) {
|
|
||||||
|
|
||||||
case map[string]interface{}:
|
|
||||||
decodedToken := decodeReferenceToken(token)
|
|
||||||
if _, ok := v[decodedToken]; ok {
|
|
||||||
node = v[decodedToken]
|
|
||||||
if isLastToken && i.mode == "SET" {
|
|
||||||
v[decodedToken] = i.setInValue
|
|
||||||
} else if isLastToken && i.mode == "DEL" {
|
|
||||||
delete(v, decodedToken)
|
|
||||||
}
|
|
||||||
} else if isLastToken && i.mode == "SET" {
|
|
||||||
v[decodedToken] = i.setInValue
|
|
||||||
} else {
|
|
||||||
i.outError = fmt.Errorf("Object has no key '%s'", decodedToken)
|
|
||||||
i.getOutKind = reflect.Map
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case []interface{}:
|
|
||||||
tokenIndex, err := strconv.Atoi(token)
|
|
||||||
if err != nil {
|
|
||||||
i.outError = fmt.Errorf("Invalid array index '%s'", token)
|
|
||||||
i.getOutKind = reflect.Slice
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if tokenIndex < 0 || tokenIndex >= len(v) {
|
|
||||||
i.outError = fmt.Errorf("Out of bound array[0,%d] index '%d'", len(v), tokenIndex)
|
|
||||||
i.getOutKind = reflect.Slice
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
node = v[tokenIndex]
|
|
||||||
if isLastToken && i.mode == "SET" {
|
|
||||||
v[tokenIndex] = i.setInValue
|
|
||||||
} else if isLastToken && i.mode == "DEL" {
|
|
||||||
v[tokenIndex] = v[len(v)-1]
|
|
||||||
v[len(v)-1] = nil
|
|
||||||
v = v[:len(v)-1]
|
|
||||||
previousNodes[ti-1].(map[string]interface{})[previousTokens[ti-1]] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
i.outError = fmt.Errorf("Invalid token reference '%s'", token)
|
|
||||||
i.getOutKind = reflect.ValueOf(node).Kind()
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
i.getOutNode = node
|
|
||||||
i.getOutKind = reflect.ValueOf(node).Kind()
|
|
||||||
i.outError = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pointer to string representation function
|
|
||||||
func (p *JsonPointer) String() string {
|
|
||||||
|
|
||||||
if len(p.referenceTokens) == 0 {
|
|
||||||
return const_empty_pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
|
|
||||||
|
|
||||||
return pointerString
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific JSON pointer encoding here
|
|
||||||
// ~0 => ~
|
|
||||||
// ~1 => /
|
|
||||||
// ... and vice versa
|
|
||||||
|
|
||||||
func decodeReferenceToken(token string) string {
|
|
||||||
step1 := strings.Replace(token, `~1`, `/`, -1)
|
|
||||||
step2 := strings.Replace(step1, `~0`, `~`, -1)
|
|
||||||
return step2
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeReferenceToken(token string) string {
|
|
||||||
step1 := strings.Replace(token, `~`, `~0`, -1)
|
|
||||||
step2 := strings.Replace(step1, `/`, `~1`, -1)
|
|
||||||
return step2
|
|
||||||
}
|
|
10
vendor/github.com/xeipuuv/gojsonreference/README.md
generated
vendored
10
vendor/github.com/xeipuuv/gojsonreference/README.md
generated
vendored
@ -1,10 +0,0 @@
|
|||||||
# gojsonreference
|
|
||||||
An implementation of JSON Reference - Go language
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
https://github.com/xeipuuv/gojsonpointer
|
|
||||||
|
|
||||||
## References
|
|
||||||
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
|
|
||||||
|
|
||||||
http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
|
|
147
vendor/github.com/xeipuuv/gojsonreference/reference.go
generated
vendored
147
vendor/github.com/xeipuuv/gojsonreference/reference.go
generated
vendored
@ -1,147 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonreference
|
|
||||||
// repository-desc An implementation of JSON Reference - Go language
|
|
||||||
//
|
|
||||||
// description Main and unique file.
|
|
||||||
//
|
|
||||||
// created 26-02-2013
|
|
||||||
|
|
||||||
package gojsonreference
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonpointer"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
const_fragment_char = `#`
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewJsonReference(jsonReferenceString string) (JsonReference, error) {
|
|
||||||
|
|
||||||
var r JsonReference
|
|
||||||
err := r.parse(jsonReferenceString)
|
|
||||||
return r, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonReference struct {
|
|
||||||
referenceUrl *url.URL
|
|
||||||
referencePointer gojsonpointer.JsonPointer
|
|
||||||
|
|
||||||
HasFullUrl bool
|
|
||||||
HasUrlPathOnly bool
|
|
||||||
HasFragmentOnly bool
|
|
||||||
HasFileScheme bool
|
|
||||||
HasFullFilePath bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *JsonReference) GetUrl() *url.URL {
|
|
||||||
return r.referenceUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *JsonReference) GetPointer() *gojsonpointer.JsonPointer {
|
|
||||||
return &r.referencePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *JsonReference) String() string {
|
|
||||||
|
|
||||||
if r.referenceUrl != nil {
|
|
||||||
return r.referenceUrl.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.HasFragmentOnly {
|
|
||||||
return const_fragment_char + r.referencePointer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.referencePointer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *JsonReference) IsCanonical() bool {
|
|
||||||
return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Constructor", parses the given string JSON reference
|
|
||||||
func (r *JsonReference) parse(jsonReferenceString string) (err error) {
|
|
||||||
|
|
||||||
r.referenceUrl, err = url.Parse(jsonReferenceString)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
refUrl := r.referenceUrl
|
|
||||||
|
|
||||||
if refUrl.Scheme != "" && refUrl.Host != "" {
|
|
||||||
r.HasFullUrl = true
|
|
||||||
} else {
|
|
||||||
if refUrl.Path != "" {
|
|
||||||
r.HasUrlPathOnly = true
|
|
||||||
} else if refUrl.RawQuery == "" && refUrl.Fragment != "" {
|
|
||||||
r.HasFragmentOnly = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.HasFileScheme = refUrl.Scheme == "file"
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// on Windows, a file URL may have an extra leading slash, and if it
|
|
||||||
// doesn't then its first component will be treated as the host by the
|
|
||||||
// Go runtime
|
|
||||||
if refUrl.Host == "" && strings.HasPrefix(refUrl.Path, "/") {
|
|
||||||
r.HasFullFilePath = filepath.IsAbs(refUrl.Path[1:])
|
|
||||||
} else {
|
|
||||||
r.HasFullFilePath = filepath.IsAbs(refUrl.Host + refUrl.Path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r.HasFullFilePath = filepath.IsAbs(refUrl.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalid json-pointer error means url has no json-pointer fragment. simply ignore error
|
|
||||||
r.referencePointer, _ = gojsonpointer.NewJsonPointer(refUrl.Fragment)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new reference from a parent and a child
|
|
||||||
// If the child cannot inherit from the parent, an error is returned
|
|
||||||
func (r *JsonReference) Inherits(child JsonReference) (*JsonReference, error) {
|
|
||||||
if child.GetUrl() == nil {
|
|
||||||
return nil, errors.New("childUrl is nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.GetUrl() == nil {
|
|
||||||
return nil, errors.New("parentUrl is nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a copy of the parent url to make sure we do not modify the original.
|
|
||||||
// URL reference resolving fails if the fragment of the child is empty, but the parent's is not.
|
|
||||||
// The fragment of the child must be used, so the fragment of the parent is manually removed.
|
|
||||||
parentUrl := *r.GetUrl()
|
|
||||||
parentUrl.Fragment = ""
|
|
||||||
|
|
||||||
ref, err := NewJsonReference(parentUrl.ResolveReference(child.GetUrl()).String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ref, err
|
|
||||||
}
|
|
3
vendor/github.com/xeipuuv/gojsonschema/.gitignore
generated
vendored
3
vendor/github.com/xeipuuv/gojsonschema/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
*.sw[nop]
|
|
||||||
*.iml
|
|
||||||
.vscode/
|
|
9
vendor/github.com/xeipuuv/gojsonschema/.travis.yml
generated
vendored
9
vendor/github.com/xeipuuv/gojsonschema/.travis.yml
generated
vendored
@ -1,9 +0,0 @@
|
|||||||
language: go
|
|
||||||
go:
|
|
||||||
- "1.11"
|
|
||||||
- "1.12"
|
|
||||||
- "1.13"
|
|
||||||
before_install:
|
|
||||||
- go get github.com/xeipuuv/gojsonreference
|
|
||||||
- go get github.com/xeipuuv/gojsonpointer
|
|
||||||
- go get github.com/stretchr/testify/assert
|
|
202
vendor/github.com/xeipuuv/gojsonschema/LICENSE-APACHE-2.0.txt
generated
vendored
202
vendor/github.com/xeipuuv/gojsonschema/LICENSE-APACHE-2.0.txt
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2015 xeipuuv
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
466
vendor/github.com/xeipuuv/gojsonschema/README.md
generated
vendored
466
vendor/github.com/xeipuuv/gojsonschema/README.md
generated
vendored
@ -1,466 +0,0 @@
|
|||||||
[](https://godoc.org/github.com/xeipuuv/gojsonschema)
|
|
||||||
[](https://travis-ci.org/xeipuuv/gojsonschema)
|
|
||||||
[](https://goreportcard.com/report/github.com/xeipuuv/gojsonschema)
|
|
||||||
|
|
||||||
# gojsonschema
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
An implementation of JSON Schema for the Go programming language. Supports draft-04, draft-06 and draft-07.
|
|
||||||
|
|
||||||
References :
|
|
||||||
|
|
||||||
* http://json-schema.org
|
|
||||||
* http://json-schema.org/latest/json-schema-core.html
|
|
||||||
* http://json-schema.org/latest/json-schema-validation.html
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```
|
|
||||||
go get github.com/xeipuuv/gojsonschema
|
|
||||||
```
|
|
||||||
|
|
||||||
Dependencies :
|
|
||||||
* [github.com/xeipuuv/gojsonpointer](https://github.com/xeipuuv/gojsonpointer)
|
|
||||||
* [github.com/xeipuuv/gojsonreference](https://github.com/xeipuuv/gojsonreference)
|
|
||||||
* [github.com/stretchr/testify/assert](https://github.com/stretchr/testify#assert-package)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```go
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/xeipuuv/gojsonschema"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
schemaLoader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
|
|
||||||
documentLoader := gojsonschema.NewReferenceLoader("file:///home/me/document.json")
|
|
||||||
|
|
||||||
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Valid() {
|
|
||||||
fmt.Printf("The document is valid\n")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("The document is not valid. see errors :\n")
|
|
||||||
for _, desc := range result.Errors() {
|
|
||||||
fmt.Printf("- %s\n", desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Loaders
|
|
||||||
|
|
||||||
There are various ways to load your JSON data.
|
|
||||||
In order to load your schemas and documents,
|
|
||||||
first declare an appropriate loader :
|
|
||||||
|
|
||||||
* Web / HTTP, using a reference :
|
|
||||||
|
|
||||||
```go
|
|
||||||
loader := gojsonschema.NewReferenceLoader("http://www.some_host.com/schema.json")
|
|
||||||
```
|
|
||||||
|
|
||||||
* Local file, using a reference :
|
|
||||||
|
|
||||||
```go
|
|
||||||
loader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
|
|
||||||
```
|
|
||||||
|
|
||||||
References use the URI scheme, the prefix (file://) and a full path to the file are required.
|
|
||||||
|
|
||||||
* JSON strings :
|
|
||||||
|
|
||||||
```go
|
|
||||||
loader := gojsonschema.NewStringLoader(`{"type": "string"}`)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Custom Go types :
|
|
||||||
|
|
||||||
```go
|
|
||||||
m := map[string]interface{}{"type": "string"}
|
|
||||||
loader := gojsonschema.NewGoLoader(m)
|
|
||||||
```
|
|
||||||
|
|
||||||
And
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Root struct {
|
|
||||||
Users []User `json:"users"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
data := Root{}
|
|
||||||
data.Users = append(data.Users, User{"John"})
|
|
||||||
data.Users = append(data.Users, User{"Sophia"})
|
|
||||||
data.Users = append(data.Users, User{"Bill"})
|
|
||||||
|
|
||||||
loader := gojsonschema.NewGoLoader(data)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Validation
|
|
||||||
|
|
||||||
Once the loaders are set, validation is easy :
|
|
||||||
|
|
||||||
```go
|
|
||||||
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, you might want to load a schema only once and process to multiple validations :
|
|
||||||
|
|
||||||
```go
|
|
||||||
schema, err := gojsonschema.NewSchema(schemaLoader)
|
|
||||||
...
|
|
||||||
result1, err := schema.Validate(documentLoader1)
|
|
||||||
...
|
|
||||||
result2, err := schema.Validate(documentLoader2)
|
|
||||||
...
|
|
||||||
// etc ...
|
|
||||||
```
|
|
||||||
|
|
||||||
To check the result :
|
|
||||||
|
|
||||||
```go
|
|
||||||
if result.Valid() {
|
|
||||||
fmt.Printf("The document is valid\n")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("The document is not valid. see errors :\n")
|
|
||||||
for _, err := range result.Errors() {
|
|
||||||
// Err implements the ResultError interface
|
|
||||||
fmt.Printf("- %s\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Loading local schemas
|
|
||||||
|
|
||||||
By default `file` and `http(s)` references to external schemas are loaded automatically via the file system or via http(s). An external schema can also be loaded using a `SchemaLoader`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
sl := gojsonschema.NewSchemaLoader()
|
|
||||||
loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`)
|
|
||||||
err := sl.AddSchema("http://some_host.com/string.json", loader1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively if your schema already has an `$id` you can use the `AddSchemas` function
|
|
||||||
```go
|
|
||||||
loader2 := gojsonschema.NewStringLoader(`{
|
|
||||||
"$id" : "http://some_host.com/maxlength.json",
|
|
||||||
"maxLength" : 5
|
|
||||||
}`)
|
|
||||||
err = sl.AddSchemas(loader2)
|
|
||||||
```
|
|
||||||
|
|
||||||
The main schema should be passed to the `Compile` function. This main schema can then directly reference the added schemas without needing to download them.
|
|
||||||
```go
|
|
||||||
loader3 := gojsonschema.NewStringLoader(`{
|
|
||||||
"$id" : "http://some_host.com/main.json",
|
|
||||||
"allOf" : [
|
|
||||||
{ "$ref" : "http://some_host.com/string.json" },
|
|
||||||
{ "$ref" : "http://some_host.com/maxlength.json" }
|
|
||||||
]
|
|
||||||
}`)
|
|
||||||
|
|
||||||
schema, err := sl.Compile(loader3)
|
|
||||||
|
|
||||||
documentLoader := gojsonschema.NewStringLoader(`"hello world"`)
|
|
||||||
|
|
||||||
result, err := schema.Validate(documentLoader)
|
|
||||||
```
|
|
||||||
|
|
||||||
It's also possible to pass a `ReferenceLoader` to the `Compile` function that references a loaded schema.
|
|
||||||
|
|
||||||
```go
|
|
||||||
err = sl.AddSchemas(loader3)
|
|
||||||
schema, err := sl.Compile(gojsonschema.NewReferenceLoader("http://some_host.com/main.json"))
|
|
||||||
```
|
|
||||||
|
|
||||||
Schemas added by `AddSchema` and `AddSchemas` are only validated when the entire schema is compiled, unless meta-schema validation is used.
|
|
||||||
|
|
||||||
## Using a specific draft
|
|
||||||
By default `gojsonschema` will try to detect the draft of a schema by using the `$schema` keyword and parse it in a strict draft-04, draft-06 or draft-07 mode. If `$schema` is missing, or the draft version is not explicitely set, a hybrid mode is used which merges together functionality of all drafts into one mode.
|
|
||||||
|
|
||||||
Autodectection can be turned off with the `AutoDetect` property. Specific draft versions can be specified with the `Draft` property.
|
|
||||||
|
|
||||||
```go
|
|
||||||
sl := gojsonschema.NewSchemaLoader()
|
|
||||||
sl.Draft = gojsonschema.Draft7
|
|
||||||
sl.AutoDetect = false
|
|
||||||
```
|
|
||||||
|
|
||||||
If autodetection is on (default), a draft-07 schema can savely reference draft-04 schemas and vice-versa, as long as `$schema` is specified in all schemas.
|
|
||||||
|
|
||||||
## Meta-schema validation
|
|
||||||
Schemas that are added using the `AddSchema`, `AddSchemas` and `Compile` can be validated against their meta-schema by setting the `Validate` property.
|
|
||||||
|
|
||||||
The following example will produce an error as `multipleOf` must be a number. If `Validate` is off (default), this error is only returned at the `Compile` step.
|
|
||||||
|
|
||||||
```go
|
|
||||||
sl := gojsonschema.NewSchemaLoader()
|
|
||||||
sl.Validate = true
|
|
||||||
err := sl.AddSchemas(gojsonschema.NewStringLoader(`{
|
|
||||||
$id" : "http://some_host.com/invalid.json",
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"multipleOf" : true
|
|
||||||
}`))
|
|
||||||
```
|
|
||||||
```
|
|
||||||
```
|
|
||||||
|
|
||||||
Errors returned by meta-schema validation are more readable and contain more information, which helps significantly if you are developing a schema.
|
|
||||||
|
|
||||||
Meta-schema validation also works with a custom `$schema`. In case `$schema` is missing, or `AutoDetect` is set to `false`, the meta-schema of the used draft is used.
|
|
||||||
|
|
||||||
|
|
||||||
## Working with Errors
|
|
||||||
|
|
||||||
The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it
|
|
||||||
```go
|
|
||||||
gojsonschema.Locale = YourCustomLocale{}
|
|
||||||
```
|
|
||||||
|
|
||||||
However, each error contains additional contextual information.
|
|
||||||
|
|
||||||
Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens.
|
|
||||||
|
|
||||||
**err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below
|
|
||||||
|
|
||||||
Note: An error of RequiredType has an err.Type() return value of "required"
|
|
||||||
|
|
||||||
"required": RequiredError
|
|
||||||
"invalid_type": InvalidTypeError
|
|
||||||
"number_any_of": NumberAnyOfError
|
|
||||||
"number_one_of": NumberOneOfError
|
|
||||||
"number_all_of": NumberAllOfError
|
|
||||||
"number_not": NumberNotError
|
|
||||||
"missing_dependency": MissingDependencyError
|
|
||||||
"internal": InternalError
|
|
||||||
"const": ConstEror
|
|
||||||
"enum": EnumError
|
|
||||||
"array_no_additional_items": ArrayNoAdditionalItemsError
|
|
||||||
"array_min_items": ArrayMinItemsError
|
|
||||||
"array_max_items": ArrayMaxItemsError
|
|
||||||
"unique": ItemsMustBeUniqueError
|
|
||||||
"contains" : ArrayContainsError
|
|
||||||
"array_min_properties": ArrayMinPropertiesError
|
|
||||||
"array_max_properties": ArrayMaxPropertiesError
|
|
||||||
"additional_property_not_allowed": AdditionalPropertyNotAllowedError
|
|
||||||
"invalid_property_pattern": InvalidPropertyPatternError
|
|
||||||
"invalid_property_name": InvalidPropertyNameError
|
|
||||||
"string_gte": StringLengthGTEError
|
|
||||||
"string_lte": StringLengthLTEError
|
|
||||||
"pattern": DoesNotMatchPatternError
|
|
||||||
"multiple_of": MultipleOfError
|
|
||||||
"number_gte": NumberGTEError
|
|
||||||
"number_gt": NumberGTError
|
|
||||||
"number_lte": NumberLTEError
|
|
||||||
"number_lt": NumberLTError
|
|
||||||
"condition_then" : ConditionThenError
|
|
||||||
"condition_else" : ConditionElseError
|
|
||||||
|
|
||||||
**err.Value()**: *interface{}* Returns the value given
|
|
||||||
|
|
||||||
**err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName
|
|
||||||
|
|
||||||
**err.Field()**: *string* Returns the fieldname in the format firstName, or for embedded properties, person.firstName. This returns the same as the String() method on *err.Context()* but removes the (root). prefix.
|
|
||||||
|
|
||||||
**err.Description()**: *string* The error description. This is based on the locale you are using. See the beginning of this section for overwriting the locale with a custom implementation.
|
|
||||||
|
|
||||||
**err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result.
|
|
||||||
|
|
||||||
**err.Details()**: *gojsonschema.ErrorDetails* Returns a map[string]interface{} of additional error details specific to the error. For example, GTE errors will have a "min" value, LTE will have a "max" value. See errors.go for a full description of all the error details. Every error always contains a "field" key that holds the value of *err.Field()*
|
|
||||||
|
|
||||||
Note in most cases, the err.Details() will be used to generate replacement strings in your locales, and not used directly. These strings follow the text/template format i.e.
|
|
||||||
```
|
|
||||||
{{.field}} must be greater than or equal to {{.min}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The library allows you to specify custom template functions, should you require more complex error message handling.
|
|
||||||
```go
|
|
||||||
gojsonschema.ErrorTemplateFuncs = map[string]interface{}{
|
|
||||||
"allcaps": func(s string) string {
|
|
||||||
return strings.ToUpper(s)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Given the above definition, you can use the custom function `"allcaps"` in your localization templates:
|
|
||||||
```
|
|
||||||
{{allcaps .field}} must be greater than or equal to {{.min}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The above error message would then be rendered with the `field` value in capital letters. For example:
|
|
||||||
```
|
|
||||||
"PASSWORD must be greater than or equal to 8"
|
|
||||||
```
|
|
||||||
|
|
||||||
Learn more about what types of template functions you can use in `ErrorTemplateFuncs` by referring to Go's [text/template FuncMap](https://golang.org/pkg/text/template/#FuncMap) type.
|
|
||||||
|
|
||||||
## Formats
|
|
||||||
JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this:
|
|
||||||
|
|
||||||
````json
|
|
||||||
{"type": "string", "format": "email"}
|
|
||||||
````
|
|
||||||
|
|
||||||
Not all formats defined in draft-07 are available. Implemented formats are:
|
|
||||||
|
|
||||||
* `date`
|
|
||||||
* `time`
|
|
||||||
* `date-time`
|
|
||||||
* `hostname`. Subdomains that start with a number are also supported, but this means that it doesn't strictly follow [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5) and has the implication that ipv4 addresses are also recognized as valid hostnames.
|
|
||||||
* `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support.
|
|
||||||
* `idn-email`. Same caveat as `email`.
|
|
||||||
* `ipv4`
|
|
||||||
* `ipv6`
|
|
||||||
* `uri`. Includes unicode support.
|
|
||||||
* `uri-reference`. Includes unicode support.
|
|
||||||
* `iri`
|
|
||||||
* `iri-reference`
|
|
||||||
* `uri-template`
|
|
||||||
* `uuid`
|
|
||||||
* `regex`. Go uses the [RE2](https://github.com/google/re2/wiki/Syntax) engine and is not [ECMA262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) compatible.
|
|
||||||
* `json-pointer`
|
|
||||||
* `relative-json-pointer`
|
|
||||||
|
|
||||||
`email`, `uri` and `uri-reference` use the same validation code as their unicode counterparts `idn-email`, `iri` and `iri-reference`. If you rely on unicode support you should use the specific
|
|
||||||
unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats.
|
|
||||||
|
|
||||||
The validation code for `uri`, `idn-email` and their relatives use mostly standard library code.
|
|
||||||
|
|
||||||
For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Define the format checker
|
|
||||||
type RoleFormatChecker struct {}
|
|
||||||
|
|
||||||
// Ensure it meets the gojsonschema.FormatChecker interface
|
|
||||||
func (f RoleFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if ok == false {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.HasPrefix("ROLE_", asString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add it to the library
|
|
||||||
gojsonschema.FormatCheckers.Add("role", RoleFormatChecker{})
|
|
||||||
````
|
|
||||||
|
|
||||||
Now to use in your json schema:
|
|
||||||
````json
|
|
||||||
{"type": "string", "format": "role"}
|
|
||||||
````
|
|
||||||
|
|
||||||
Another example would be to check if the provided integer matches an id on database:
|
|
||||||
|
|
||||||
JSON schema:
|
|
||||||
```json
|
|
||||||
{"type": "integer", "format": "ValidUserId"}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Define the format checker
|
|
||||||
type ValidUserIdFormatChecker struct {}
|
|
||||||
|
|
||||||
// Ensure it meets the gojsonschema.FormatChecker interface
|
|
||||||
func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
|
|
||||||
asFloat64, ok := input.(float64) // Numbers are always float64 here
|
|
||||||
if ok == false {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX
|
|
||||||
// do the magic on the database looking for the int(asFloat64)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add it to the library
|
|
||||||
gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{})
|
|
||||||
````
|
|
||||||
|
|
||||||
Formats can also be removed, for example if you want to override one of the formats that is defined by default.
|
|
||||||
|
|
||||||
```go
|
|
||||||
gojsonschema.FormatCheckers.Remove("hostname")
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Additional custom validation
|
|
||||||
After the validation has run and you have the results, you may add additional
|
|
||||||
errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead
|
|
||||||
of having to add special exceptions for your own errors. Below is an example.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type AnswerInvalidError struct {
|
|
||||||
gojsonschema.ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError {
|
|
||||||
err := AnswerInvalidError{}
|
|
||||||
err.SetContext(context)
|
|
||||||
err.SetType("custom_invalid_error")
|
|
||||||
// it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed
|
|
||||||
// using the description of err will be overridden by this.
|
|
||||||
err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}")
|
|
||||||
err.SetValue(value)
|
|
||||||
err.SetDetails(details)
|
|
||||||
|
|
||||||
return &err
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// ...
|
|
||||||
schema, err := gojsonschema.NewSchema(schemaLoader)
|
|
||||||
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
|
|
||||||
|
|
||||||
if true { // some validation
|
|
||||||
jsonContext := gojsonschema.NewJsonContext("question", nil)
|
|
||||||
errDetail := gojsonschema.ErrorDetails{
|
|
||||||
"answer": 42,
|
|
||||||
}
|
|
||||||
result.AddError(
|
|
||||||
newAnswerInvalidError(
|
|
||||||
gojsonschema.NewJsonContext("answer", jsonContext),
|
|
||||||
52,
|
|
||||||
errDetail,
|
|
||||||
),
|
|
||||||
errDetail,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is especially useful if you want to add validation beyond what the
|
|
||||||
json schema drafts can provide such business specific logic.
|
|
||||||
|
|
||||||
## Uses
|
|
||||||
|
|
||||||
gojsonschema uses the following test suite :
|
|
||||||
|
|
||||||
https://github.com/json-schema/JSON-Schema-Test-Suite
|
|
125
vendor/github.com/xeipuuv/gojsonschema/draft.go
generated
vendored
125
vendor/github.com/xeipuuv/gojsonschema/draft.go
generated
vendored
@ -1,125 +0,0 @@
|
|||||||
// Copyright 2018 johandorland ( https://github.com/johandorland )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonreference"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Draft is a JSON-schema draft version
|
|
||||||
type Draft int
|
|
||||||
|
|
||||||
// Supported Draft versions
|
|
||||||
const (
|
|
||||||
Draft4 Draft = 4
|
|
||||||
Draft6 Draft = 6
|
|
||||||
Draft7 Draft = 7
|
|
||||||
Hybrid Draft = math.MaxInt32
|
|
||||||
)
|
|
||||||
|
|
||||||
type draftConfig struct {
|
|
||||||
Version Draft
|
|
||||||
MetaSchemaURL string
|
|
||||||
MetaSchema string
|
|
||||||
}
|
|
||||||
type draftConfigs []draftConfig
|
|
||||||
|
|
||||||
var drafts draftConfigs
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
drafts = []draftConfig{
|
|
||||||
{
|
|
||||||
Version: Draft4,
|
|
||||||
MetaSchemaURL: "http://json-schema.org/draft-04/schema",
|
|
||||||
MetaSchema: `{"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: Draft6,
|
|
||||||
MetaSchemaURL: "http://json-schema.org/draft-06/schema",
|
|
||||||
MetaSchema: `{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: Draft7,
|
|
||||||
MetaSchemaURL: "http://json-schema.org/draft-07/schema",
|
|
||||||
MetaSchema: `{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc draftConfigs) GetMetaSchema(url string) string {
|
|
||||||
for _, config := range dc {
|
|
||||||
if config.MetaSchemaURL == url {
|
|
||||||
return config.MetaSchema
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
func (dc draftConfigs) GetDraftVersion(url string) *Draft {
|
|
||||||
for _, config := range dc {
|
|
||||||
if config.MetaSchemaURL == url {
|
|
||||||
return &config.Version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (dc draftConfigs) GetSchemaURL(draft Draft) string {
|
|
||||||
for _, config := range dc {
|
|
||||||
if config.Version == draft {
|
|
||||||
return config.MetaSchemaURL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSchemaURL(documentNode interface{}) (string, *Draft, error) {
|
|
||||||
|
|
||||||
if isKind(documentNode, reflect.Bool) {
|
|
||||||
return "", nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isKind(documentNode, reflect.Map) {
|
|
||||||
return "", nil, errors.New("schema is invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
m := documentNode.(map[string]interface{})
|
|
||||||
|
|
||||||
if existsMapKey(m, KEY_SCHEMA) {
|
|
||||||
if !isKind(m[KEY_SCHEMA], reflect.String) {
|
|
||||||
return "", nil, errors.New(formatErrorDescription(
|
|
||||||
Locale.MustBeOfType(),
|
|
||||||
ErrorDetails{
|
|
||||||
"key": KEY_SCHEMA,
|
|
||||||
"type": TYPE_STRING,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
schemaReference, err := gojsonreference.NewJsonReference(m[KEY_SCHEMA].(string))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
schema := schemaReference.String()
|
|
||||||
|
|
||||||
return schema, drafts.GetDraftVersion(schema), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil, nil
|
|
||||||
}
|
|
364
vendor/github.com/xeipuuv/gojsonschema/errors.go
generated
vendored
364
vendor/github.com/xeipuuv/gojsonschema/errors.go
generated
vendored
@ -1,364 +0,0 @@
|
|||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sync"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errorTemplates = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
|
|
||||||
|
|
||||||
// template.Template is not thread-safe for writing, so some locking is done
|
|
||||||
// sync.RWMutex is used for efficiently locking when new templates are created
|
|
||||||
type errorTemplate struct {
|
|
||||||
*template.Template
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
|
|
||||||
// FalseError. ErrorDetails: -
|
|
||||||
FalseError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequiredError indicates that a required field is missing
|
|
||||||
// ErrorDetails: property string
|
|
||||||
RequiredError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidTypeError indicates that a field has the incorrect type
|
|
||||||
// ErrorDetails: expected, given
|
|
||||||
InvalidTypeError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberAnyOfError is produced in case of a failing "anyOf" validation
|
|
||||||
// ErrorDetails: -
|
|
||||||
NumberAnyOfError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberOneOfError is produced in case of a failing "oneOf" validation
|
|
||||||
// ErrorDetails: -
|
|
||||||
NumberOneOfError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberAllOfError is produced in case of a failing "allOf" validation
|
|
||||||
// ErrorDetails: -
|
|
||||||
NumberAllOfError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberNotError is produced if a "not" validation failed
|
|
||||||
// ErrorDetails: -
|
|
||||||
NumberNotError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// MissingDependencyError is produced in case of a "missing dependency" problem
|
|
||||||
// ErrorDetails: dependency
|
|
||||||
MissingDependencyError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// InternalError indicates an internal error
|
|
||||||
// ErrorDetails: error
|
|
||||||
InternalError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConstError indicates a const error
|
|
||||||
// ErrorDetails: allowed
|
|
||||||
ConstError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumError indicates an enum error
|
|
||||||
// ErrorDetails: allowed
|
|
||||||
EnumError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed
|
|
||||||
// ErrorDetails: -
|
|
||||||
ArrayNoAdditionalItemsError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMinItemsError is produced if an array contains less items than the allowed minimum
|
|
||||||
// ErrorDetails: min
|
|
||||||
ArrayMinItemsError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMaxItemsError is produced if an array contains more items than the allowed maximum
|
|
||||||
// ErrorDetails: max
|
|
||||||
ArrayMaxItemsError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items
|
|
||||||
// ErrorDetails: type, i, j
|
|
||||||
ItemsMustBeUniqueError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayContainsError is produced if an array contains invalid items
|
|
||||||
// ErrorDetails:
|
|
||||||
ArrayContainsError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum
|
|
||||||
// ErrorDetails: min
|
|
||||||
ArrayMinPropertiesError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum
|
|
||||||
// ErrorDetails: max
|
|
||||||
ArrayMaxPropertiesError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed
|
|
||||||
// ErrorDetails: property
|
|
||||||
AdditionalPropertyNotAllowedError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidPropertyPatternError is produced if an pattern was found
|
|
||||||
// ErrorDetails: property, pattern
|
|
||||||
InvalidPropertyPatternError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidPropertyNameError is produced if an invalid-named property was found
|
|
||||||
// ErrorDetails: property
|
|
||||||
InvalidPropertyNameError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringLengthGTEError is produced if a string is shorter than the minimum required length
|
|
||||||
// ErrorDetails: min
|
|
||||||
StringLengthGTEError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringLengthLTEError is produced if a string is longer than the maximum allowed length
|
|
||||||
// ErrorDetails: max
|
|
||||||
StringLengthLTEError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoesNotMatchPatternError is produced if a string does not match the defined pattern
|
|
||||||
// ErrorDetails: pattern
|
|
||||||
DoesNotMatchPatternError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoesNotMatchFormatError is produced if a string does not match the defined format
|
|
||||||
// ErrorDetails: format
|
|
||||||
DoesNotMatchFormatError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultipleOfError is produced if a number is not a multiple of the defined multipleOf
|
|
||||||
// ErrorDetails: multiple
|
|
||||||
MultipleOfError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberGTEError is produced if a number is lower than the allowed minimum
|
|
||||||
// ErrorDetails: min
|
|
||||||
NumberGTEError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set
|
|
||||||
// ErrorDetails: min
|
|
||||||
NumberGTError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberLTEError is produced if a number is higher than the allowed maximum
|
|
||||||
// ErrorDetails: max
|
|
||||||
NumberLTEError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set
|
|
||||||
// ErrorDetails: max
|
|
||||||
NumberLTError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConditionThenError is produced if a condition's "then" validation is invalid
|
|
||||||
// ErrorDetails: -
|
|
||||||
ConditionThenError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConditionElseError is produced if a condition's "else" condition is invalid
|
|
||||||
// ErrorDetails: -
|
|
||||||
ConditionElseError struct {
|
|
||||||
ResultErrorFields
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// newError takes a ResultError type and sets the type, context, description, details, value, and field
|
|
||||||
func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) {
|
|
||||||
var t string
|
|
||||||
var d string
|
|
||||||
switch err.(type) {
|
|
||||||
case *FalseError:
|
|
||||||
t = "false"
|
|
||||||
d = locale.False()
|
|
||||||
case *RequiredError:
|
|
||||||
t = "required"
|
|
||||||
d = locale.Required()
|
|
||||||
case *InvalidTypeError:
|
|
||||||
t = "invalid_type"
|
|
||||||
d = locale.InvalidType()
|
|
||||||
case *NumberAnyOfError:
|
|
||||||
t = "number_any_of"
|
|
||||||
d = locale.NumberAnyOf()
|
|
||||||
case *NumberOneOfError:
|
|
||||||
t = "number_one_of"
|
|
||||||
d = locale.NumberOneOf()
|
|
||||||
case *NumberAllOfError:
|
|
||||||
t = "number_all_of"
|
|
||||||
d = locale.NumberAllOf()
|
|
||||||
case *NumberNotError:
|
|
||||||
t = "number_not"
|
|
||||||
d = locale.NumberNot()
|
|
||||||
case *MissingDependencyError:
|
|
||||||
t = "missing_dependency"
|
|
||||||
d = locale.MissingDependency()
|
|
||||||
case *InternalError:
|
|
||||||
t = "internal"
|
|
||||||
d = locale.Internal()
|
|
||||||
case *ConstError:
|
|
||||||
t = "const"
|
|
||||||
d = locale.Const()
|
|
||||||
case *EnumError:
|
|
||||||
t = "enum"
|
|
||||||
d = locale.Enum()
|
|
||||||
case *ArrayNoAdditionalItemsError:
|
|
||||||
t = "array_no_additional_items"
|
|
||||||
d = locale.ArrayNoAdditionalItems()
|
|
||||||
case *ArrayMinItemsError:
|
|
||||||
t = "array_min_items"
|
|
||||||
d = locale.ArrayMinItems()
|
|
||||||
case *ArrayMaxItemsError:
|
|
||||||
t = "array_max_items"
|
|
||||||
d = locale.ArrayMaxItems()
|
|
||||||
case *ItemsMustBeUniqueError:
|
|
||||||
t = "unique"
|
|
||||||
d = locale.Unique()
|
|
||||||
case *ArrayContainsError:
|
|
||||||
t = "contains"
|
|
||||||
d = locale.ArrayContains()
|
|
||||||
case *ArrayMinPropertiesError:
|
|
||||||
t = "array_min_properties"
|
|
||||||
d = locale.ArrayMinProperties()
|
|
||||||
case *ArrayMaxPropertiesError:
|
|
||||||
t = "array_max_properties"
|
|
||||||
d = locale.ArrayMaxProperties()
|
|
||||||
case *AdditionalPropertyNotAllowedError:
|
|
||||||
t = "additional_property_not_allowed"
|
|
||||||
d = locale.AdditionalPropertyNotAllowed()
|
|
||||||
case *InvalidPropertyPatternError:
|
|
||||||
t = "invalid_property_pattern"
|
|
||||||
d = locale.InvalidPropertyPattern()
|
|
||||||
case *InvalidPropertyNameError:
|
|
||||||
t = "invalid_property_name"
|
|
||||||
d = locale.InvalidPropertyName()
|
|
||||||
case *StringLengthGTEError:
|
|
||||||
t = "string_gte"
|
|
||||||
d = locale.StringGTE()
|
|
||||||
case *StringLengthLTEError:
|
|
||||||
t = "string_lte"
|
|
||||||
d = locale.StringLTE()
|
|
||||||
case *DoesNotMatchPatternError:
|
|
||||||
t = "pattern"
|
|
||||||
d = locale.DoesNotMatchPattern()
|
|
||||||
case *DoesNotMatchFormatError:
|
|
||||||
t = "format"
|
|
||||||
d = locale.DoesNotMatchFormat()
|
|
||||||
case *MultipleOfError:
|
|
||||||
t = "multiple_of"
|
|
||||||
d = locale.MultipleOf()
|
|
||||||
case *NumberGTEError:
|
|
||||||
t = "number_gte"
|
|
||||||
d = locale.NumberGTE()
|
|
||||||
case *NumberGTError:
|
|
||||||
t = "number_gt"
|
|
||||||
d = locale.NumberGT()
|
|
||||||
case *NumberLTEError:
|
|
||||||
t = "number_lte"
|
|
||||||
d = locale.NumberLTE()
|
|
||||||
case *NumberLTError:
|
|
||||||
t = "number_lt"
|
|
||||||
d = locale.NumberLT()
|
|
||||||
case *ConditionThenError:
|
|
||||||
t = "condition_then"
|
|
||||||
d = locale.ConditionThen()
|
|
||||||
case *ConditionElseError:
|
|
||||||
t = "condition_else"
|
|
||||||
d = locale.ConditionElse()
|
|
||||||
}
|
|
||||||
|
|
||||||
err.SetType(t)
|
|
||||||
err.SetContext(context)
|
|
||||||
err.SetValue(value)
|
|
||||||
err.SetDetails(details)
|
|
||||||
err.SetDescriptionFormat(d)
|
|
||||||
details["field"] = err.Field()
|
|
||||||
|
|
||||||
if _, exists := details["context"]; !exists && context != nil {
|
|
||||||
details["context"] = context.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatErrorDescription takes a string in the default text/template
|
|
||||||
// format and converts it to a string with replacements. The fields come
|
|
||||||
// from the ErrorDetails struct and vary for each type of error.
|
|
||||||
func formatErrorDescription(s string, details ErrorDetails) string {
|
|
||||||
|
|
||||||
var tpl *template.Template
|
|
||||||
var descrAsBuffer bytes.Buffer
|
|
||||||
var err error
|
|
||||||
|
|
||||||
errorTemplates.RLock()
|
|
||||||
tpl = errorTemplates.Lookup(s)
|
|
||||||
errorTemplates.RUnlock()
|
|
||||||
|
|
||||||
if tpl == nil {
|
|
||||||
errorTemplates.Lock()
|
|
||||||
tpl = errorTemplates.New(s)
|
|
||||||
|
|
||||||
if ErrorTemplateFuncs != nil {
|
|
||||||
tpl.Funcs(ErrorTemplateFuncs)
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl, err = tpl.Parse(s)
|
|
||||||
errorTemplates.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tpl.Execute(&descrAsBuffer, details)
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return descrAsBuffer.String()
|
|
||||||
}
|
|
368
vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
generated
vendored
368
vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
generated
vendored
@ -1,368 +0,0 @@
|
|||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/mail"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// FormatChecker is the interface all formatters added to FormatCheckerChain must implement
|
|
||||||
FormatChecker interface {
|
|
||||||
// IsFormat checks if input has the correct format and type
|
|
||||||
IsFormat(input interface{}) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// FormatCheckerChain holds the formatters
|
|
||||||
FormatCheckerChain struct {
|
|
||||||
formatters map[string]FormatChecker
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmailFormatChecker verifies email address formats
|
|
||||||
EmailFormatChecker struct{}
|
|
||||||
|
|
||||||
// IPV4FormatChecker verifies IP addresses in the IPv4 format
|
|
||||||
IPV4FormatChecker struct{}
|
|
||||||
|
|
||||||
// IPV6FormatChecker verifies IP addresses in the IPv6 format
|
|
||||||
IPV6FormatChecker struct{}
|
|
||||||
|
|
||||||
// DateTimeFormatChecker verifies date/time formats per RFC3339 5.6
|
|
||||||
//
|
|
||||||
// Valid formats:
|
|
||||||
// Partial Time: HH:MM:SS
|
|
||||||
// Full Date: YYYY-MM-DD
|
|
||||||
// Full Time: HH:MM:SSZ-07:00
|
|
||||||
// Date Time: YYYY-MM-DDTHH:MM:SSZ-0700
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
// HH = 2DIGIT hour ; 00-23
|
|
||||||
// MM = 2DIGIT ; 00-59
|
|
||||||
// SS = 2DIGIT ; 00-58, 00-60 based on leap second rules
|
|
||||||
// T = Literal
|
|
||||||
// Z = Literal
|
|
||||||
//
|
|
||||||
// Note: Nanoseconds are also suported in all formats
|
|
||||||
//
|
|
||||||
// http://tools.ietf.org/html/rfc3339#section-5.6
|
|
||||||
DateTimeFormatChecker struct{}
|
|
||||||
|
|
||||||
// DateFormatChecker verifies date formats
|
|
||||||
//
|
|
||||||
// Valid format:
|
|
||||||
// Full Date: YYYY-MM-DD
|
|
||||||
//
|
|
||||||
// Where
|
|
||||||
// YYYY = 4DIGIT year
|
|
||||||
// MM = 2DIGIT month ; 01-12
|
|
||||||
// DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year
|
|
||||||
DateFormatChecker struct{}
|
|
||||||
|
|
||||||
// TimeFormatChecker verifies time formats
|
|
||||||
//
|
|
||||||
// Valid formats:
|
|
||||||
// Partial Time: HH:MM:SS
|
|
||||||
// Full Time: HH:MM:SSZ-07:00
|
|
||||||
//
|
|
||||||
// Where
|
|
||||||
// HH = 2DIGIT hour ; 00-23
|
|
||||||
// MM = 2DIGIT ; 00-59
|
|
||||||
// SS = 2DIGIT ; 00-58, 00-60 based on leap second rules
|
|
||||||
// T = Literal
|
|
||||||
// Z = Literal
|
|
||||||
TimeFormatChecker struct{}
|
|
||||||
|
|
||||||
// URIFormatChecker validates a URI with a valid Scheme per RFC3986
|
|
||||||
URIFormatChecker struct{}
|
|
||||||
|
|
||||||
// URIReferenceFormatChecker validates a URI or relative-reference per RFC3986
|
|
||||||
URIReferenceFormatChecker struct{}
|
|
||||||
|
|
||||||
// URITemplateFormatChecker validates a URI template per RFC6570
|
|
||||||
URITemplateFormatChecker struct{}
|
|
||||||
|
|
||||||
// HostnameFormatChecker validates a hostname is in the correct format
|
|
||||||
HostnameFormatChecker struct{}
|
|
||||||
|
|
||||||
// UUIDFormatChecker validates a UUID is in the correct format
|
|
||||||
UUIDFormatChecker struct{}
|
|
||||||
|
|
||||||
// RegexFormatChecker validates a regex is in the correct format
|
|
||||||
RegexFormatChecker struct{}
|
|
||||||
|
|
||||||
// JSONPointerFormatChecker validates a JSON Pointer per RFC6901
|
|
||||||
JSONPointerFormatChecker struct{}
|
|
||||||
|
|
||||||
// RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format
|
|
||||||
RelativeJSONPointerFormatChecker struct{}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// FormatCheckers holds the valid formatters, and is a public variable
|
|
||||||
// so library users can add custom formatters
|
|
||||||
FormatCheckers = FormatCheckerChain{
|
|
||||||
formatters: map[string]FormatChecker{
|
|
||||||
"date": DateFormatChecker{},
|
|
||||||
"time": TimeFormatChecker{},
|
|
||||||
"date-time": DateTimeFormatChecker{},
|
|
||||||
"hostname": HostnameFormatChecker{},
|
|
||||||
"email": EmailFormatChecker{},
|
|
||||||
"idn-email": EmailFormatChecker{},
|
|
||||||
"ipv4": IPV4FormatChecker{},
|
|
||||||
"ipv6": IPV6FormatChecker{},
|
|
||||||
"uri": URIFormatChecker{},
|
|
||||||
"uri-reference": URIReferenceFormatChecker{},
|
|
||||||
"iri": URIFormatChecker{},
|
|
||||||
"iri-reference": URIReferenceFormatChecker{},
|
|
||||||
"uri-template": URITemplateFormatChecker{},
|
|
||||||
"uuid": UUIDFormatChecker{},
|
|
||||||
"regex": RegexFormatChecker{},
|
|
||||||
"json-pointer": JSONPointerFormatChecker{},
|
|
||||||
"relative-json-pointer": RelativeJSONPointerFormatChecker{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
|
|
||||||
rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
|
|
||||||
|
|
||||||
// Use a regex to make sure curly brackets are balanced properly after validating it as a AURI
|
|
||||||
rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")
|
|
||||||
|
|
||||||
rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
|
||||||
|
|
||||||
rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")
|
|
||||||
|
|
||||||
rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")
|
|
||||||
|
|
||||||
lock = new(sync.RWMutex)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add adds a FormatChecker to the FormatCheckerChain
|
|
||||||
// The name used will be the value used for the format key in your json schema
|
|
||||||
func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
|
|
||||||
lock.Lock()
|
|
||||||
c.formatters[name] = f
|
|
||||||
lock.Unlock()
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
|
|
||||||
func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
|
|
||||||
lock.Lock()
|
|
||||||
delete(c.formatters, name)
|
|
||||||
lock.Unlock()
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
|
|
||||||
func (c *FormatCheckerChain) Has(name string) bool {
|
|
||||||
lock.RLock()
|
|
||||||
_, ok := c.formatters[name]
|
|
||||||
lock.RUnlock()
|
|
||||||
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat will check an input against a FormatChecker with the given name
|
|
||||||
// to see if it is the correct format
|
|
||||||
func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
|
|
||||||
lock.RLock()
|
|
||||||
f, ok := c.formatters[name]
|
|
||||||
lock.RUnlock()
|
|
||||||
|
|
||||||
// If a format is unrecognized it should always pass validation
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.IsFormat(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted e-mail address
|
|
||||||
func (f EmailFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := mail.ParseAddress(asString)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted IPv4-address
|
|
||||||
func (f IPV4FormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit: https://github.com/asaskevich/govalidator
|
|
||||||
ip := net.ParseIP(asString)
|
|
||||||
return ip != nil && strings.Contains(asString, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted IPv6=address
|
|
||||||
func (f IPV6FormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit: https://github.com/asaskevich/govalidator
|
|
||||||
ip := net.ParseIP(asString)
|
|
||||||
return ip != nil && strings.Contains(asString, ":")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted date/time per RFC3339 5.6
|
|
||||||
func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
formats := []string{
|
|
||||||
"15:04:05",
|
|
||||||
"15:04:05Z07:00",
|
|
||||||
"2006-01-02",
|
|
||||||
time.RFC3339,
|
|
||||||
time.RFC3339Nano,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, format := range formats {
|
|
||||||
if _, err := time.Parse(format, asString); err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted date (YYYY-MM-DD)
|
|
||||||
func (f DateFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, err := time.Parse("2006-01-02", asString)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input correctly formatted time (HH:MM:SS or HH:MM:SSZ-07:00)
|
|
||||||
func (f TimeFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := time.Parse("15:04:05", asString)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is correctly formatted URI with a valid Scheme per RFC3986
|
|
||||||
func (f URIFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(asString)
|
|
||||||
|
|
||||||
if err != nil || u.Scheme == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return !strings.Contains(asString, `\`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted URI or relative-reference per RFC3986
|
|
||||||
func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := url.Parse(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 {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(asString)
|
|
||||||
if err != nil || strings.Contains(asString, `\`) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rxURITemplate.MatchString(u.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted hostname
|
|
||||||
func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rxHostname.MatchString(asString) && len(asString) < 256
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted UUID
|
|
||||||
func (f UUIDFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rxUUID.MatchString(asString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted regular expression
|
|
||||||
func (f RegexFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if asString == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
_, err := regexp.Compile(asString)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted JSON Pointer per RFC6901
|
|
||||||
func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rxJSONPointer.MatchString(asString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFormat checks if input is a correctly formatted relative JSON Pointer
|
|
||||||
func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
|
|
||||||
asString, ok := input.(string)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rxRelJSONPointer.MatchString(asString)
|
|
||||||
}
|
|
13
vendor/github.com/xeipuuv/gojsonschema/glide.yaml
generated
vendored
13
vendor/github.com/xeipuuv/gojsonschema/glide.yaml
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
package: github.com/xeipuuv/gojsonschema
|
|
||||||
license: Apache 2.0
|
|
||||||
import:
|
|
||||||
- package: github.com/xeipuuv/gojsonschema
|
|
||||||
|
|
||||||
- package: github.com/xeipuuv/gojsonpointer
|
|
||||||
|
|
||||||
- package: github.com/xeipuuv/gojsonreference
|
|
||||||
|
|
||||||
testImport:
|
|
||||||
- package: github.com/stretchr/testify
|
|
||||||
subpackages:
|
|
||||||
- assert
|
|
37
vendor/github.com/xeipuuv/gojsonschema/internalLog.go
generated
vendored
37
vendor/github.com/xeipuuv/gojsonschema/internalLog.go
generated
vendored
@ -1,37 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Very simple log wrapper.
|
|
||||||
// Used for debugging/testing purposes.
|
|
||||||
//
|
|
||||||
// created 01-01-2015
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const internalLogEnabled = false
|
|
||||||
|
|
||||||
func internalLog(format string, v ...interface{}) {
|
|
||||||
log.Printf(format, v...)
|
|
||||||
}
|
|
73
vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
generated
vendored
73
vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
generated
vendored
@ -1,73 +0,0 @@
|
|||||||
// Copyright 2013 MongoDB, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author tolsen
|
|
||||||
// author-github https://github.com/tolsen
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Implements a persistent (immutable w/ shared structure) singly-linked list of strings for the purpose of storing a json context
|
|
||||||
//
|
|
||||||
// created 04-09-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import "bytes"
|
|
||||||
|
|
||||||
// JsonContext implements a persistent linked-list of strings
|
|
||||||
type JsonContext struct {
|
|
||||||
head string
|
|
||||||
tail *JsonContext
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewJsonContext creates a new JsonContext
|
|
||||||
func NewJsonContext(head string, tail *JsonContext) *JsonContext {
|
|
||||||
return &JsonContext{head, tail}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String displays the context in reverse.
|
|
||||||
// This plays well with the data structure's persistent nature with
|
|
||||||
// Cons and a json document's tree structure.
|
|
||||||
func (c *JsonContext) String(del ...string) string {
|
|
||||||
byteArr := make([]byte, 0, c.stringLen())
|
|
||||||
buf := bytes.NewBuffer(byteArr)
|
|
||||||
c.writeStringToBuffer(buf, del)
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *JsonContext) stringLen() int {
|
|
||||||
length := 0
|
|
||||||
if c.tail != nil {
|
|
||||||
length = c.tail.stringLen() + 1 // add 1 for "."
|
|
||||||
}
|
|
||||||
|
|
||||||
length += len(c.head)
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *JsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) {
|
|
||||||
if c.tail != nil {
|
|
||||||
c.tail.writeStringToBuffer(buf, del)
|
|
||||||
|
|
||||||
if len(del) > 0 {
|
|
||||||
buf.WriteString(del[0])
|
|
||||||
} else {
|
|
||||||
buf.WriteString(".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteString(c.head)
|
|
||||||
}
|
|
386
vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
generated
vendored
386
vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
generated
vendored
@ -1,386 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Different strategies to load JSON files.
|
|
||||||
// Includes References (file and HTTP), JSON strings and Go types.
|
|
||||||
//
|
|
||||||
// created 01-02-2015
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonreference"
|
|
||||||
)
|
|
||||||
|
|
||||||
var osFS = osFileSystem(os.Open)
|
|
||||||
|
|
||||||
// JSONLoader defines the JSON loader interface
|
|
||||||
type JSONLoader interface {
|
|
||||||
JsonSource() interface{}
|
|
||||||
LoadJSON() (interface{}, error)
|
|
||||||
JsonReference() (gojsonreference.JsonReference, error)
|
|
||||||
LoaderFactory() JSONLoaderFactory
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONLoaderFactory defines the JSON loader factory interface
|
|
||||||
type JSONLoaderFactory interface {
|
|
||||||
// New creates a new JSON loader for the given source
|
|
||||||
New(source string) JSONLoader
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultJSONLoaderFactory is the default JSON loader factory
|
|
||||||
type DefaultJSONLoaderFactory struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem
|
|
||||||
type FileSystemJSONLoaderFactory struct {
|
|
||||||
fs http.FileSystem
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new JSON loader for the given source
|
|
||||||
func (d DefaultJSONLoaderFactory) New(source string) JSONLoader {
|
|
||||||
return &jsonReferenceLoader{
|
|
||||||
fs: osFS,
|
|
||||||
source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new JSON loader for the given source
|
|
||||||
func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader {
|
|
||||||
return &jsonReferenceLoader{
|
|
||||||
fs: f.fs,
|
|
||||||
source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// osFileSystem is a functional wrapper for os.Open that implements http.FileSystem.
|
|
||||||
type osFileSystem func(string) (*os.File, error)
|
|
||||||
|
|
||||||
// Opens a file with the given name
|
|
||||||
func (o osFileSystem) Open(name string) (http.File, error) {
|
|
||||||
return o(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON Reference loader
|
|
||||||
// references are used to load JSONs from files and HTTP
|
|
||||||
|
|
||||||
type jsonReferenceLoader struct {
|
|
||||||
fs http.FileSystem
|
|
||||||
source string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonReferenceLoader) JsonSource() interface{} {
|
|
||||||
return l.source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
|
||||||
return gojsonreference.NewJsonReference(l.JsonSource().(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory {
|
|
||||||
return &FileSystemJSONLoaderFactory{
|
|
||||||
fs: l.fs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
|
|
||||||
func NewReferenceLoader(source string) JSONLoader {
|
|
||||||
return &jsonReferenceLoader{
|
|
||||||
fs: osFS,
|
|
||||||
source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
|
|
||||||
func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader {
|
|
||||||
return &jsonReferenceLoader{
|
|
||||||
fs: fs,
|
|
||||||
source: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
reference, err := gojsonreference.NewJsonReference(l.JsonSource().(string))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
refToURL := reference
|
|
||||||
refToURL.GetUrl().Fragment = ""
|
|
||||||
|
|
||||||
var document interface{}
|
|
||||||
|
|
||||||
if reference.HasFileScheme {
|
|
||||||
|
|
||||||
filename := strings.TrimPrefix(refToURL.String(), "file://")
|
|
||||||
filename, err = url.QueryUnescape(filename)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// on Windows, a file URL may have an extra leading slash, use slashes
|
|
||||||
// instead of backslashes, and have spaces escaped
|
|
||||||
filename = strings.TrimPrefix(filename, "/")
|
|
||||||
filename = filepath.FromSlash(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
document, err = l.loadFromFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
document, err = l.loadFromHTTP(refToURL.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return document, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
|
|
||||||
|
|
||||||
// returned cached versions for metaschemas for drafts 4, 6 and 7
|
|
||||||
// for performance and allow for easier offline use
|
|
||||||
if metaSchema := drafts.GetMetaSchema(address); metaSchema != "" {
|
|
||||||
return decodeJSONUsingNumber(strings.NewReader(metaSchema))
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.Get(address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// must return HTTP Status 200 OK
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, errors.New(formatErrorDescription(Locale.HttpBadStatus(), ErrorDetails{"status": resp.Status}))
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyBuff, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
|
|
||||||
f, err := l.fs.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
bodyBuff, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON string loader
|
|
||||||
|
|
||||||
type jsonStringLoader struct {
|
|
||||||
source string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonStringLoader) JsonSource() interface{} {
|
|
||||||
return l.source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonStringLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
|
||||||
return gojsonreference.NewJsonReference("#")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory {
|
|
||||||
return &DefaultJSONLoaderFactory{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringLoader creates a new JSONLoader, taking a string as source
|
|
||||||
func NewStringLoader(source string) JSONLoader {
|
|
||||||
return &jsonStringLoader{source: source}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonStringLoader) LoadJSON() (interface{}, error) {
|
|
||||||
|
|
||||||
return decodeJSONUsingNumber(strings.NewReader(l.JsonSource().(string)))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON bytes loader
|
|
||||||
|
|
||||||
type jsonBytesLoader struct {
|
|
||||||
source []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonBytesLoader) JsonSource() interface{} {
|
|
||||||
return l.source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonBytesLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
|
||||||
return gojsonreference.NewJsonReference("#")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory {
|
|
||||||
return &DefaultJSONLoaderFactory{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBytesLoader creates a new JSONLoader, taking a `[]byte` as source
|
|
||||||
func NewBytesLoader(source []byte) JSONLoader {
|
|
||||||
return &jsonBytesLoader{source: source}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
|
|
||||||
return decodeJSONUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON Go (types) loader
|
|
||||||
// used to load JSONs from the code as maps, interface{}, structs ...
|
|
||||||
|
|
||||||
type jsonGoLoader struct {
|
|
||||||
source interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonGoLoader) JsonSource() interface{} {
|
|
||||||
return l.source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonGoLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
|
||||||
return gojsonreference.NewJsonReference("#")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory {
|
|
||||||
return &DefaultJSONLoaderFactory{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGoLoader creates a new JSONLoader from a given Go struct
|
|
||||||
func NewGoLoader(source interface{}) JSONLoader {
|
|
||||||
return &jsonGoLoader{source: source}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonGoLoader) LoadJSON() (interface{}, error) {
|
|
||||||
|
|
||||||
// convert it to a compliant JSON first to avoid types "mismatches"
|
|
||||||
|
|
||||||
jsonBytes, err := json.Marshal(l.JsonSource())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeJSONUsingNumber(bytes.NewReader(jsonBytes))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsonIOLoader struct {
|
|
||||||
buf *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReaderLoader creates a new JSON loader using the provided io.Reader
|
|
||||||
func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) {
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWriterLoader creates a new JSON loader using the provided io.Writer
|
|
||||||
func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) {
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonIOLoader) JsonSource() interface{} {
|
|
||||||
return l.buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonIOLoader) LoadJSON() (interface{}, error) {
|
|
||||||
return decodeJSONUsingNumber(l.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
|
||||||
return gojsonreference.NewJsonReference("#")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory {
|
|
||||||
return &DefaultJSONLoaderFactory{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON raw loader
|
|
||||||
// In case the JSON is already marshalled to interface{} use this loader
|
|
||||||
// This is used for testing as otherwise there is no guarantee the JSON is marshalled
|
|
||||||
// "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
|
|
||||||
type jsonRawLoader struct {
|
|
||||||
source interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRawLoader creates a new JSON raw loader for the given source
|
|
||||||
func NewRawLoader(source interface{}) JSONLoader {
|
|
||||||
return &jsonRawLoader{source: source}
|
|
||||||
}
|
|
||||||
func (l *jsonRawLoader) JsonSource() interface{} {
|
|
||||||
return l.source
|
|
||||||
}
|
|
||||||
func (l *jsonRawLoader) LoadJSON() (interface{}, error) {
|
|
||||||
return l.source, nil
|
|
||||||
}
|
|
||||||
func (l *jsonRawLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
|
||||||
return gojsonreference.NewJsonReference("#")
|
|
||||||
}
|
|
||||||
func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory {
|
|
||||||
return &DefaultJSONLoaderFactory{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeJSONUsingNumber(r io.Reader) (interface{}, error) {
|
|
||||||
|
|
||||||
var document interface{}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(r)
|
|
||||||
decoder.UseNumber()
|
|
||||||
|
|
||||||
err := decoder.Decode(&document)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return document, nil
|
|
||||||
|
|
||||||
}
|
|
472
vendor/github.com/xeipuuv/gojsonschema/locales.go
generated
vendored
472
vendor/github.com/xeipuuv/gojsonschema/locales.go
generated
vendored
@ -1,472 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Contains const string and messages.
|
|
||||||
//
|
|
||||||
// created 01-01-2015
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
type (
|
|
||||||
// locale is an interface for defining custom error strings
|
|
||||||
locale interface {
|
|
||||||
|
|
||||||
// False returns a format-string for "false" schema validation errors
|
|
||||||
False() string
|
|
||||||
|
|
||||||
// Required returns a format-string for "required" schema validation errors
|
|
||||||
Required() string
|
|
||||||
|
|
||||||
// InvalidType returns a format-string for "invalid type" schema validation errors
|
|
||||||
InvalidType() string
|
|
||||||
|
|
||||||
// NumberAnyOf returns a format-string for "anyOf" schema validation errors
|
|
||||||
NumberAnyOf() string
|
|
||||||
|
|
||||||
// NumberOneOf returns a format-string for "oneOf" schema validation errors
|
|
||||||
NumberOneOf() string
|
|
||||||
|
|
||||||
// NumberAllOf returns a format-string for "allOf" schema validation errors
|
|
||||||
NumberAllOf() string
|
|
||||||
|
|
||||||
// NumberNot returns a format-string to format a NumberNotError
|
|
||||||
NumberNot() string
|
|
||||||
|
|
||||||
// MissingDependency returns a format-string for "missing dependency" schema validation errors
|
|
||||||
MissingDependency() string
|
|
||||||
|
|
||||||
// Internal returns a format-string for internal errors
|
|
||||||
Internal() string
|
|
||||||
|
|
||||||
// Const returns a format-string to format a ConstError
|
|
||||||
Const() string
|
|
||||||
|
|
||||||
// Enum returns a format-string to format an EnumError
|
|
||||||
Enum() string
|
|
||||||
|
|
||||||
// ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema
|
|
||||||
ArrayNotEnoughItems() string
|
|
||||||
|
|
||||||
// ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError
|
|
||||||
ArrayNoAdditionalItems() string
|
|
||||||
|
|
||||||
// ArrayMinItems returns a format-string to format an ArrayMinItemsError
|
|
||||||
ArrayMinItems() string
|
|
||||||
|
|
||||||
// ArrayMaxItems returns a format-string to format an ArrayMaxItemsError
|
|
||||||
ArrayMaxItems() string
|
|
||||||
|
|
||||||
// Unique returns a format-string to format an ItemsMustBeUniqueError
|
|
||||||
Unique() string
|
|
||||||
|
|
||||||
// ArrayContains returns a format-string to format an ArrayContainsError
|
|
||||||
ArrayContains() string
|
|
||||||
|
|
||||||
// ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError
|
|
||||||
ArrayMinProperties() string
|
|
||||||
|
|
||||||
// ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError
|
|
||||||
ArrayMaxProperties() string
|
|
||||||
|
|
||||||
// AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError
|
|
||||||
AdditionalPropertyNotAllowed() string
|
|
||||||
|
|
||||||
// InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError
|
|
||||||
InvalidPropertyPattern() string
|
|
||||||
|
|
||||||
// InvalidPropertyName returns a format-string to format an InvalidPropertyNameError
|
|
||||||
InvalidPropertyName() string
|
|
||||||
|
|
||||||
// StringGTE returns a format-string to format an StringLengthGTEError
|
|
||||||
StringGTE() string
|
|
||||||
|
|
||||||
// StringLTE returns a format-string to format an StringLengthLTEError
|
|
||||||
StringLTE() string
|
|
||||||
|
|
||||||
// DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError
|
|
||||||
DoesNotMatchPattern() string
|
|
||||||
|
|
||||||
// DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError
|
|
||||||
DoesNotMatchFormat() string
|
|
||||||
|
|
||||||
// MultipleOf returns a format-string to format an MultipleOfError
|
|
||||||
MultipleOf() string
|
|
||||||
|
|
||||||
// NumberGTE returns a format-string to format an NumberGTEError
|
|
||||||
NumberGTE() string
|
|
||||||
|
|
||||||
// NumberGT returns a format-string to format an NumberGTError
|
|
||||||
NumberGT() string
|
|
||||||
|
|
||||||
// NumberLTE returns a format-string to format an NumberLTEError
|
|
||||||
NumberLTE() string
|
|
||||||
|
|
||||||
// NumberLT returns a format-string to format an NumberLTError
|
|
||||||
NumberLT() string
|
|
||||||
|
|
||||||
// Schema validations
|
|
||||||
|
|
||||||
// RegexPattern returns a format-string to format a regex-pattern error
|
|
||||||
RegexPattern() string
|
|
||||||
|
|
||||||
// GreaterThanZero returns a format-string to format an error where a number must be greater than zero
|
|
||||||
GreaterThanZero() string
|
|
||||||
|
|
||||||
// MustBeOfA returns a format-string to format an error where a value is of the wrong type
|
|
||||||
MustBeOfA() string
|
|
||||||
|
|
||||||
// MustBeOfAn returns a format-string to format an error where a value is of the wrong type
|
|
||||||
MustBeOfAn() string
|
|
||||||
|
|
||||||
// CannotBeUsedWithout returns a format-string to format a "cannot be used without" error
|
|
||||||
CannotBeUsedWithout() string
|
|
||||||
|
|
||||||
// CannotBeGT returns a format-string to format an error where a value are greater than allowed
|
|
||||||
CannotBeGT() string
|
|
||||||
|
|
||||||
// MustBeOfType returns a format-string to format an error where a value does not match the required type
|
|
||||||
MustBeOfType() string
|
|
||||||
|
|
||||||
// MustBeValidRegex returns a format-string to format an error where a regex is invalid
|
|
||||||
MustBeValidRegex() string
|
|
||||||
|
|
||||||
// MustBeValidFormat returns a format-string to format an error where a value does not match the expected format
|
|
||||||
MustBeValidFormat() string
|
|
||||||
|
|
||||||
// MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0
|
|
||||||
MustBeGTEZero() string
|
|
||||||
|
|
||||||
// KeyCannotBeGreaterThan returns a format-string to format an error where a key is greater than the maximum allowed
|
|
||||||
KeyCannotBeGreaterThan() string
|
|
||||||
|
|
||||||
// KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type
|
|
||||||
KeyItemsMustBeOfType() string
|
|
||||||
|
|
||||||
// KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique
|
|
||||||
KeyItemsMustBeUnique() string
|
|
||||||
|
|
||||||
// ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error
|
|
||||||
ReferenceMustBeCanonical() string
|
|
||||||
|
|
||||||
// NotAValidType returns a format-string to format an invalid type error
|
|
||||||
NotAValidType() string
|
|
||||||
|
|
||||||
// Duplicated returns a format-string to format an error where types are duplicated
|
|
||||||
Duplicated() string
|
|
||||||
|
|
||||||
// HttpBadStatus returns a format-string for errors when loading a schema using HTTP
|
|
||||||
HttpBadStatus() string
|
|
||||||
|
|
||||||
// ParseError returns a format-string for JSON parsing errors
|
|
||||||
ParseError() string
|
|
||||||
|
|
||||||
// ConditionThen returns a format-string for ConditionThenError errors
|
|
||||||
ConditionThen() string
|
|
||||||
|
|
||||||
// ConditionElse returns a format-string for ConditionElseError errors
|
|
||||||
ConditionElse() string
|
|
||||||
|
|
||||||
// ErrorFormat returns a format string for errors
|
|
||||||
ErrorFormat() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultLocale is the default locale for this package
|
|
||||||
DefaultLocale struct{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// False returns a format-string for "false" schema validation errors
|
|
||||||
func (l DefaultLocale) False() string {
|
|
||||||
return "False always fails validation"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required returns a format-string for "required" schema validation errors
|
|
||||||
func (l DefaultLocale) Required() string {
|
|
||||||
return `{{.property}} is required`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidType returns a format-string for "invalid type" schema validation errors
|
|
||||||
func (l DefaultLocale) InvalidType() string {
|
|
||||||
return `Invalid type. Expected: {{.expected}}, given: {{.given}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberAnyOf returns a format-string for "anyOf" schema validation errors
|
|
||||||
func (l DefaultLocale) NumberAnyOf() string {
|
|
||||||
return `Must validate at least one schema (anyOf)`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberOneOf returns a format-string for "oneOf" schema validation errors
|
|
||||||
func (l DefaultLocale) NumberOneOf() string {
|
|
||||||
return `Must validate one and only one schema (oneOf)`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberAllOf returns a format-string for "allOf" schema validation errors
|
|
||||||
func (l DefaultLocale) NumberAllOf() string {
|
|
||||||
return `Must validate all the schemas (allOf)`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberNot returns a format-string to format a NumberNotError
|
|
||||||
func (l DefaultLocale) NumberNot() string {
|
|
||||||
return `Must not validate the schema (not)`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MissingDependency returns a format-string for "missing dependency" schema validation errors
|
|
||||||
func (l DefaultLocale) MissingDependency() string {
|
|
||||||
return `Has a dependency on {{.dependency}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal returns a format-string for internal errors
|
|
||||||
func (l DefaultLocale) Internal() string {
|
|
||||||
return `Internal Error {{.error}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Const returns a format-string to format a ConstError
|
|
||||||
func (l DefaultLocale) Const() string {
|
|
||||||
return `{{.field}} does not match: {{.allowed}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enum returns a format-string to format an EnumError
|
|
||||||
func (l DefaultLocale) Enum() string {
|
|
||||||
return `{{.field}} must be one of the following: {{.allowed}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError
|
|
||||||
func (l DefaultLocale) ArrayNoAdditionalItems() string {
|
|
||||||
return `No additional items allowed on array`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema
|
|
||||||
func (l DefaultLocale) ArrayNotEnoughItems() string {
|
|
||||||
return `Not enough items on array to match positional list of schema`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMinItems returns a format-string to format an ArrayMinItemsError
|
|
||||||
func (l DefaultLocale) ArrayMinItems() string {
|
|
||||||
return `Array must have at least {{.min}} items`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMaxItems returns a format-string to format an ArrayMaxItemsError
|
|
||||||
func (l DefaultLocale) ArrayMaxItems() string {
|
|
||||||
return `Array must have at most {{.max}} items`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unique returns a format-string to format an ItemsMustBeUniqueError
|
|
||||||
func (l DefaultLocale) Unique() string {
|
|
||||||
return `{{.type}} items[{{.i}},{{.j}}] must be unique`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayContains returns a format-string to format an ArrayContainsError
|
|
||||||
func (l DefaultLocale) ArrayContains() string {
|
|
||||||
return `At least one of the items must match`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError
|
|
||||||
func (l DefaultLocale) ArrayMinProperties() string {
|
|
||||||
return `Must have at least {{.min}} properties`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError
|
|
||||||
func (l DefaultLocale) ArrayMaxProperties() string {
|
|
||||||
return `Must have at most {{.max}} properties`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError
|
|
||||||
func (l DefaultLocale) AdditionalPropertyNotAllowed() string {
|
|
||||||
return `Additional property {{.property}} is not allowed`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError
|
|
||||||
func (l DefaultLocale) InvalidPropertyPattern() string {
|
|
||||||
return `Property "{{.property}}" does not match pattern {{.pattern}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidPropertyName returns a format-string to format an InvalidPropertyNameError
|
|
||||||
func (l DefaultLocale) InvalidPropertyName() string {
|
|
||||||
return `Property name of "{{.property}}" does not match`
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringGTE returns a format-string to format an StringLengthGTEError
|
|
||||||
func (l DefaultLocale) StringGTE() string {
|
|
||||||
return `String length must be greater than or equal to {{.min}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringLTE returns a format-string to format an StringLengthLTEError
|
|
||||||
func (l DefaultLocale) StringLTE() string {
|
|
||||||
return `String length must be less than or equal to {{.max}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError
|
|
||||||
func (l DefaultLocale) DoesNotMatchPattern() string {
|
|
||||||
return `Does not match pattern '{{.pattern}}'`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError
|
|
||||||
func (l DefaultLocale) DoesNotMatchFormat() string {
|
|
||||||
return `Does not match format '{{.format}}'`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultipleOf returns a format-string to format an MultipleOfError
|
|
||||||
func (l DefaultLocale) MultipleOf() string {
|
|
||||||
return `Must be a multiple of {{.multiple}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberGTE returns the format string to format a NumberGTEError
|
|
||||||
func (l DefaultLocale) NumberGTE() string {
|
|
||||||
return `Must be greater than or equal to {{.min}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberGT returns the format string to format a NumberGTError
|
|
||||||
func (l DefaultLocale) NumberGT() string {
|
|
||||||
return `Must be greater than {{.min}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberLTE returns the format string to format a NumberLTEError
|
|
||||||
func (l DefaultLocale) NumberLTE() string {
|
|
||||||
return `Must be less than or equal to {{.max}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberLT returns the format string to format a NumberLTError
|
|
||||||
func (l DefaultLocale) NumberLT() string {
|
|
||||||
return `Must be less than {{.max}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schema validators
|
|
||||||
|
|
||||||
// RegexPattern returns a format-string to format a regex-pattern error
|
|
||||||
func (l DefaultLocale) RegexPattern() string {
|
|
||||||
return `Invalid regex pattern '{{.pattern}}'`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GreaterThanZero returns a format-string to format an error where a number must be greater than zero
|
|
||||||
func (l DefaultLocale) GreaterThanZero() string {
|
|
||||||
return `{{.number}} must be strictly greater than 0`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBeOfA returns a format-string to format an error where a value is of the wrong type
|
|
||||||
func (l DefaultLocale) MustBeOfA() string {
|
|
||||||
return `{{.x}} must be of a {{.y}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBeOfAn returns a format-string to format an error where a value is of the wrong type
|
|
||||||
func (l DefaultLocale) MustBeOfAn() string {
|
|
||||||
return `{{.x}} must be of an {{.y}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CannotBeUsedWithout returns a format-string to format a "cannot be used without" error
|
|
||||||
func (l DefaultLocale) CannotBeUsedWithout() string {
|
|
||||||
return `{{.x}} cannot be used without {{.y}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CannotBeGT returns a format-string to format an error where a value are greater than allowed
|
|
||||||
func (l DefaultLocale) CannotBeGT() string {
|
|
||||||
return `{{.x}} cannot be greater than {{.y}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBeOfType returns a format-string to format an error where a value does not match the required type
|
|
||||||
func (l DefaultLocale) MustBeOfType() string {
|
|
||||||
return `{{.key}} must be of type {{.type}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBeValidRegex returns a format-string to format an error where a regex is invalid
|
|
||||||
func (l DefaultLocale) MustBeValidRegex() string {
|
|
||||||
return `{{.key}} must be a valid regex`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBeValidFormat returns a format-string to format an error where a value does not match the expected format
|
|
||||||
func (l DefaultLocale) MustBeValidFormat() string {
|
|
||||||
return `{{.key}} must be a valid format {{.given}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0
|
|
||||||
func (l DefaultLocale) MustBeGTEZero() string {
|
|
||||||
return `{{.key}} must be greater than or equal to 0`
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyCannotBeGreaterThan returns a format-string to format an error where a value is greater than the maximum allowed
|
|
||||||
func (l DefaultLocale) KeyCannotBeGreaterThan() string {
|
|
||||||
return `{{.key}} cannot be greater than {{.y}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type
|
|
||||||
func (l DefaultLocale) KeyItemsMustBeOfType() string {
|
|
||||||
return `{{.key}} items must be {{.type}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique
|
|
||||||
func (l DefaultLocale) KeyItemsMustBeUnique() string {
|
|
||||||
return `{{.key}} items must be unique`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error
|
|
||||||
func (l DefaultLocale) ReferenceMustBeCanonical() string {
|
|
||||||
return `Reference {{.reference}} must be canonical`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotAValidType returns a format-string to format an invalid type error
|
|
||||||
func (l DefaultLocale) NotAValidType() string {
|
|
||||||
return `has a primitive type that is NOT VALID -- given: {{.given}} Expected valid values are:{{.expected}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicated returns a format-string to format an error where types are duplicated
|
|
||||||
func (l DefaultLocale) Duplicated() string {
|
|
||||||
return `{{.type}} type is duplicated`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpBadStatus returns a format-string for errors when loading a schema using HTTP
|
|
||||||
func (l DefaultLocale) HttpBadStatus() string {
|
|
||||||
return `Could not read schema from HTTP, response status is {{.status}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorFormat returns a format string for errors
|
|
||||||
// Replacement options: field, description, context, value
|
|
||||||
func (l DefaultLocale) ErrorFormat() string {
|
|
||||||
return `{{.field}}: {{.description}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseError returns a format-string for JSON parsing errors
|
|
||||||
func (l DefaultLocale) ParseError() string {
|
|
||||||
return `Expected: {{.expected}}, given: Invalid JSON`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConditionThen returns a format-string for ConditionThenError errors
|
|
||||||
// If/Else
|
|
||||||
func (l DefaultLocale) ConditionThen() string {
|
|
||||||
return `Must validate "then" as "if" was valid`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConditionElse returns a format-string for ConditionElseError errors
|
|
||||||
func (l DefaultLocale) ConditionElse() string {
|
|
||||||
return `Must validate "else" as "if" was not valid`
|
|
||||||
}
|
|
||||||
|
|
||||||
// constants
|
|
||||||
const (
|
|
||||||
STRING_NUMBER = "number"
|
|
||||||
STRING_ARRAY_OF_STRINGS = "array of strings"
|
|
||||||
STRING_ARRAY_OF_SCHEMAS = "array of schemas"
|
|
||||||
STRING_SCHEMA = "valid schema"
|
|
||||||
STRING_SCHEMA_OR_ARRAY_OF_STRINGS = "schema or array of strings"
|
|
||||||
STRING_PROPERTIES = "properties"
|
|
||||||
STRING_DEPENDENCY = "dependency"
|
|
||||||
STRING_PROPERTY = "property"
|
|
||||||
STRING_UNDEFINED = "undefined"
|
|
||||||
STRING_CONTEXT_ROOT = "(root)"
|
|
||||||
STRING_ROOT_SCHEMA_PROPERTY = "(root)"
|
|
||||||
)
|
|
220
vendor/github.com/xeipuuv/gojsonschema/result.go
generated
vendored
220
vendor/github.com/xeipuuv/gojsonschema/result.go
generated
vendored
@ -1,220 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Result and ResultError implementations.
|
|
||||||
//
|
|
||||||
// created 01-01-2015
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// ErrorDetails is a map of details specific to each error.
|
|
||||||
// While the values will vary, every error will contain a "field" value
|
|
||||||
ErrorDetails map[string]interface{}
|
|
||||||
|
|
||||||
// ResultError is the interface that library errors must implement
|
|
||||||
ResultError interface {
|
|
||||||
// Field returns the field name without the root context
|
|
||||||
// i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName
|
|
||||||
Field() string
|
|
||||||
// SetType sets the error-type
|
|
||||||
SetType(string)
|
|
||||||
// Type returns the error-type
|
|
||||||
Type() string
|
|
||||||
// SetContext sets the JSON-context for the error
|
|
||||||
SetContext(*JsonContext)
|
|
||||||
// Context returns the JSON-context of the error
|
|
||||||
Context() *JsonContext
|
|
||||||
// SetDescription sets a description for the error
|
|
||||||
SetDescription(string)
|
|
||||||
// Description returns the description of the error
|
|
||||||
Description() string
|
|
||||||
// SetDescriptionFormat sets the format for the description in the default text/template format
|
|
||||||
SetDescriptionFormat(string)
|
|
||||||
// DescriptionFormat returns the format for the description in the default text/template format
|
|
||||||
DescriptionFormat() string
|
|
||||||
// SetValue sets the value related to the error
|
|
||||||
SetValue(interface{})
|
|
||||||
// Value returns the value related to the error
|
|
||||||
Value() interface{}
|
|
||||||
// SetDetails sets the details specific to the error
|
|
||||||
SetDetails(ErrorDetails)
|
|
||||||
// Details returns details about the error
|
|
||||||
Details() ErrorDetails
|
|
||||||
// String returns a string representation of the error
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResultErrorFields holds the fields for each ResultError implementation.
|
|
||||||
// ResultErrorFields implements the ResultError interface, so custom errors
|
|
||||||
// can be defined by just embedding this type
|
|
||||||
ResultErrorFields struct {
|
|
||||||
errorType string // A string with the type of error (i.e. invalid_type)
|
|
||||||
context *JsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
|
|
||||||
description string // A human readable error message
|
|
||||||
descriptionFormat string // A format for human readable error message
|
|
||||||
value interface{} // Value given by the JSON file that is the source of the error
|
|
||||||
details ErrorDetails
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result holds the result of a validation
|
|
||||||
Result struct {
|
|
||||||
errors []ResultError
|
|
||||||
// Scores how well the validation matched. Useful in generating
|
|
||||||
// better error messages for anyOf and oneOf.
|
|
||||||
score int
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Field returns the field name without the root context
|
|
||||||
// i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName
|
|
||||||
func (v *ResultErrorFields) Field() string {
|
|
||||||
return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetType sets the error-type
|
|
||||||
func (v *ResultErrorFields) SetType(errorType string) {
|
|
||||||
v.errorType = errorType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the error-type
|
|
||||||
func (v *ResultErrorFields) Type() string {
|
|
||||||
return v.errorType
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetContext sets the JSON-context for the error
|
|
||||||
func (v *ResultErrorFields) SetContext(context *JsonContext) {
|
|
||||||
v.context = context
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context returns the JSON-context of the error
|
|
||||||
func (v *ResultErrorFields) Context() *JsonContext {
|
|
||||||
return v.context
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDescription sets a description for the error
|
|
||||||
func (v *ResultErrorFields) SetDescription(description string) {
|
|
||||||
v.description = description
|
|
||||||
}
|
|
||||||
|
|
||||||
// Description returns the description of the error
|
|
||||||
func (v *ResultErrorFields) Description() string {
|
|
||||||
return v.description
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDescriptionFormat sets the format for the description in the default text/template format
|
|
||||||
func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) {
|
|
||||||
v.descriptionFormat = descriptionFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
// DescriptionFormat returns the format for the description in the default text/template format
|
|
||||||
func (v *ResultErrorFields) DescriptionFormat() string {
|
|
||||||
return v.descriptionFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetValue sets the value related to the error
|
|
||||||
func (v *ResultErrorFields) SetValue(value interface{}) {
|
|
||||||
v.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the value related to the error
|
|
||||||
func (v *ResultErrorFields) Value() interface{} {
|
|
||||||
return v.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDetails sets the details specific to the error
|
|
||||||
func (v *ResultErrorFields) SetDetails(details ErrorDetails) {
|
|
||||||
v.details = details
|
|
||||||
}
|
|
||||||
|
|
||||||
// Details returns details about the error
|
|
||||||
func (v *ResultErrorFields) Details() ErrorDetails {
|
|
||||||
return v.details
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the error
|
|
||||||
func (v ResultErrorFields) String() string {
|
|
||||||
// as a fallback, the value is displayed go style
|
|
||||||
valueString := fmt.Sprintf("%v", v.value)
|
|
||||||
|
|
||||||
// marshal the go value value to json
|
|
||||||
if v.value == nil {
|
|
||||||
valueString = TYPE_NULL
|
|
||||||
} else {
|
|
||||||
if vs, err := marshalToJSONString(v.value); err == nil {
|
|
||||||
if vs == nil {
|
|
||||||
valueString = TYPE_NULL
|
|
||||||
} else {
|
|
||||||
valueString = *vs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatErrorDescription(Locale.ErrorFormat(), ErrorDetails{
|
|
||||||
"context": v.context.String(),
|
|
||||||
"description": v.description,
|
|
||||||
"value": valueString,
|
|
||||||
"field": v.Field(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid indicates if no errors were found
|
|
||||||
func (v *Result) Valid() bool {
|
|
||||||
return len(v.errors) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors returns the errors that were found
|
|
||||||
func (v *Result) Errors() []ResultError {
|
|
||||||
return v.errors
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddError appends a fully filled error to the error set
|
|
||||||
// SetDescription() will be called with the result of the parsed err.DescriptionFormat()
|
|
||||||
func (v *Result) AddError(err ResultError, details ErrorDetails) {
|
|
||||||
if _, exists := details["context"]; !exists && err.Context() != nil {
|
|
||||||
details["context"] = err.Context().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
|
|
||||||
|
|
||||||
v.errors = append(v.errors, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Result) addInternalError(err ResultError, context *JsonContext, value interface{}, details ErrorDetails) {
|
|
||||||
newError(err, context, value, Locale, details)
|
|
||||||
v.errors = append(v.errors, err)
|
|
||||||
v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to copy errors from a sub-schema to the main one
|
|
||||||
func (v *Result) mergeErrors(otherResult *Result) {
|
|
||||||
v.errors = append(v.errors, otherResult.Errors()...)
|
|
||||||
v.score += otherResult.score
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Result) incrementScore() {
|
|
||||||
v.score++
|
|
||||||
}
|
|
1087
vendor/github.com/xeipuuv/gojsonschema/schema.go
generated
vendored
1087
vendor/github.com/xeipuuv/gojsonschema/schema.go
generated
vendored
File diff suppressed because it is too large
Load Diff
206
vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go
generated
vendored
206
vendor/github.com/xeipuuv/gojsonschema/schemaLoader.go
generated
vendored
@ -1,206 +0,0 @@
|
|||||||
// Copyright 2018 johandorland ( https://github.com/johandorland )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonreference"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SchemaLoader is used to load schemas
|
|
||||||
type SchemaLoader struct {
|
|
||||||
pool *schemaPool
|
|
||||||
AutoDetect bool
|
|
||||||
Validate bool
|
|
||||||
Draft Draft
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSchemaLoader creates a new NewSchemaLoader
|
|
||||||
func NewSchemaLoader() *SchemaLoader {
|
|
||||||
|
|
||||||
ps := &SchemaLoader{
|
|
||||||
pool: &schemaPool{
|
|
||||||
schemaPoolDocuments: make(map[string]*schemaPoolDocument),
|
|
||||||
},
|
|
||||||
AutoDetect: true,
|
|
||||||
Validate: false,
|
|
||||||
Draft: Hybrid,
|
|
||||||
}
|
|
||||||
ps.pool.autoDetect = &ps.AutoDetect
|
|
||||||
|
|
||||||
return ps
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sl *SchemaLoader) validateMetaschema(documentNode interface{}) error {
|
|
||||||
|
|
||||||
var (
|
|
||||||
schema string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if sl.AutoDetect {
|
|
||||||
schema, _, err = parseSchemaURL(documentNode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no explicit "$schema" is used, use the default metaschema associated with the draft used
|
|
||||||
if schema == "" {
|
|
||||||
if sl.Draft == Hybrid {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
schema = drafts.GetSchemaURL(sl.Draft)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Disable validation when loading the metaschema to prevent an infinite recursive loop
|
|
||||||
sl.Validate = false
|
|
||||||
|
|
||||||
metaSchema, err := sl.Compile(NewReferenceLoader(schema))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sl.Validate = true
|
|
||||||
|
|
||||||
result := metaSchema.validateDocument(documentNode)
|
|
||||||
|
|
||||||
if !result.Valid() {
|
|
||||||
var res bytes.Buffer
|
|
||||||
for _, err := range result.Errors() {
|
|
||||||
res.WriteString(err.String())
|
|
||||||
res.WriteString("\n")
|
|
||||||
}
|
|
||||||
return errors.New(res.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require
|
|
||||||
// an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema
|
|
||||||
func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error {
|
|
||||||
emptyRef, _ := gojsonreference.NewJsonReference("")
|
|
||||||
|
|
||||||
for _, loader := range loaders {
|
|
||||||
doc, err := loader.LoadJSON()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if sl.Validate {
|
|
||||||
if err := sl.validateMetaschema(doc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Directly use the Recursive function, so that it get only added to the schema pool by $id
|
|
||||||
// and not by the ref of the document as it's empty
|
|
||||||
if err = sl.pool.parseReferences(doc, emptyRef, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//AddSchema adds a schema under the provided URL to the schema cache
|
|
||||||
func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error {
|
|
||||||
|
|
||||||
ref, err := gojsonreference.NewJsonReference(url)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
doc, err := loader.LoadJSON()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if sl.Validate {
|
|
||||||
if err := sl.validateMetaschema(doc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sl.pool.parseReferences(doc, ref, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile loads and compiles a schema
|
|
||||||
func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) {
|
|
||||||
|
|
||||||
ref, err := rootSchema.JsonReference()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
d := Schema{}
|
|
||||||
d.pool = sl.pool
|
|
||||||
d.pool.jsonLoaderFactory = rootSchema.LoaderFactory()
|
|
||||||
d.documentReference = ref
|
|
||||||
d.referencePool = newSchemaReferencePool()
|
|
||||||
|
|
||||||
var doc interface{}
|
|
||||||
if ref.String() != "" {
|
|
||||||
// Get document from schema pool
|
|
||||||
spd, err := d.pool.GetDocument(d.documentReference)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
doc = spd.Document
|
|
||||||
} else {
|
|
||||||
// Load JSON directly
|
|
||||||
doc, err = rootSchema.LoadJSON()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// References need only be parsed if loading JSON directly
|
|
||||||
// as pool.GetDocument already does this for us if loading by reference
|
|
||||||
err = sl.pool.parseReferences(doc, ref, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sl.Validate {
|
|
||||||
if err := sl.validateMetaschema(doc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draft := sl.Draft
|
|
||||||
if sl.AutoDetect {
|
|
||||||
_, detectedDraft, err := parseSchemaURL(doc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if detectedDraft != nil {
|
|
||||||
draft = *detectedDraft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = d.parse(doc, draft)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &d, nil
|
|
||||||
}
|
|
215
vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
generated
vendored
215
vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
generated
vendored
@ -1,215 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Defines resources pooling.
|
|
||||||
// Eases referencing and avoids downloading the same resource twice.
|
|
||||||
//
|
|
||||||
// created 26-02-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonreference"
|
|
||||||
)
|
|
||||||
|
|
||||||
type schemaPoolDocument struct {
|
|
||||||
Document interface{}
|
|
||||||
Draft *Draft
|
|
||||||
}
|
|
||||||
|
|
||||||
type schemaPool struct {
|
|
||||||
schemaPoolDocuments map[string]*schemaPoolDocument
|
|
||||||
jsonLoaderFactory JSONLoaderFactory
|
|
||||||
autoDetect *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.JsonReference, pooled bool) error {
|
|
||||||
|
|
||||||
var (
|
|
||||||
draft *Draft
|
|
||||||
err error
|
|
||||||
reference = ref.String()
|
|
||||||
)
|
|
||||||
// Only the root document should be added to the schema pool if pooled is true
|
|
||||||
if _, ok := p.schemaPoolDocuments[reference]; pooled && ok {
|
|
||||||
return fmt.Errorf("Reference already exists: \"%s\"", reference)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *p.autoDetect {
|
|
||||||
_, draft, err = parseSchemaURL(document)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = p.parseReferencesRecursive(document, ref, draft)
|
|
||||||
|
|
||||||
if pooled {
|
|
||||||
p.schemaPoolDocuments[reference] = &schemaPoolDocument{Document: document, Draft: draft}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference, draft *Draft) error {
|
|
||||||
// parseReferencesRecursive parses a JSON document and resolves all $id and $ref references.
|
|
||||||
// For $ref references it takes into account the $id scope it is in and replaces
|
|
||||||
// the reference by the absolute resolved reference
|
|
||||||
|
|
||||||
// When encountering errors it fails silently. Error handling is done when the schema
|
|
||||||
// is syntactically parsed and any error encountered here should also come up there.
|
|
||||||
switch m := document.(type) {
|
|
||||||
case []interface{}:
|
|
||||||
for _, v := range m {
|
|
||||||
p.parseReferencesRecursive(v, ref, draft)
|
|
||||||
}
|
|
||||||
case map[string]interface{}:
|
|
||||||
localRef := &ref
|
|
||||||
|
|
||||||
keyID := KEY_ID_NEW
|
|
||||||
if existsMapKey(m, KEY_ID) {
|
|
||||||
keyID = KEY_ID
|
|
||||||
}
|
|
||||||
if existsMapKey(m, keyID) && isKind(m[keyID], reflect.String) {
|
|
||||||
jsonReference, err := gojsonreference.NewJsonReference(m[keyID].(string))
|
|
||||||
if err == nil {
|
|
||||||
localRef, err = ref.Inherits(jsonReference)
|
|
||||||
if err == nil {
|
|
||||||
if _, ok := p.schemaPoolDocuments[localRef.String()]; ok {
|
|
||||||
return fmt.Errorf("Reference already exists: \"%s\"", localRef.String())
|
|
||||||
}
|
|
||||||
p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document, Draft: draft}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if existsMapKey(m, KEY_REF) && isKind(m[KEY_REF], reflect.String) {
|
|
||||||
jsonReference, err := gojsonreference.NewJsonReference(m[KEY_REF].(string))
|
|
||||||
if err == nil {
|
|
||||||
absoluteRef, err := localRef.Inherits(jsonReference)
|
|
||||||
if err == nil {
|
|
||||||
m[KEY_REF] = absoluteRef.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range m {
|
|
||||||
// const and enums should be interpreted literally, so ignore them
|
|
||||||
if k == KEY_CONST || k == KEY_ENUM {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Something like a property or a dependency is not a valid schema, as it might describe properties named "$ref", "$id" or "const", etc
|
|
||||||
// Therefore don't treat it like a schema.
|
|
||||||
if k == KEY_PROPERTIES || k == KEY_DEPENDENCIES || k == KEY_PATTERN_PROPERTIES {
|
|
||||||
if child, ok := v.(map[string]interface{}); ok {
|
|
||||||
for _, v := range child {
|
|
||||||
p.parseReferencesRecursive(v, *localRef, draft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.parseReferencesRecursive(v, *localRef, draft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) {
|
|
||||||
|
|
||||||
var (
|
|
||||||
spd *schemaPoolDocument
|
|
||||||
draft *Draft
|
|
||||||
ok bool
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("Get Document ( %s )", reference.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a deep copy, so we can remove the fragment part later on without altering the original
|
|
||||||
refToURL, _ := gojsonreference.NewJsonReference(reference.String())
|
|
||||||
|
|
||||||
// First check if the given fragment is a location independent identifier
|
|
||||||
// http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3
|
|
||||||
|
|
||||||
if spd, ok = p.schemaPoolDocuments[refToURL.String()]; ok {
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog(" From pool")
|
|
||||||
}
|
|
||||||
return spd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the given reference is not a location independent identifier,
|
|
||||||
// strip the fragment and look for a document with it's base URI
|
|
||||||
|
|
||||||
refToURL.GetUrl().Fragment = ""
|
|
||||||
|
|
||||||
if cachedSpd, ok := p.schemaPoolDocuments[refToURL.String()]; ok {
|
|
||||||
document, _, err := reference.GetPointer().Get(cachedSpd.Document)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog(" From pool")
|
|
||||||
}
|
|
||||||
|
|
||||||
spd = &schemaPoolDocument{Document: document, Draft: cachedSpd.Draft}
|
|
||||||
p.schemaPoolDocuments[reference.String()] = spd
|
|
||||||
|
|
||||||
return spd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is not possible to load anything remotely that is not canonical...
|
|
||||||
if !reference.IsCanonical() {
|
|
||||||
return nil, errors.New(formatErrorDescription(
|
|
||||||
Locale.ReferenceMustBeCanonical(),
|
|
||||||
ErrorDetails{"reference": reference.String()},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String())
|
|
||||||
document, err := jsonReferenceLoader.LoadJSON()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the whole document to the pool for potential re-use
|
|
||||||
p.parseReferences(document, refToURL, true)
|
|
||||||
|
|
||||||
_, draft, _ = parseSchemaURL(document)
|
|
||||||
|
|
||||||
// resolve the potential fragment and also cache it
|
|
||||||
document, _, err = reference.GetPointer().Get(document)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &schemaPoolDocument{Document: document, Draft: draft}, nil
|
|
||||||
}
|
|
68
vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
generated
vendored
68
vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
generated
vendored
@ -1,68 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Pool of referenced schemas.
|
|
||||||
//
|
|
||||||
// created 25-06-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type schemaReferencePool struct {
|
|
||||||
documents map[string]*subSchema
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSchemaReferencePool() *schemaReferencePool {
|
|
||||||
|
|
||||||
p := &schemaReferencePool{}
|
|
||||||
p.documents = make(map[string]*subSchema)
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *schemaReferencePool) Get(ref string) (r *subSchema, o bool) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog(fmt.Sprintf("Schema Reference ( %s )", ref))
|
|
||||||
}
|
|
||||||
|
|
||||||
if sch, ok := p.documents[ref]; ok {
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog(fmt.Sprintf(" From pool"))
|
|
||||||
}
|
|
||||||
return sch, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *schemaReferencePool) Add(ref string, sch *subSchema) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog(fmt.Sprintf("Add Schema Reference %s to pool", ref))
|
|
||||||
}
|
|
||||||
if _, ok := p.documents[ref]; !ok {
|
|
||||||
p.documents[ref] = sch
|
|
||||||
}
|
|
||||||
}
|
|
83
vendor/github.com/xeipuuv/gojsonschema/schemaType.go
generated
vendored
83
vendor/github.com/xeipuuv/gojsonschema/schemaType.go
generated
vendored
@ -1,83 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Helper structure to handle schema types, and the combination of them.
|
|
||||||
//
|
|
||||||
// created 28-02-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type jsonSchemaType struct {
|
|
||||||
types []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the schema typed ? that is containing at least one type
|
|
||||||
// When not typed, the schema does not need any type validation
|
|
||||||
func (t *jsonSchemaType) IsTyped() bool {
|
|
||||||
return len(t.types) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *jsonSchemaType) Add(etype string) error {
|
|
||||||
|
|
||||||
if !isStringInSlice(JSON_TYPES, etype) {
|
|
||||||
return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"given": "/" + etype + "/", "expected": JSON_TYPES}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Contains(etype) {
|
|
||||||
return errors.New(formatErrorDescription(Locale.Duplicated(), ErrorDetails{"type": etype}))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.types = append(t.types, etype)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *jsonSchemaType) Contains(etype string) bool {
|
|
||||||
|
|
||||||
for _, v := range t.types {
|
|
||||||
if v == etype {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *jsonSchemaType) String() string {
|
|
||||||
|
|
||||||
if len(t.types) == 0 {
|
|
||||||
return STRING_UNDEFINED // should never happen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Displayed as a list [type1,type2,...]
|
|
||||||
if len(t.types) > 1 {
|
|
||||||
return fmt.Sprintf("[%s]", strings.Join(t.types, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only one type: name only
|
|
||||||
return t.types[0]
|
|
||||||
}
|
|
149
vendor/github.com/xeipuuv/gojsonschema/subSchema.go
generated
vendored
149
vendor/github.com/xeipuuv/gojsonschema/subSchema.go
generated
vendored
@ -1,149 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Defines the structure of a sub-subSchema.
|
|
||||||
// A sub-subSchema can contain other sub-schemas.
|
|
||||||
//
|
|
||||||
// created 27-02-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/xeipuuv/gojsonreference"
|
|
||||||
"math/big"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const (
|
|
||||||
KEY_SCHEMA = "$schema"
|
|
||||||
KEY_ID = "id"
|
|
||||||
KEY_ID_NEW = "$id"
|
|
||||||
KEY_REF = "$ref"
|
|
||||||
KEY_TITLE = "title"
|
|
||||||
KEY_DESCRIPTION = "description"
|
|
||||||
KEY_TYPE = "type"
|
|
||||||
KEY_ITEMS = "items"
|
|
||||||
KEY_ADDITIONAL_ITEMS = "additionalItems"
|
|
||||||
KEY_PROPERTIES = "properties"
|
|
||||||
KEY_PATTERN_PROPERTIES = "patternProperties"
|
|
||||||
KEY_ADDITIONAL_PROPERTIES = "additionalProperties"
|
|
||||||
KEY_PROPERTY_NAMES = "propertyNames"
|
|
||||||
KEY_DEFINITIONS = "definitions"
|
|
||||||
KEY_MULTIPLE_OF = "multipleOf"
|
|
||||||
KEY_MINIMUM = "minimum"
|
|
||||||
KEY_MAXIMUM = "maximum"
|
|
||||||
KEY_EXCLUSIVE_MINIMUM = "exclusiveMinimum"
|
|
||||||
KEY_EXCLUSIVE_MAXIMUM = "exclusiveMaximum"
|
|
||||||
KEY_MIN_LENGTH = "minLength"
|
|
||||||
KEY_MAX_LENGTH = "maxLength"
|
|
||||||
KEY_PATTERN = "pattern"
|
|
||||||
KEY_FORMAT = "format"
|
|
||||||
KEY_MIN_PROPERTIES = "minProperties"
|
|
||||||
KEY_MAX_PROPERTIES = "maxProperties"
|
|
||||||
KEY_DEPENDENCIES = "dependencies"
|
|
||||||
KEY_REQUIRED = "required"
|
|
||||||
KEY_MIN_ITEMS = "minItems"
|
|
||||||
KEY_MAX_ITEMS = "maxItems"
|
|
||||||
KEY_UNIQUE_ITEMS = "uniqueItems"
|
|
||||||
KEY_CONTAINS = "contains"
|
|
||||||
KEY_CONST = "const"
|
|
||||||
KEY_ENUM = "enum"
|
|
||||||
KEY_ONE_OF = "oneOf"
|
|
||||||
KEY_ANY_OF = "anyOf"
|
|
||||||
KEY_ALL_OF = "allOf"
|
|
||||||
KEY_NOT = "not"
|
|
||||||
KEY_IF = "if"
|
|
||||||
KEY_THEN = "then"
|
|
||||||
KEY_ELSE = "else"
|
|
||||||
)
|
|
||||||
|
|
||||||
type subSchema struct {
|
|
||||||
draft *Draft
|
|
||||||
|
|
||||||
// basic subSchema meta properties
|
|
||||||
id *gojsonreference.JsonReference
|
|
||||||
title *string
|
|
||||||
description *string
|
|
||||||
|
|
||||||
property string
|
|
||||||
|
|
||||||
// Quick pass/fail for boolean schemas
|
|
||||||
pass *bool
|
|
||||||
|
|
||||||
// Types associated with the subSchema
|
|
||||||
types jsonSchemaType
|
|
||||||
|
|
||||||
// Reference url
|
|
||||||
ref *gojsonreference.JsonReference
|
|
||||||
// Schema referenced
|
|
||||||
refSchema *subSchema
|
|
||||||
|
|
||||||
// hierarchy
|
|
||||||
parent *subSchema
|
|
||||||
itemsChildren []*subSchema
|
|
||||||
itemsChildrenIsSingleSchema bool
|
|
||||||
propertiesChildren []*subSchema
|
|
||||||
|
|
||||||
// validation : number / integer
|
|
||||||
multipleOf *big.Rat
|
|
||||||
maximum *big.Rat
|
|
||||||
exclusiveMaximum *big.Rat
|
|
||||||
minimum *big.Rat
|
|
||||||
exclusiveMinimum *big.Rat
|
|
||||||
|
|
||||||
// validation : string
|
|
||||||
minLength *int
|
|
||||||
maxLength *int
|
|
||||||
pattern *regexp.Regexp
|
|
||||||
format string
|
|
||||||
|
|
||||||
// validation : object
|
|
||||||
minProperties *int
|
|
||||||
maxProperties *int
|
|
||||||
required []string
|
|
||||||
|
|
||||||
dependencies map[string]interface{}
|
|
||||||
additionalProperties interface{}
|
|
||||||
patternProperties map[string]*subSchema
|
|
||||||
propertyNames *subSchema
|
|
||||||
|
|
||||||
// validation : array
|
|
||||||
minItems *int
|
|
||||||
maxItems *int
|
|
||||||
uniqueItems bool
|
|
||||||
contains *subSchema
|
|
||||||
|
|
||||||
additionalItems interface{}
|
|
||||||
|
|
||||||
// validation : all
|
|
||||||
_const *string //const is a golang keyword
|
|
||||||
enum []string
|
|
||||||
|
|
||||||
// validation : subSchema
|
|
||||||
oneOf []*subSchema
|
|
||||||
anyOf []*subSchema
|
|
||||||
allOf []*subSchema
|
|
||||||
not *subSchema
|
|
||||||
_if *subSchema // if/else are golang keywords
|
|
||||||
_then *subSchema
|
|
||||||
_else *subSchema
|
|
||||||
}
|
|
62
vendor/github.com/xeipuuv/gojsonschema/types.go
generated
vendored
62
vendor/github.com/xeipuuv/gojsonschema/types.go
generated
vendored
@ -1,62 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Contains const types for schema and JSON.
|
|
||||||
//
|
|
||||||
// created 28-02-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
// Type constants
|
|
||||||
const (
|
|
||||||
TYPE_ARRAY = `array`
|
|
||||||
TYPE_BOOLEAN = `boolean`
|
|
||||||
TYPE_INTEGER = `integer`
|
|
||||||
TYPE_NUMBER = `number`
|
|
||||||
TYPE_NULL = `null`
|
|
||||||
TYPE_OBJECT = `object`
|
|
||||||
TYPE_STRING = `string`
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSON_TYPES hosts the list of type that are supported in JSON
|
|
||||||
var JSON_TYPES []string
|
|
||||||
|
|
||||||
// SCHEMA_TYPES hosts the list of type that are supported in schemas
|
|
||||||
var SCHEMA_TYPES []string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
JSON_TYPES = []string{
|
|
||||||
TYPE_ARRAY,
|
|
||||||
TYPE_BOOLEAN,
|
|
||||||
TYPE_INTEGER,
|
|
||||||
TYPE_NUMBER,
|
|
||||||
TYPE_NULL,
|
|
||||||
TYPE_OBJECT,
|
|
||||||
TYPE_STRING}
|
|
||||||
|
|
||||||
SCHEMA_TYPES = []string{
|
|
||||||
TYPE_ARRAY,
|
|
||||||
TYPE_BOOLEAN,
|
|
||||||
TYPE_INTEGER,
|
|
||||||
TYPE_NUMBER,
|
|
||||||
TYPE_OBJECT,
|
|
||||||
TYPE_STRING}
|
|
||||||
}
|
|
197
vendor/github.com/xeipuuv/gojsonschema/utils.go
generated
vendored
197
vendor/github.com/xeipuuv/gojsonschema/utils.go
generated
vendored
@ -1,197 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Various utility functions.
|
|
||||||
//
|
|
||||||
// created 26-02-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isKind(what interface{}, kinds ...reflect.Kind) bool {
|
|
||||||
target := what
|
|
||||||
if isJSONNumber(what) {
|
|
||||||
// JSON Numbers are strings!
|
|
||||||
target = *mustBeNumber(what)
|
|
||||||
}
|
|
||||||
targetKind := reflect.ValueOf(target).Kind()
|
|
||||||
for _, kind := range kinds {
|
|
||||||
if targetKind == kind {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func existsMapKey(m map[string]interface{}, k string) bool {
|
|
||||||
_, ok := m[k]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func isStringInSlice(s []string, what string) bool {
|
|
||||||
for i := range s {
|
|
||||||
if s[i] == what {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s.
|
|
||||||
func indexStringInSlice(s []string, what string) int {
|
|
||||||
for i := range s {
|
|
||||||
if s[i] == what {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalToJSONString(value interface{}) (*string, error) {
|
|
||||||
|
|
||||||
mBytes, err := json.Marshal(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sBytes := string(mBytes)
|
|
||||||
return &sBytes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalWithoutNumber(value interface{}) (*string, error) {
|
|
||||||
|
|
||||||
// The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
|
|
||||||
// This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1
|
|
||||||
// One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber
|
|
||||||
// so that these differences in representation are removed
|
|
||||||
|
|
||||||
jsonString, err := marshalToJSONString(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var document interface{}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(*jsonString), &document)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return marshalToJSONString(document)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isJSONNumber(what interface{}) bool {
|
|
||||||
|
|
||||||
switch what.(type) {
|
|
||||||
|
|
||||||
case json.Number:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkJSONInteger(what interface{}) (isInt bool) {
|
|
||||||
|
|
||||||
jsonNumber := what.(json.Number)
|
|
||||||
|
|
||||||
bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber))
|
|
||||||
|
|
||||||
return isValidNumber && bigFloat.IsInt()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
|
||||||
const (
|
|
||||||
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
|
||||||
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
func mustBeInteger(what interface{}) *int {
|
|
||||||
|
|
||||||
if isJSONNumber(what) {
|
|
||||||
|
|
||||||
number := what.(json.Number)
|
|
||||||
|
|
||||||
isInt := checkJSONInteger(number)
|
|
||||||
|
|
||||||
if isInt {
|
|
||||||
|
|
||||||
int64Value, err := number.Int64()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
int32Value := int(int64Value)
|
|
||||||
return &int32Value
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustBeNumber(what interface{}) *big.Rat {
|
|
||||||
|
|
||||||
if isJSONNumber(what) {
|
|
||||||
number := what.(json.Number)
|
|
||||||
float64Value, success := new(big.Rat).SetString(string(number))
|
|
||||||
if success {
|
|
||||||
return float64Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertDocumentNode(val interface{}) interface{} {
|
|
||||||
|
|
||||||
if lval, ok := val.([]interface{}); ok {
|
|
||||||
|
|
||||||
res := []interface{}{}
|
|
||||||
for _, v := range lval {
|
|
||||||
res = append(res, convertDocumentNode(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if mval, ok := val.(map[interface{}]interface{}); ok {
|
|
||||||
|
|
||||||
res := map[string]interface{}{}
|
|
||||||
|
|
||||||
for k, v := range mval {
|
|
||||||
res[k.(string)] = convertDocumentNode(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
858
vendor/github.com/xeipuuv/gojsonschema/validation.go
generated
vendored
858
vendor/github.com/xeipuuv/gojsonschema/validation.go
generated
vendored
@ -1,858 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonschema
|
|
||||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
|
||||||
//
|
|
||||||
// description Extends Schema and subSchema, implements the validation phase.
|
|
||||||
//
|
|
||||||
// created 28-02-2013
|
|
||||||
|
|
||||||
package gojsonschema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validate loads and validates a JSON schema
|
|
||||||
func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) {
|
|
||||||
// load schema
|
|
||||||
schema, err := NewSchema(ls)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return schema.Validate(ld)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate loads and validates a JSON document
|
|
||||||
func (v *Schema) Validate(l JSONLoader) (*Result, error) {
|
|
||||||
root, err := l.LoadJSON()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return v.validateDocument(root), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Schema) validateDocument(root interface{}) *Result {
|
|
||||||
result := &Result{}
|
|
||||||
context := NewJsonContext(STRING_CONTEXT_ROOT, nil)
|
|
||||||
v.rootSchema.validateRecursive(v.rootSchema, root, result, context)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) subValidateWithContext(document interface{}, context *JsonContext) *Result {
|
|
||||||
result := &Result{}
|
|
||||||
v.validateRecursive(v, document, result, context)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walker function to validate the json recursively against the subSchema
|
|
||||||
func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateRecursive %s", context.String())
|
|
||||||
internalLog(" %v", currentNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle true/false schema as early as possible as all other fields will be nil
|
|
||||||
if currentSubSchema.pass != nil {
|
|
||||||
if !*currentSubSchema.pass {
|
|
||||||
result.addInternalError(
|
|
||||||
new(FalseError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle referenced schemas, returns directly when a $ref is found
|
|
||||||
if currentSubSchema.refSchema != nil {
|
|
||||||
v.validateRecursive(currentSubSchema.refSchema, currentNode, result, context)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for null value
|
|
||||||
if currentNode == nil {
|
|
||||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(InvalidTypeError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{
|
|
||||||
"expected": currentSubSchema.types.String(),
|
|
||||||
"given": TYPE_NULL,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSubSchema.validateSchema(currentSubSchema, currentNode, result, context)
|
|
||||||
v.validateCommon(currentSubSchema, currentNode, result, context)
|
|
||||||
|
|
||||||
} else { // Not a null value
|
|
||||||
|
|
||||||
if isJSONNumber(currentNode) {
|
|
||||||
|
|
||||||
value := currentNode.(json.Number)
|
|
||||||
|
|
||||||
isInt := checkJSONInteger(value)
|
|
||||||
|
|
||||||
validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isInt && currentSubSchema.types.Contains(TYPE_INTEGER))
|
|
||||||
|
|
||||||
if currentSubSchema.types.IsTyped() && !validType {
|
|
||||||
|
|
||||||
givenType := TYPE_INTEGER
|
|
||||||
if !isInt {
|
|
||||||
givenType = TYPE_NUMBER
|
|
||||||
}
|
|
||||||
|
|
||||||
result.addInternalError(
|
|
||||||
new(InvalidTypeError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{
|
|
||||||
"expected": currentSubSchema.types.String(),
|
|
||||||
"given": givenType,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSubSchema.validateSchema(currentSubSchema, value, result, context)
|
|
||||||
v.validateNumber(currentSubSchema, value, result, context)
|
|
||||||
v.validateCommon(currentSubSchema, value, result, context)
|
|
||||||
v.validateString(currentSubSchema, value, result, context)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
rValue := reflect.ValueOf(currentNode)
|
|
||||||
rKind := rValue.Kind()
|
|
||||||
|
|
||||||
switch rKind {
|
|
||||||
|
|
||||||
// Slice => JSON array
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
|
|
||||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(InvalidTypeError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{
|
|
||||||
"expected": currentSubSchema.types.String(),
|
|
||||||
"given": TYPE_ARRAY,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
castCurrentNode := currentNode.([]interface{})
|
|
||||||
|
|
||||||
currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
|
|
||||||
|
|
||||||
v.validateArray(currentSubSchema, castCurrentNode, result, context)
|
|
||||||
v.validateCommon(currentSubSchema, castCurrentNode, result, context)
|
|
||||||
|
|
||||||
// Map => JSON object
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(InvalidTypeError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{
|
|
||||||
"expected": currentSubSchema.types.String(),
|
|
||||||
"given": TYPE_OBJECT,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
castCurrentNode, ok := currentNode.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
castCurrentNode = convertDocumentNode(currentNode).(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
|
|
||||||
|
|
||||||
v.validateObject(currentSubSchema, castCurrentNode, result, context)
|
|
||||||
v.validateCommon(currentSubSchema, castCurrentNode, result, context)
|
|
||||||
|
|
||||||
for _, pSchema := range currentSubSchema.propertiesChildren {
|
|
||||||
nextNode, ok := castCurrentNode[pSchema.property]
|
|
||||||
if ok {
|
|
||||||
subContext := NewJsonContext(pSchema.property, context)
|
|
||||||
v.validateRecursive(pSchema, nextNode, result, subContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple JSON values : string, number, boolean
|
|
||||||
|
|
||||||
case reflect.Bool:
|
|
||||||
|
|
||||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(InvalidTypeError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{
|
|
||||||
"expected": currentSubSchema.types.String(),
|
|
||||||
"given": TYPE_BOOLEAN,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
value := currentNode.(bool)
|
|
||||||
|
|
||||||
currentSubSchema.validateSchema(currentSubSchema, value, result, context)
|
|
||||||
v.validateNumber(currentSubSchema, value, result, context)
|
|
||||||
v.validateCommon(currentSubSchema, value, result, context)
|
|
||||||
v.validateString(currentSubSchema, value, result, context)
|
|
||||||
|
|
||||||
case reflect.String:
|
|
||||||
|
|
||||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(InvalidTypeError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{
|
|
||||||
"expected": currentSubSchema.types.String(),
|
|
||||||
"given": TYPE_STRING,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
value := currentNode.(string)
|
|
||||||
|
|
||||||
currentSubSchema.validateSchema(currentSubSchema, value, result, context)
|
|
||||||
v.validateNumber(currentSubSchema, value, result, context)
|
|
||||||
v.validateCommon(currentSubSchema, value, result, context)
|
|
||||||
v.validateString(currentSubSchema, value, result, context)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Different kinds of validation there, subSchema / common / array / object / string...
|
|
||||||
func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateSchema %s", context.String())
|
|
||||||
internalLog(" %v", currentNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(currentSubSchema.anyOf) > 0 {
|
|
||||||
|
|
||||||
validatedAnyOf := false
|
|
||||||
var bestValidationResult *Result
|
|
||||||
|
|
||||||
for _, anyOfSchema := range currentSubSchema.anyOf {
|
|
||||||
if !validatedAnyOf {
|
|
||||||
validationResult := anyOfSchema.subValidateWithContext(currentNode, context)
|
|
||||||
validatedAnyOf = validationResult.Valid()
|
|
||||||
|
|
||||||
if !validatedAnyOf && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) {
|
|
||||||
bestValidationResult = validationResult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !validatedAnyOf {
|
|
||||||
|
|
||||||
result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
|
|
||||||
|
|
||||||
if bestValidationResult != nil {
|
|
||||||
// add error messages of closest matching subSchema as
|
|
||||||
// that's probably the one the user was trying to match
|
|
||||||
result.mergeErrors(bestValidationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(currentSubSchema.oneOf) > 0 {
|
|
||||||
|
|
||||||
nbValidated := 0
|
|
||||||
var bestValidationResult *Result
|
|
||||||
|
|
||||||
for _, oneOfSchema := range currentSubSchema.oneOf {
|
|
||||||
validationResult := oneOfSchema.subValidateWithContext(currentNode, context)
|
|
||||||
if validationResult.Valid() {
|
|
||||||
nbValidated++
|
|
||||||
} else if nbValidated == 0 && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) {
|
|
||||||
bestValidationResult = validationResult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nbValidated != 1 {
|
|
||||||
|
|
||||||
result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
|
|
||||||
|
|
||||||
if nbValidated == 0 {
|
|
||||||
// add error messages of closest matching subSchema as
|
|
||||||
// that's probably the one the user was trying to match
|
|
||||||
result.mergeErrors(bestValidationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(currentSubSchema.allOf) > 0 {
|
|
||||||
nbValidated := 0
|
|
||||||
|
|
||||||
for _, allOfSchema := range currentSubSchema.allOf {
|
|
||||||
validationResult := allOfSchema.subValidateWithContext(currentNode, context)
|
|
||||||
if validationResult.Valid() {
|
|
||||||
nbValidated++
|
|
||||||
}
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
if nbValidated != len(currentSubSchema.allOf) {
|
|
||||||
result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentSubSchema.not != nil {
|
|
||||||
validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context)
|
|
||||||
if validationResult.Valid() {
|
|
||||||
result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentSubSchema.dependencies != nil && len(currentSubSchema.dependencies) > 0 {
|
|
||||||
if isKind(currentNode, reflect.Map) {
|
|
||||||
for elementKey := range currentNode.(map[string]interface{}) {
|
|
||||||
if dependency, ok := currentSubSchema.dependencies[elementKey]; ok {
|
|
||||||
switch dependency := dependency.(type) {
|
|
||||||
|
|
||||||
case []string:
|
|
||||||
for _, dependOnKey := range dependency {
|
|
||||||
if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved {
|
|
||||||
result.addInternalError(
|
|
||||||
new(MissingDependencyError),
|
|
||||||
context,
|
|
||||||
currentNode,
|
|
||||||
ErrorDetails{"dependency": dependOnKey},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case *subSchema:
|
|
||||||
dependency.validateRecursive(dependency, currentNode, result, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentSubSchema._if != nil {
|
|
||||||
validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context)
|
|
||||||
if currentSubSchema._then != nil && validationResultIf.Valid() {
|
|
||||||
validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context)
|
|
||||||
if !validationResultThen.Valid() {
|
|
||||||
result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{})
|
|
||||||
result.mergeErrors(validationResultThen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentSubSchema._else != nil && !validationResultIf.Valid() {
|
|
||||||
validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context)
|
|
||||||
if !validationResultElse.Valid() {
|
|
||||||
result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{})
|
|
||||||
result.mergeErrors(validationResultElse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateCommon %s", context.String())
|
|
||||||
internalLog(" %v", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// const:
|
|
||||||
if currentSubSchema._const != nil {
|
|
||||||
vString, err := marshalWithoutNumber(value)
|
|
||||||
if err != nil {
|
|
||||||
result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
|
|
||||||
}
|
|
||||||
if *vString != *currentSubSchema._const {
|
|
||||||
result.addInternalError(new(ConstError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{
|
|
||||||
"allowed": *currentSubSchema._const,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enum:
|
|
||||||
if len(currentSubSchema.enum) > 0 {
|
|
||||||
vString, err := marshalWithoutNumber(value)
|
|
||||||
if err != nil {
|
|
||||||
result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
|
|
||||||
}
|
|
||||||
if !isStringInSlice(currentSubSchema.enum, *vString) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(EnumError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{
|
|
||||||
"allowed": strings.Join(currentSubSchema.enum, ", "),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateArray %s", context.String())
|
|
||||||
internalLog(" %v", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
nbValues := len(value)
|
|
||||||
|
|
||||||
// TODO explain
|
|
||||||
if currentSubSchema.itemsChildrenIsSingleSchema {
|
|
||||||
for i := range value {
|
|
||||||
subContext := NewJsonContext(strconv.Itoa(i), context)
|
|
||||||
validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext)
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if currentSubSchema.itemsChildren != nil && len(currentSubSchema.itemsChildren) > 0 {
|
|
||||||
|
|
||||||
nbItems := len(currentSubSchema.itemsChildren)
|
|
||||||
|
|
||||||
// while we have both schemas and values, check them against each other
|
|
||||||
for i := 0; i != nbItems && i != nbValues; i++ {
|
|
||||||
subContext := NewJsonContext(strconv.Itoa(i), context)
|
|
||||||
validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
if nbItems < nbValues {
|
|
||||||
// we have less schemas than elements in the instance array,
|
|
||||||
// but that might be ok if "additionalItems" is specified.
|
|
||||||
|
|
||||||
switch currentSubSchema.additionalItems.(type) {
|
|
||||||
case bool:
|
|
||||||
if !currentSubSchema.additionalItems.(bool) {
|
|
||||||
result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
|
|
||||||
}
|
|
||||||
case *subSchema:
|
|
||||||
additionalItemSchema := currentSubSchema.additionalItems.(*subSchema)
|
|
||||||
for i := nbItems; i != nbValues; i++ {
|
|
||||||
subContext := NewJsonContext(strconv.Itoa(i), context)
|
|
||||||
validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext)
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// minItems & maxItems
|
|
||||||
if currentSubSchema.minItems != nil {
|
|
||||||
if nbValues < int(*currentSubSchema.minItems) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(ArrayMinItemsError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"min": *currentSubSchema.minItems},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentSubSchema.maxItems != nil {
|
|
||||||
if nbValues > int(*currentSubSchema.maxItems) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(ArrayMaxItemsError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"max": *currentSubSchema.maxItems},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// uniqueItems:
|
|
||||||
if currentSubSchema.uniqueItems {
|
|
||||||
var stringifiedItems = make(map[string]int)
|
|
||||||
for j, v := range value {
|
|
||||||
vString, err := marshalWithoutNumber(v)
|
|
||||||
if err != nil {
|
|
||||||
result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err})
|
|
||||||
}
|
|
||||||
if i, ok := stringifiedItems[*vString]; ok {
|
|
||||||
result.addInternalError(
|
|
||||||
new(ItemsMustBeUniqueError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
stringifiedItems[*vString] = j
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// contains:
|
|
||||||
|
|
||||||
if currentSubSchema.contains != nil {
|
|
||||||
validatedOne := false
|
|
||||||
var bestValidationResult *Result
|
|
||||||
|
|
||||||
for i, v := range value {
|
|
||||||
subContext := NewJsonContext(strconv.Itoa(i), context)
|
|
||||||
|
|
||||||
validationResult := currentSubSchema.contains.subValidateWithContext(v, subContext)
|
|
||||||
if validationResult.Valid() {
|
|
||||||
validatedOne = true
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
if bestValidationResult == nil || validationResult.score > bestValidationResult.score {
|
|
||||||
bestValidationResult = validationResult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !validatedOne {
|
|
||||||
result.addInternalError(
|
|
||||||
new(ArrayContainsError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{},
|
|
||||||
)
|
|
||||||
if bestValidationResult != nil {
|
|
||||||
result.mergeErrors(bestValidationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateObject %s", context.String())
|
|
||||||
internalLog(" %v", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// minProperties & maxProperties:
|
|
||||||
if currentSubSchema.minProperties != nil {
|
|
||||||
if len(value) < int(*currentSubSchema.minProperties) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(ArrayMinPropertiesError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"min": *currentSubSchema.minProperties},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentSubSchema.maxProperties != nil {
|
|
||||||
if len(value) > int(*currentSubSchema.maxProperties) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(ArrayMaxPropertiesError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"max": *currentSubSchema.maxProperties},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// required:
|
|
||||||
for _, requiredProperty := range currentSubSchema.required {
|
|
||||||
_, ok := value[requiredProperty]
|
|
||||||
if ok {
|
|
||||||
result.incrementScore()
|
|
||||||
} else {
|
|
||||||
result.addInternalError(
|
|
||||||
new(RequiredError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"property": requiredProperty},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// additionalProperty & patternProperty:
|
|
||||||
for pk := range value {
|
|
||||||
|
|
||||||
// Check whether this property is described by "properties"
|
|
||||||
found := false
|
|
||||||
for _, spValue := range currentSubSchema.propertiesChildren {
|
|
||||||
if pk == spValue.property {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether this property is described by "patternProperties"
|
|
||||||
ppMatch := 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},
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
case *subSchema:
|
|
||||||
validationResult := ap.subValidateWithContext(value[pk], NewJsonContext(pk, context))
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// propertyNames:
|
|
||||||
if currentSubSchema.propertyNames != nil {
|
|
||||||
for pk := range value {
|
|
||||||
validationResult := currentSubSchema.propertyNames.subValidateWithContext(pk, context)
|
|
||||||
if !validationResult.Valid() {
|
|
||||||
result.addInternalError(new(InvalidPropertyNameError),
|
|
||||||
context,
|
|
||||||
value, ErrorDetails{
|
|
||||||
"property": pk,
|
|
||||||
})
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *JsonContext) bool {
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validatePatternProperty %s", context.String())
|
|
||||||
internalLog(" %s %v", key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
validated := false
|
|
||||||
|
|
||||||
for pk, pv := range currentSubSchema.patternProperties {
|
|
||||||
if matches, _ := regexp.MatchString(pk, key); matches {
|
|
||||||
validated = true
|
|
||||||
subContext := NewJsonContext(key, context)
|
|
||||||
validationResult := pv.subValidateWithContext(value, subContext)
|
|
||||||
result.mergeErrors(validationResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validated {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
// Ignore JSON numbers
|
|
||||||
if isJSONNumber(value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore non strings
|
|
||||||
if !isKind(value, reflect.String) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateString %s", context.String())
|
|
||||||
internalLog(" %v", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
stringValue := value.(string)
|
|
||||||
|
|
||||||
// minLength & maxLength:
|
|
||||||
if currentSubSchema.minLength != nil {
|
|
||||||
if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(StringLengthGTEError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"min": *currentSubSchema.minLength},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentSubSchema.maxLength != nil {
|
|
||||||
if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(StringLengthLTEError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"max": *currentSubSchema.maxLength},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pattern:
|
|
||||||
if currentSubSchema.pattern != nil {
|
|
||||||
if !currentSubSchema.pattern.MatchString(stringValue) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(DoesNotMatchPatternError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"pattern": currentSubSchema.pattern},
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// format
|
|
||||||
if currentSubSchema.format != "" {
|
|
||||||
if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(DoesNotMatchFormatError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"format": currentSubSchema.format},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
|
|
||||||
|
|
||||||
// Ignore non numbers
|
|
||||||
if !isJSONNumber(value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if internalLogEnabled {
|
|
||||||
internalLog("validateNumber %s", context.String())
|
|
||||||
internalLog(" %v", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
number := value.(json.Number)
|
|
||||||
float64Value, _ := new(big.Rat).SetString(string(number))
|
|
||||||
|
|
||||||
// multipleOf:
|
|
||||||
if currentSubSchema.multipleOf != nil {
|
|
||||||
if q := new(big.Rat).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() {
|
|
||||||
result.addInternalError(
|
|
||||||
new(MultipleOfError),
|
|
||||||
context,
|
|
||||||
number,
|
|
||||||
ErrorDetails{
|
|
||||||
"multiple": new(big.Float).SetRat(currentSubSchema.multipleOf),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//maximum & exclusiveMaximum:
|
|
||||||
if currentSubSchema.maximum != nil {
|
|
||||||
if float64Value.Cmp(currentSubSchema.maximum) == 1 {
|
|
||||||
result.addInternalError(
|
|
||||||
new(NumberLTEError),
|
|
||||||
context,
|
|
||||||
number,
|
|
||||||
ErrorDetails{
|
|
||||||
"max": new(big.Float).SetRat(currentSubSchema.maximum),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentSubSchema.exclusiveMaximum != nil {
|
|
||||||
if float64Value.Cmp(currentSubSchema.exclusiveMaximum) >= 0 {
|
|
||||||
result.addInternalError(
|
|
||||||
new(NumberLTError),
|
|
||||||
context,
|
|
||||||
number,
|
|
||||||
ErrorDetails{
|
|
||||||
"max": new(big.Float).SetRat(currentSubSchema.exclusiveMaximum),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//minimum & exclusiveMinimum:
|
|
||||||
if currentSubSchema.minimum != nil {
|
|
||||||
if float64Value.Cmp(currentSubSchema.minimum) == -1 {
|
|
||||||
result.addInternalError(
|
|
||||||
new(NumberGTEError),
|
|
||||||
context,
|
|
||||||
number,
|
|
||||||
ErrorDetails{
|
|
||||||
"min": new(big.Float).SetRat(currentSubSchema.minimum),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentSubSchema.exclusiveMinimum != nil {
|
|
||||||
if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 {
|
|
||||||
result.addInternalError(
|
|
||||||
new(NumberGTError),
|
|
||||||
context,
|
|
||||||
number,
|
|
||||||
ErrorDetails{
|
|
||||||
"min": new(big.Float).SetRat(currentSubSchema.exclusiveMinimum),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// format
|
|
||||||
if currentSubSchema.format != "" {
|
|
||||||
if !FormatCheckers.IsFormat(currentSubSchema.format, float64Value) {
|
|
||||||
result.addInternalError(
|
|
||||||
new(DoesNotMatchFormatError),
|
|
||||||
context,
|
|
||||||
value,
|
|
||||||
ErrorDetails{"format": currentSubSchema.format},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.incrementScore()
|
|
||||||
}
|
|
14
vendor/modules.txt
vendored
14
vendor/modules.txt
vendored
@ -453,7 +453,7 @@ github.com/oklog/ulid
|
|||||||
# github.com/opencontainers/go-digest v1.0.0
|
# github.com/opencontainers/go-digest v1.0.0
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/opencontainers/go-digest
|
github.com/opencontainers/go-digest
|
||||||
# github.com/opencontainers/image-spec v1.1.0
|
# github.com/opencontainers/image-spec v1.1.1
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/opencontainers/image-spec/schema
|
github.com/opencontainers/image-spec/schema
|
||||||
github.com/opencontainers/image-spec/specs-go
|
github.com/opencontainers/image-spec/specs-go
|
||||||
@ -492,6 +492,9 @@ github.com/proglottis/gpgme
|
|||||||
github.com/rivo/uniseg
|
github.com/rivo/uniseg
|
||||||
# github.com/russross/blackfriday v2.0.0+incompatible
|
# github.com/russross/blackfriday v2.0.0+incompatible
|
||||||
## explicit
|
## explicit
|
||||||
|
# github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||||
|
## explicit; go 1.19
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5
|
||||||
# github.com/secure-systems-lab/go-securesystemslib v0.9.0
|
# github.com/secure-systems-lab/go-securesystemslib v0.9.0
|
||||||
## explicit; go 1.20
|
## explicit; go 1.20
|
||||||
github.com/secure-systems-lab/go-securesystemslib/encrypted
|
github.com/secure-systems-lab/go-securesystemslib/encrypted
|
||||||
@ -573,15 +576,6 @@ github.com/vbauerster/mpb/v8
|
|||||||
github.com/vbauerster/mpb/v8/cwriter
|
github.com/vbauerster/mpb/v8/cwriter
|
||||||
github.com/vbauerster/mpb/v8/decor
|
github.com/vbauerster/mpb/v8/decor
|
||||||
github.com/vbauerster/mpb/v8/internal
|
github.com/vbauerster/mpb/v8/internal
|
||||||
# github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb
|
|
||||||
## explicit
|
|
||||||
github.com/xeipuuv/gojsonpointer
|
|
||||||
# github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415
|
|
||||||
## explicit
|
|
||||||
github.com/xeipuuv/gojsonreference
|
|
||||||
# github.com/xeipuuv/gojsonschema v1.2.0
|
|
||||||
## explicit
|
|
||||||
github.com/xeipuuv/gojsonschema
|
|
||||||
# go.mongodb.org/mongo-driver v1.14.0
|
# go.mongodb.org/mongo-driver v1.14.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
go.mongodb.org/mongo-driver/bson
|
go.mongodb.org/mongo-driver/bson
|
||||||
|
Loading…
Reference in New Issue
Block a user