mirror of https://github.com/docker/cli.git
287 lines
5.8 KiB
Go
287 lines
5.8 KiB
Go
|
// 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"
|
||
|
"path/filepath"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/xeipuuv/gojsonreference"
|
||
|
)
|
||
|
|
||
|
// JSON loader interface
|
||
|
|
||
|
type JSONLoader interface {
|
||
|
jsonSource() interface{}
|
||
|
loadJSON() (interface{}, error)
|
||
|
loadSchema() (*Schema, error)
|
||
|
}
|
||
|
|
||
|
// JSON Reference loader
|
||
|
// references are used to load JSONs from files and HTTP
|
||
|
|
||
|
type jsonReferenceLoader struct {
|
||
|
source string
|
||
|
}
|
||
|
|
||
|
func (l *jsonReferenceLoader) jsonSource() interface{} {
|
||
|
return l.source
|
||
|
}
|
||
|
|
||
|
func NewReferenceLoader(source string) *jsonReferenceLoader {
|
||
|
return &jsonReferenceLoader{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.Replace(refToUrl.String(), "file://", "", -1)
|
||
|
if runtime.GOOS == "windows" {
|
||
|
// on Windows, a file URL may have an extra leading slash, use slashes
|
||
|
// instead of backslashes, and have spaces escaped
|
||
|
if strings.HasPrefix(filename, "/") {
|
||
|
filename = filename[1:]
|
||
|
}
|
||
|
filename = filepath.FromSlash(filename)
|
||
|
filename = strings.Replace(filename, "%20", " ", -1)
|
||
|
}
|
||
|
|
||
|
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) loadSchema() (*Schema, error) {
|
||
|
|
||
|
var err error
|
||
|
|
||
|
d := Schema{}
|
||
|
d.pool = newSchemaPool()
|
||
|
d.referencePool = newSchemaReferencePool()
|
||
|
|
||
|
d.documentReference, err = gojsonreference.NewJsonReference(l.jsonSource().(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
spd, err := d.pool.GetDocument(d.documentReference)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
err = d.parse(spd.Document)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &d, nil
|
||
|
|
||
|
}
|
||
|
|
||
|
func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
|
||
|
|
||
|
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) {
|
||
|
|
||
|
bodyBuff, err := ioutil.ReadFile(path)
|
||
|
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 NewStringLoader(source string) *jsonStringLoader {
|
||
|
return &jsonStringLoader{source: source}
|
||
|
}
|
||
|
|
||
|
func (l *jsonStringLoader) loadJSON() (interface{}, error) {
|
||
|
|
||
|
return decodeJsonUsingNumber(strings.NewReader(l.jsonSource().(string)))
|
||
|
|
||
|
}
|
||
|
|
||
|
func (l *jsonStringLoader) loadSchema() (*Schema, error) {
|
||
|
|
||
|
var err error
|
||
|
|
||
|
document, err := l.loadJSON()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
d := Schema{}
|
||
|
d.pool = newSchemaPool()
|
||
|
d.referencePool = newSchemaReferencePool()
|
||
|
d.documentReference, err = gojsonreference.NewJsonReference("#")
|
||
|
d.pool.SetStandaloneDocument(document)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
err = d.parse(document)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &d, nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// 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 NewGoLoader(source interface{}) *jsonGoLoader {
|
||
|
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))
|
||
|
|
||
|
}
|
||
|
|
||
|
func (l *jsonGoLoader) loadSchema() (*Schema, error) {
|
||
|
|
||
|
var err error
|
||
|
|
||
|
document, err := l.loadJSON()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
d := Schema{}
|
||
|
d.pool = newSchemaPool()
|
||
|
d.referencePool = newSchemaReferencePool()
|
||
|
d.documentReference, err = gojsonreference.NewJsonReference("#")
|
||
|
d.pool.SetStandaloneDocument(document)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
err = d.parse(document)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &d, nil
|
||
|
|
||
|
}
|
||
|
|
||
|
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
|
||
|
|
||
|
}
|