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	"encoding/json"
19	"fmt"
20	"strconv"
21
22	"github.com/go-openapi/jsonpointer"
23	"github.com/go-openapi/swag"
24)
25
26// Swagger this is the root document object for the API specification.
27// It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier) together into one document.
28//
29// For more information: http://goo.gl/8us55a#swagger-object-
30type Swagger struct {
31	VendorExtensible
32	SwaggerProps
33}
34
35// JSONLookup look up a value by the json property name
36func (s Swagger) JSONLookup(token string) (interface{}, error) {
37	if ex, ok := s.Extensions[token]; ok {
38		return &ex, nil
39	}
40	r, _, err := jsonpointer.GetForToken(s.SwaggerProps, token)
41	return r, err
42}
43
44// MarshalJSON marshals this swagger structure to json
45func (s Swagger) MarshalJSON() ([]byte, error) {
46	b1, err := json.Marshal(s.SwaggerProps)
47	if err != nil {
48		return nil, err
49	}
50	b2, err := json.Marshal(s.VendorExtensible)
51	if err != nil {
52		return nil, err
53	}
54	return swag.ConcatJSON(b1, b2), nil
55}
56
57// UnmarshalJSON unmarshals a swagger spec from json
58func (s *Swagger) UnmarshalJSON(data []byte) error {
59	var sw Swagger
60	if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil {
61		return err
62	}
63	if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil {
64		return err
65	}
66	*s = sw
67	return nil
68}
69
70type SwaggerProps struct {
71	ID                  string                 `json:"id,omitempty"`
72	Consumes            []string               `json:"consumes,omitempty"`
73	Produces            []string               `json:"produces,omitempty"`
74	Schemes             []string               `json:"schemes,omitempty"` // the scheme, when present must be from [http, https, ws, wss]
75	Swagger             string                 `json:"swagger,omitempty"`
76	Info                *Info                  `json:"info,omitempty"`
77	Host                string                 `json:"host,omitempty"`
78	BasePath            string                 `json:"basePath,omitempty"` // must start with a leading "/"
79	Paths               *Paths                 `json:"paths"`              // required
80	Definitions         Definitions            `json:"definitions"`
81	Parameters          map[string]Parameter   `json:"parameters,omitempty"`
82	Responses           map[string]Response    `json:"responses,omitempty"`
83	SecurityDefinitions SecurityDefinitions    `json:"securityDefinitions,omitempty"`
84	Security            []map[string][]string  `json:"security,omitempty"`
85	Tags                []Tag                  `json:"tags,omitempty"`
86	ExternalDocs        *ExternalDocumentation `json:"externalDocs,omitempty"`
87}
88
89// Dependencies represent a dependencies property
90type Dependencies map[string]SchemaOrStringArray
91
92// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property
93type SchemaOrBool struct {
94	Allows bool
95	Schema *Schema
96}
97
98// JSONLookup implements an interface to customize json pointer lookup
99func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) {
100	if token == "allows" {
101		return s.Allows, nil
102	}
103	r, _, err := jsonpointer.GetForToken(s.Schema, token)
104	return r, err
105}
106
107var jsTrue = []byte("true")
108var jsFalse = []byte("false")
109
110// MarshalJSON convert this object to JSON
111func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
112	if s.Schema != nil {
113		return json.Marshal(s.Schema)
114	}
115
116	if s.Schema == nil && !s.Allows {
117		return jsFalse, nil
118	}
119	return jsTrue, nil
120}
121
122// UnmarshalJSON converts this bool or schema object from a JSON structure
123func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
124	var nw SchemaOrBool
125	if len(data) >= 4 {
126		if data[0] == '{' {
127			var sch Schema
128			if err := json.Unmarshal(data, &sch); err != nil {
129				return err
130			}
131			nw.Schema = &sch
132		}
133		nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e')
134	}
135	*s = nw
136	return nil
137}
138
139// SchemaOrStringArray represents a schema or a string array
140type SchemaOrStringArray struct {
141	Schema   *Schema
142	Property []string
143}
144
145// JSONLookup implements an interface to customize json pointer lookup
146func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) {
147	r, _, err := jsonpointer.GetForToken(s.Schema, token)
148	return r, err
149}
150
151// MarshalJSON converts this schema object or array into JSON structure
152func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
153	if len(s.Property) > 0 {
154		return json.Marshal(s.Property)
155	}
156	if s.Schema != nil {
157		return json.Marshal(s.Schema)
158	}
159	return nil, nil
160}
161
162// UnmarshalJSON converts this schema object or array from a JSON structure
163func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error {
164	var first byte
165	if len(data) > 1 {
166		first = data[0]
167	}
168	var nw SchemaOrStringArray
169	if first == '{' {
170		var sch Schema
171		if err := json.Unmarshal(data, &sch); err != nil {
172			return err
173		}
174		nw.Schema = &sch
175	}
176	if first == '[' {
177		if err := json.Unmarshal(data, &nw.Property); err != nil {
178			return err
179		}
180	}
181	*s = nw
182	return nil
183}
184
185// Definitions contains the models explicitly defined in this spec
186// An object to hold data types that can be consumed and produced by operations.
187// These data types can be primitives, arrays or models.
188//
189// For more information: http://goo.gl/8us55a#definitionsObject
190type Definitions map[string]Schema
191
192// SecurityDefinitions a declaration of the security schemes available to be used in the specification.
193// This does not enforce the security schemes on the operations and only serves to provide
194// the relevant details for each scheme.
195//
196// For more information: http://goo.gl/8us55a#securityDefinitionsObject
197type SecurityDefinitions map[string]*SecurityScheme
198
199// StringOrArray represents a value that can either be a string
200// or an array of strings. Mainly here for serialization purposes
201type StringOrArray []string
202
203// Contains returns true when the value is contained in the slice
204func (s StringOrArray) Contains(value string) bool {
205	for _, str := range s {
206		if str == value {
207			return true
208		}
209	}
210	return false
211}
212
213// JSONLookup implements an interface to customize json pointer lookup
214func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) {
215	if _, err := strconv.Atoi(token); err == nil {
216		r, _, err := jsonpointer.GetForToken(s.Schemas, token)
217		return r, err
218	}
219	r, _, err := jsonpointer.GetForToken(s.Schema, token)
220	return r, err
221}
222
223// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string
224func (s *StringOrArray) UnmarshalJSON(data []byte) error {
225	var first byte
226	if len(data) > 1 {
227		first = data[0]
228	}
229
230	if first == '[' {
231		var parsed []string
232		if err := json.Unmarshal(data, &parsed); err != nil {
233			return err
234		}
235		*s = StringOrArray(parsed)
236		return nil
237	}
238
239	var single interface{}
240	if err := json.Unmarshal(data, &single); err != nil {
241		return err
242	}
243	if single == nil {
244		return nil
245	}
246	switch single.(type) {
247	case string:
248		*s = StringOrArray([]string{single.(string)})
249		return nil
250	default:
251		return fmt.Errorf("only string or array is allowed, not %T", single)
252	}
253}
254
255// MarshalJSON converts this string or array to a JSON array or JSON string
256func (s StringOrArray) MarshalJSON() ([]byte, error) {
257	if len(s) == 1 {
258		return json.Marshal([]string(s)[0])
259	}
260	return json.Marshal([]string(s))
261}
262
263// SchemaOrArray represents a value that can either be a Schema
264// or an array of Schema. Mainly here for serialization purposes
265type SchemaOrArray struct {
266	Schema  *Schema
267	Schemas []Schema
268}
269
270// Len returns the number of schemas in this property
271func (s SchemaOrArray) Len() int {
272	if s.Schema != nil {
273		return 1
274	}
275	return len(s.Schemas)
276}
277
278// ContainsType returns true when one of the schemas is of the specified type
279func (s *SchemaOrArray) ContainsType(name string) bool {
280	if s.Schema != nil {
281		return s.Schema.Type != nil && s.Schema.Type.Contains(name)
282	}
283	return false
284}
285
286// MarshalJSON converts this schema object or array into JSON structure
287func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
288	if len(s.Schemas) > 0 {
289		return json.Marshal(s.Schemas)
290	}
291	return json.Marshal(s.Schema)
292}
293
294// UnmarshalJSON converts this schema object or array from a JSON structure
295func (s *SchemaOrArray) UnmarshalJSON(data []byte) error {
296	var nw SchemaOrArray
297	var first byte
298	if len(data) > 1 {
299		first = data[0]
300	}
301	if first == '{' {
302		var sch Schema
303		if err := json.Unmarshal(data, &sch); err != nil {
304			return err
305		}
306		nw.Schema = &sch
307	}
308	if first == '[' {
309		if err := json.Unmarshal(data, &nw.Schemas); err != nil {
310			return err
311		}
312	}
313	*s = nw
314	return nil
315}
316
317// vim:set ft=go noet sts=2 sw=2 ts=2:
318