podman
266 строк · 6.2 Кб
1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package loads
16
17import (
18"bytes"
19"encoding/gob"
20"encoding/json"
21"fmt"
22
23"github.com/go-openapi/analysis"
24"github.com/go-openapi/spec"
25"github.com/go-openapi/swag"
26)
27
28func init() {
29gob.Register(map[string]interface{}{})
30gob.Register([]interface{}{})
31}
32
33// Document represents a swagger spec document
34type Document struct {
35// specAnalyzer
36Analyzer *analysis.Spec
37spec *spec.Swagger
38specFilePath string
39origSpec *spec.Swagger
40schema *spec.Schema
41raw json.RawMessage
42pathLoader *loader
43}
44
45// JSONSpec loads a spec from a json document
46func JSONSpec(path string, options ...LoaderOption) (*Document, error) {
47data, err := JSONDoc(path)
48if err != nil {
49return nil, err
50}
51// convert to json
52return Analyzed(data, "", options...)
53}
54
55// Embedded returns a Document based on embedded specs. No analysis is required
56func Embedded(orig, flat json.RawMessage, options ...LoaderOption) (*Document, error) {
57var origSpec, flatSpec spec.Swagger
58if err := json.Unmarshal(orig, &origSpec); err != nil {
59return nil, err
60}
61if err := json.Unmarshal(flat, &flatSpec); err != nil {
62return nil, err
63}
64return &Document{
65raw: orig,
66origSpec: &origSpec,
67spec: &flatSpec,
68pathLoader: loaderFromOptions(options),
69}, nil
70}
71
72// Spec loads a new spec document from a local or remote path
73func Spec(path string, options ...LoaderOption) (*Document, error) {
74
75ldr := loaderFromOptions(options)
76
77b, err := ldr.Load(path)
78if err != nil {
79return nil, err
80}
81
82document, err := Analyzed(b, "", options...)
83if err != nil {
84return nil, err
85}
86
87if document != nil {
88document.specFilePath = path
89document.pathLoader = ldr
90}
91
92return document, err
93}
94
95// Analyzed creates a new analyzed spec document for a root json.RawMessage.
96func Analyzed(data json.RawMessage, version string, options ...LoaderOption) (*Document, error) {
97if version == "" {
98version = "2.0"
99}
100if version != "2.0" {
101return nil, fmt.Errorf("spec version %q is not supported", version)
102}
103
104raw, err := trimData(data) // trim blanks, then convert yaml docs into json
105if err != nil {
106return nil, err
107}
108
109swspec := new(spec.Swagger)
110if err = json.Unmarshal(raw, swspec); err != nil {
111return nil, err
112}
113
114origsqspec, err := cloneSpec(swspec)
115if err != nil {
116return nil, err
117}
118
119d := &Document{
120Analyzer: analysis.New(swspec),
121schema: spec.MustLoadSwagger20Schema(),
122spec: swspec,
123raw: raw,
124origSpec: origsqspec,
125pathLoader: loaderFromOptions(options),
126}
127
128return d, nil
129}
130
131func trimData(in json.RawMessage) (json.RawMessage, error) {
132trimmed := bytes.TrimSpace(in)
133if len(trimmed) == 0 {
134return in, nil
135}
136
137if trimmed[0] == '{' || trimmed[0] == '[' {
138return trimmed, nil
139}
140
141// assume yaml doc: convert it to json
142yml, err := swag.BytesToYAMLDoc(trimmed)
143if err != nil {
144return nil, fmt.Errorf("analyzed: %v", err)
145}
146
147d, err := swag.YAMLToJSON(yml)
148if err != nil {
149return nil, fmt.Errorf("analyzed: %v", err)
150}
151
152return d, nil
153}
154
155// Expanded expands the ref fields in the spec document and returns a new spec document
156func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) {
157
158swspec := new(spec.Swagger)
159if err := json.Unmarshal(d.raw, swspec); err != nil {
160return nil, err
161}
162
163var expandOptions *spec.ExpandOptions
164if len(options) > 0 {
165expandOptions = options[0]
166} else {
167expandOptions = &spec.ExpandOptions{
168RelativeBase: d.specFilePath,
169}
170}
171
172if expandOptions.PathLoader == nil {
173if d.pathLoader != nil {
174// use loader from Document options
175expandOptions.PathLoader = d.pathLoader.Load
176} else {
177// use package level loader
178expandOptions.PathLoader = loaders.Load
179}
180}
181
182if err := spec.ExpandSpec(swspec, expandOptions); err != nil {
183return nil, err
184}
185
186dd := &Document{
187Analyzer: analysis.New(swspec),
188spec: swspec,
189specFilePath: d.specFilePath,
190schema: spec.MustLoadSwagger20Schema(),
191raw: d.raw,
192origSpec: d.origSpec,
193}
194return dd, nil
195}
196
197// BasePath the base path for this spec
198func (d *Document) BasePath() string {
199return d.spec.BasePath
200}
201
202// Version returns the version of this spec
203func (d *Document) Version() string {
204return d.spec.Swagger
205}
206
207// Schema returns the swagger 2.0 schema
208func (d *Document) Schema() *spec.Schema {
209return d.schema
210}
211
212// Spec returns the swagger spec object model
213func (d *Document) Spec() *spec.Swagger {
214return d.spec
215}
216
217// Host returns the host for the API
218func (d *Document) Host() string {
219return d.spec.Host
220}
221
222// Raw returns the raw swagger spec as json bytes
223func (d *Document) Raw() json.RawMessage {
224return d.raw
225}
226
227// OrigSpec yields the original spec
228func (d *Document) OrigSpec() *spec.Swagger {
229return d.origSpec
230}
231
232// ResetDefinitions gives a shallow copy with the models reset to the original spec
233func (d *Document) ResetDefinitions() *Document {
234defs := make(map[string]spec.Schema, len(d.origSpec.Definitions))
235for k, v := range d.origSpec.Definitions {
236defs[k] = v
237}
238
239d.spec.Definitions = defs
240return d
241}
242
243// Pristine creates a new pristine document instance based on the input data
244func (d *Document) Pristine() *Document {
245dd, _ := Analyzed(d.Raw(), d.Version())
246dd.pathLoader = d.pathLoader
247return dd
248}
249
250// SpecFilePath returns the file path of the spec if one is defined
251func (d *Document) SpecFilePath() string {
252return d.specFilePath
253}
254
255func cloneSpec(src *spec.Swagger) (*spec.Swagger, error) {
256var b bytes.Buffer
257if err := gob.NewEncoder(&b).Encode(src); err != nil {
258return nil, err
259}
260
261var dst spec.Swagger
262if err := gob.NewDecoder(&b).Decode(&dst); err != nil {
263return nil, err
264}
265return &dst, nil
266}
267