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 spec
16
17import (
18	"bytes"
19	"encoding/gob"
20	"encoding/json"
21	"fmt"
22	"strconv"
23
24	"github.com/go-openapi/jsonpointer"
25	"github.com/go-openapi/swag"
26)
27
28// Swagger this is the root document object for the API specification.
29// It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier)
30// together into one document.
31//
32// For more information: http://goo.gl/8us55a#swagger-object-
33type Swagger struct {
34	VendorExtensible
35	SwaggerProps
36}
37
38// JSONLookup look up a value by the json property name
39func (s Swagger) JSONLookup(token string) (interface{}, error) {
40	if ex, ok := s.Extensions[token]; ok {
41		return &ex, nil
42	}
43	r, _, err := jsonpointer.GetForToken(s.SwaggerProps, token)
44	return r, err
45}
46
47// MarshalJSON marshals this swagger structure to json
48func (s Swagger) MarshalJSON() ([]byte, error) {
49	b1, err := json.Marshal(s.SwaggerProps)
50	if err != nil {
51		return nil, err
52	}
53	b2, err := json.Marshal(s.VendorExtensible)
54	if err != nil {
55		return nil, err
56	}
57	return swag.ConcatJSON(b1, b2), nil
58}
59
60// UnmarshalJSON unmarshals a swagger spec from json
61func (s *Swagger) UnmarshalJSON(data []byte) error {
62	var sw Swagger
63	if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil {
64		return err
65	}
66	if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil {
67		return err
68	}
69	*s = sw
70	return nil
71}
72
73// GobEncode provides a safe gob encoder for Swagger, including extensions
74func (s Swagger) GobEncode() ([]byte, error) {
75	var b bytes.Buffer
76	raw := struct {
77		Props SwaggerProps
78		Ext   VendorExtensible
79	}{
80		Props: s.SwaggerProps,
81		Ext:   s.VendorExtensible,
82	}
83	err := gob.NewEncoder(&b).Encode(raw)
84	return b.Bytes(), err
85}
86
87// GobDecode provides a safe gob decoder for Swagger, including extensions
88func (s *Swagger) GobDecode(b []byte) error {
89	var raw struct {
90		Props SwaggerProps
91		Ext   VendorExtensible
92	}
93	buf := bytes.NewBuffer(b)
94	err := gob.NewDecoder(buf).Decode(&raw)
95	if err != nil {
96		return err
97	}
98	s.SwaggerProps = raw.Props
99	s.VendorExtensible = raw.Ext
100	return nil
101}
102
103// SwaggerProps captures the top-level properties of an Api specification
104//
105// NOTE: validation rules
106// - the scheme, when present must be from [http, https, ws, wss]
107// - BasePath must start with a leading "/"
108// - Paths is required
109type SwaggerProps struct {
110	ID                  string                 `json:"id,omitempty"`
111	Consumes            []string               `json:"consumes,omitempty"`
112	Produces            []string               `json:"produces,omitempty"`
113	Schemes             []string               `json:"schemes,omitempty"`
114	Swagger             string                 `json:"swagger,omitempty"`
115	Info                *Info                  `json:"info,omitempty"`
116	Host                string                 `json:"host,omitempty"`
117	BasePath            string                 `json:"basePath,omitempty"`
118	Paths               *Paths                 `json:"paths"`
119	Definitions         Definitions            `json:"definitions,omitempty"`
120	Parameters          map[string]Parameter   `json:"parameters,omitempty"`
121	Responses           map[string]Response    `json:"responses,omitempty"`
122	SecurityDefinitions SecurityDefinitions    `json:"securityDefinitions,omitempty"`
123	Security            []map[string][]string  `json:"security,omitempty"`
124	Tags                []Tag                  `json:"tags,omitempty"`
125	ExternalDocs        *ExternalDocumentation `json:"externalDocs,omitempty"`
126}
127
128type swaggerPropsAlias SwaggerProps
129
130type gobSwaggerPropsAlias struct {
131	Security []map[string]struct {
132		List []string
133		Pad  bool
134	}
135	Alias           *swaggerPropsAlias
136	SecurityIsEmpty bool
137}
138
139// GobEncode provides a safe gob encoder for SwaggerProps, including empty security requirements
140func (o SwaggerProps) GobEncode() ([]byte, error) {
141	raw := gobSwaggerPropsAlias{
142		Alias: (*swaggerPropsAlias)(&o),
143	}
144
145	var b bytes.Buffer
146	if o.Security == nil {
147		// nil security requirement
148		err := gob.NewEncoder(&b).Encode(raw)
149		return b.Bytes(), err
150	}
151
152	if len(o.Security) == 0 {
153		// empty, but non-nil security requirement
154		raw.SecurityIsEmpty = true
155		raw.Alias.Security = nil
156		err := gob.NewEncoder(&b).Encode(raw)
157		return b.Bytes(), err
158	}
159
160	raw.Security = make([]map[string]struct {
161		List []string
162		Pad  bool
163	}, 0, len(o.Security))
164	for _, req := range o.Security {
165		v := make(map[string]struct {
166			List []string
167			Pad  bool
168		}, len(req))
169		for k, val := range req {
170			v[k] = struct {
171				List []string
172				Pad  bool
173			}{
174				List: val,
175			}
176		}
177		raw.Security = append(raw.Security, v)
178	}
179
180	err := gob.NewEncoder(&b).Encode(raw)
181	return b.Bytes(), err
182}
183
184// GobDecode provides a safe gob decoder for SwaggerProps, including empty security requirements
185func (o *SwaggerProps) GobDecode(b []byte) error {
186	var raw gobSwaggerPropsAlias
187
188	buf := bytes.NewBuffer(b)
189	err := gob.NewDecoder(buf).Decode(&raw)
190	if err != nil {
191		return err
192	}
193	if raw.Alias == nil {
194		return nil
195	}
196
197	switch {
198	case raw.SecurityIsEmpty:
199		// empty, but non-nil security requirement
200		raw.Alias.Security = []map[string][]string{}
201	case len(raw.Alias.Security) == 0:
202		// nil security requirement
203		raw.Alias.Security = nil
204	default:
205		raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security))
206		for _, req := range raw.Security {
207			v := make(map[string][]string, len(req))
208			for k, val := range req {
209				v[k] = make([]string, 0, len(val.List))
210				v[k] = append(v[k], val.List...)
211			}
212			raw.Alias.Security = append(raw.Alias.Security, v)
213		}
214	}
215
216	*o = *(*SwaggerProps)(raw.Alias)
217	return nil
218}
219
220// Dependencies represent a dependencies property
221type Dependencies map[string]SchemaOrStringArray
222
223// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property
224type SchemaOrBool struct {
225	Allows bool
226	Schema *Schema
227}
228
229// JSONLookup implements an interface to customize json pointer lookup
230func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) {
231	if token == "allows" {
232		return s.Allows, nil
233	}
234	r, _, err := jsonpointer.GetForToken(s.Schema, token)
235	return r, err
236}
237
238var jsTrue = []byte("true")
239var jsFalse = []byte("false")
240
241// MarshalJSON convert this object to JSON
242func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
243	if s.Schema != nil {
244		return json.Marshal(s.Schema)
245	}
246
247	if s.Schema == nil && !s.Allows {
248		return jsFalse, nil
249	}
250	return jsTrue, nil
251}
252
253// UnmarshalJSON converts this bool or schema object from a JSON structure
254func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
255	var nw SchemaOrBool
256	if len(data) >= 4 {
257		if data[0] == '{' {
258			var sch Schema
259			if err := json.Unmarshal(data, &sch); err != nil {
260				return err
261			}
262			nw.Schema = &sch
263		}
264		nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e')
265	}
266	*s = nw
267	return nil
268}
269
270// SchemaOrStringArray represents a schema or a string array
271type SchemaOrStringArray struct {
272	Schema   *Schema
273	Property []string
274}
275
276// JSONLookup implements an interface to customize json pointer lookup
277func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) {
278	r, _, err := jsonpointer.GetForToken(s.Schema, token)
279	return r, err
280}
281
282// MarshalJSON converts this schema object or array into JSON structure
283func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
284	if len(s.Property) > 0 {
285		return json.Marshal(s.Property)
286	}
287	if s.Schema != nil {
288		return json.Marshal(s.Schema)
289	}
290	return []byte("null"), nil
291}
292
293// UnmarshalJSON converts this schema object or array from a JSON structure
294func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error {
295	var first byte
296	if len(data) > 1 {
297		first = data[0]
298	}
299	var nw SchemaOrStringArray
300	if first == '{' {
301		var sch Schema
302		if err := json.Unmarshal(data, &sch); err != nil {
303			return err
304		}
305		nw.Schema = &sch
306	}
307	if first == '[' {
308		if err := json.Unmarshal(data, &nw.Property); err != nil {
309			return err
310		}
311	}
312	*s = nw
313	return nil
314}
315
316// Definitions contains the models explicitly defined in this spec
317// An object to hold data types that can be consumed and produced by operations.
318// These data types can be primitives, arrays or models.
319//
320// For more information: http://goo.gl/8us55a#definitionsObject
321type Definitions map[string]Schema
322
323// SecurityDefinitions a declaration of the security schemes available to be used in the specification.
324// This does not enforce the security schemes on the operations and only serves to provide
325// the relevant details for each scheme.
326//
327// For more information: http://goo.gl/8us55a#securityDefinitionsObject
328type SecurityDefinitions map[string]*SecurityScheme
329
330// StringOrArray represents a value that can either be a string
331// or an array of strings. Mainly here for serialization purposes
332type StringOrArray []string
333
334// Contains returns true when the value is contained in the slice
335func (s StringOrArray) Contains(value string) bool {
336	for _, str := range s {
337		if str == value {
338			return true
339		}
340	}
341	return false
342}
343
344// JSONLookup implements an interface to customize json pointer lookup
345func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) {
346	if _, err := strconv.Atoi(token); err == nil {
347		r, _, err := jsonpointer.GetForToken(s.Schemas, token)
348		return r, err
349	}
350	r, _, err := jsonpointer.GetForToken(s.Schema, token)
351	return r, err
352}
353
354// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string
355func (s *StringOrArray) UnmarshalJSON(data []byte) error {
356	var first byte
357	if len(data) > 1 {
358		first = data[0]
359	}
360
361	if first == '[' {
362		var parsed []string
363		if err := json.Unmarshal(data, &parsed); err != nil {
364			return err
365		}
366		*s = StringOrArray(parsed)
367		return nil
368	}
369
370	var single interface{}
371	if err := json.Unmarshal(data, &single); err != nil {
372		return err
373	}
374	if single == nil {
375		return nil
376	}
377	switch v := single.(type) {
378	case string:
379		*s = StringOrArray([]string{v})
380		return nil
381	default:
382		return fmt.Errorf("only string or array is allowed, not %T", single)
383	}
384}
385
386// MarshalJSON converts this string or array to a JSON array or JSON string
387func (s StringOrArray) MarshalJSON() ([]byte, error) {
388	if len(s) == 1 {
389		return json.Marshal([]string(s)[0])
390	}
391	return json.Marshal([]string(s))
392}
393
394// SchemaOrArray represents a value that can either be a Schema
395// or an array of Schema. Mainly here for serialization purposes
396type SchemaOrArray struct {
397	Schema  *Schema
398	Schemas []Schema
399}
400
401// Len returns the number of schemas in this property
402func (s SchemaOrArray) Len() int {
403	if s.Schema != nil {
404		return 1
405	}
406	return len(s.Schemas)
407}
408
409// ContainsType returns true when one of the schemas is of the specified type
410func (s *SchemaOrArray) ContainsType(name string) bool {
411	if s.Schema != nil {
412		return s.Schema.Type != nil && s.Schema.Type.Contains(name)
413	}
414	return false
415}
416
417// MarshalJSON converts this schema object or array into JSON structure
418func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
419	if len(s.Schemas) > 0 {
420		return json.Marshal(s.Schemas)
421	}
422	return json.Marshal(s.Schema)
423}
424
425// UnmarshalJSON converts this schema object or array from a JSON structure
426func (s *SchemaOrArray) UnmarshalJSON(data []byte) error {
427	var nw SchemaOrArray
428	var first byte
429	if len(data) > 1 {
430		first = data[0]
431	}
432	if first == '{' {
433		var sch Schema
434		if err := json.Unmarshal(data, &sch); err != nil {
435			return err
436		}
437		nw.Schema = &sch
438	}
439	if first == '[' {
440		if err := json.Unmarshal(data, &nw.Schemas); err != nil {
441			return err
442		}
443	}
444	*s = nw
445	return nil
446}
447
448// vim:set ft=go noet sts=2 sw=2 ts=2:
449