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