| // Copyright 2015 go-swagger maintainers |
| // |
| // 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 loads |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "net/url" |
| |
| "github.com/go-openapi/analysis" |
| "github.com/go-openapi/spec" |
| "github.com/go-openapi/swag" |
| ) |
| |
| // JSONDoc loads a json document from either a file or a remote url |
| func JSONDoc(path string) (json.RawMessage, error) { |
| data, err := swag.LoadFromFileOrHTTP(path) |
| if err != nil { |
| return nil, err |
| } |
| return json.RawMessage(data), nil |
| } |
| |
| // DocLoader represents a doc loader type |
| type DocLoader func(string) (json.RawMessage, error) |
| |
| // DocMatcher represents a predicate to check if a loader matches |
| type DocMatcher func(string) bool |
| |
| var ( |
| loaders *loader |
| defaultLoader *loader |
| ) |
| |
| func init() { |
| defaultLoader = &loader{Match: func(_ string) bool { return true }, Fn: JSONDoc} |
| loaders = defaultLoader |
| spec.PathLoader = loaders.Fn |
| AddLoader(swag.YAMLMatcher, swag.YAMLDoc) |
| } |
| |
| // AddLoader for a document |
| func AddLoader(predicate DocMatcher, load DocLoader) { |
| prev := loaders |
| loaders = &loader{ |
| Match: predicate, |
| Fn: load, |
| Next: prev, |
| } |
| spec.PathLoader = loaders.Fn |
| } |
| |
| type loader struct { |
| Fn DocLoader |
| Match DocMatcher |
| Next *loader |
| } |
| |
| // JSONSpec loads a spec from a json document |
| func JSONSpec(path string) (*Document, error) { |
| data, err := JSONDoc(path) |
| if err != nil { |
| return nil, err |
| } |
| // convert to json |
| return Analyzed(json.RawMessage(data), "") |
| } |
| |
| // Document represents a swagger spec document |
| type Document struct { |
| // specAnalyzer |
| Analyzer *analysis.Spec |
| spec *spec.Swagger |
| specFilePath string |
| origSpec *spec.Swagger |
| schema *spec.Schema |
| raw json.RawMessage |
| } |
| |
| // Embedded returns a Document based on embedded specs. No analysis is required |
| func Embedded(orig, flat json.RawMessage) (*Document, error) { |
| var origSpec, flatSpec spec.Swagger |
| if err := json.Unmarshal(orig, &origSpec); err != nil { |
| return nil, err |
| } |
| if err := json.Unmarshal(flat, &flatSpec); err != nil { |
| return nil, err |
| } |
| return &Document{ |
| raw: orig, |
| origSpec: &origSpec, |
| spec: &flatSpec, |
| }, nil |
| } |
| |
| // Spec loads a new spec document |
| func Spec(path string) (*Document, error) { |
| specURL, err := url.Parse(path) |
| if err != nil { |
| return nil, err |
| } |
| var lastErr error |
| for l := loaders.Next; l != nil; l = l.Next { |
| if loaders.Match(specURL.Path) { |
| b, err2 := loaders.Fn(path) |
| if err2 != nil { |
| lastErr = err2 |
| continue |
| } |
| doc, err := Analyzed(b, "") |
| if err != nil { |
| return nil, err |
| } |
| if doc != nil { |
| doc.specFilePath = path |
| } |
| return doc, nil |
| } |
| } |
| if lastErr != nil { |
| return nil, lastErr |
| } |
| b, err := defaultLoader.Fn(path) |
| if err != nil { |
| return nil, err |
| } |
| |
| document, err := Analyzed(b, "") |
| if document != nil { |
| document.specFilePath = path |
| } |
| |
| return document, err |
| } |
| |
| // Analyzed creates a new analyzed spec document |
| func Analyzed(data json.RawMessage, version string) (*Document, error) { |
| if version == "" { |
| version = "2.0" |
| } |
| if version != "2.0" { |
| return nil, fmt.Errorf("spec version %q is not supported", version) |
| } |
| |
| raw := data |
| trimmed := bytes.TrimSpace(data) |
| if len(trimmed) > 0 { |
| if trimmed[0] != '{' && trimmed[0] != '[' { |
| yml, err := swag.BytesToYAMLDoc(trimmed) |
| if err != nil { |
| return nil, fmt.Errorf("analyzed: %v", err) |
| } |
| d, err := swag.YAMLToJSON(yml) |
| if err != nil { |
| return nil, fmt.Errorf("analyzed: %v", err) |
| } |
| raw = d |
| } |
| } |
| |
| swspec := new(spec.Swagger) |
| if err := json.Unmarshal(raw, swspec); err != nil { |
| return nil, err |
| } |
| |
| origsqspec := new(spec.Swagger) |
| if err := json.Unmarshal(raw, origsqspec); err != nil { |
| return nil, err |
| } |
| |
| d := &Document{ |
| Analyzer: analysis.New(swspec), |
| schema: spec.MustLoadSwagger20Schema(), |
| spec: swspec, |
| raw: raw, |
| origSpec: origsqspec, |
| } |
| return d, nil |
| } |
| |
| // Expanded expands the ref fields in the spec document and returns a new spec document |
| func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { |
| swspec := new(spec.Swagger) |
| if err := json.Unmarshal(d.raw, swspec); err != nil { |
| return nil, err |
| } |
| |
| var expandOptions *spec.ExpandOptions |
| if len(options) > 0 { |
| expandOptions = options[0] |
| } else { |
| expandOptions = &spec.ExpandOptions{ |
| RelativeBase: d.specFilePath, |
| } |
| } |
| |
| if err := spec.ExpandSpec(swspec, expandOptions); err != nil { |
| return nil, err |
| } |
| |
| dd := &Document{ |
| Analyzer: analysis.New(swspec), |
| spec: swspec, |
| specFilePath: d.specFilePath, |
| schema: spec.MustLoadSwagger20Schema(), |
| raw: d.raw, |
| origSpec: d.origSpec, |
| } |
| return dd, nil |
| } |
| |
| // BasePath the base path for this spec |
| func (d *Document) BasePath() string { |
| return d.spec.BasePath |
| } |
| |
| // Version returns the version of this spec |
| func (d *Document) Version() string { |
| return d.spec.Swagger |
| } |
| |
| // Schema returns the swagger 2.0 schema |
| func (d *Document) Schema() *spec.Schema { |
| return d.schema |
| } |
| |
| // Spec returns the swagger spec object model |
| func (d *Document) Spec() *spec.Swagger { |
| return d.spec |
| } |
| |
| // Host returns the host for the API |
| func (d *Document) Host() string { |
| return d.spec.Host |
| } |
| |
| // Raw returns the raw swagger spec as json bytes |
| func (d *Document) Raw() json.RawMessage { |
| return d.raw |
| } |
| |
| func (d *Document) OrigSpec() *spec.Swagger { |
| return d.origSpec |
| } |
| |
| // ResetDefinitions gives a shallow copy with the models reset |
| func (d *Document) ResetDefinitions() *Document { |
| defs := make(map[string]spec.Schema, len(d.origSpec.Definitions)) |
| for k, v := range d.origSpec.Definitions { |
| defs[k] = v |
| } |
| |
| d.spec.Definitions = defs |
| return d |
| } |
| |
| // Pristine creates a new pristine document instance based on the input data |
| func (d *Document) Pristine() *Document { |
| dd, _ := Analyzed(d.Raw(), d.Version()) |
| return dd |
| } |
| |
| // SpecFilePath returns the file path of the spec if one is defined |
| func (d *Document) SpecFilePath() string { |
| return d.specFilePath |
| } |