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 "net/url" 21 "strings" 22 23 "github.com/go-openapi/jsonpointer" 24 "github.com/go-openapi/swag" 25) 26 27// BooleanProperty creates a boolean property 28func BooleanProperty() *Schema { 29 return &Schema{SchemaProps: SchemaProps{Type: []string{"boolean"}}} 30} 31 32// BoolProperty creates a boolean property 33func BoolProperty() *Schema { return BooleanProperty() } 34 35// StringProperty creates a string property 36func StringProperty() *Schema { 37 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} 38} 39 40// CharProperty creates a string property 41func CharProperty() *Schema { 42 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} 43} 44 45// Float64Property creates a float64/double property 46func Float64Property() *Schema { 47 return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "double"}} 48} 49 50// Float32Property creates a float32/float property 51func Float32Property() *Schema { 52 return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "float"}} 53} 54 55// Int8Property creates an int8 property 56func Int8Property() *Schema { 57 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int8"}} 58} 59 60// Int16Property creates an int16 property 61func Int16Property() *Schema { 62 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int16"}} 63} 64 65// Int32Property creates an int32 property 66func Int32Property() *Schema { 67 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int32"}} 68} 69 70// Int64Property creates an int64 property 71func Int64Property() *Schema { 72 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int64"}} 73} 74 75// StrFmtProperty creates a property for the named string format 76func StrFmtProperty(format string) *Schema { 77 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: format}} 78} 79 80// DateProperty creates a date property 81func DateProperty() *Schema { 82 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date"}} 83} 84 85// DateTimeProperty creates a date time property 86func DateTimeProperty() *Schema { 87 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date-time"}} 88} 89 90// MapProperty creates a map property 91func MapProperty(property *Schema) *Schema { 92 return &Schema{SchemaProps: SchemaProps{Type: []string{"object"}, AdditionalProperties: &SchemaOrBool{Allows: true, Schema: property}}} 93} 94 95// RefProperty creates a ref property 96func RefProperty(name string) *Schema { 97 return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} 98} 99 100// RefSchema creates a ref property 101func RefSchema(name string) *Schema { 102 return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} 103} 104 105// ArrayProperty creates an array property 106func ArrayProperty(items *Schema) *Schema { 107 if items == nil { 108 return &Schema{SchemaProps: SchemaProps{Type: []string{"array"}}} 109 } 110 return &Schema{SchemaProps: SchemaProps{Items: &SchemaOrArray{Schema: items}, Type: []string{"array"}}} 111} 112 113// ComposedSchema creates a schema with allOf 114func ComposedSchema(schemas ...Schema) *Schema { 115 s := new(Schema) 116 s.AllOf = schemas 117 return s 118} 119 120// SchemaURL represents a schema url 121type SchemaURL string 122 123// MarshalJSON marshal this to JSON 124func (r SchemaURL) MarshalJSON() ([]byte, error) { 125 if r == "" { 126 return []byte("{}"), nil 127 } 128 v := map[string]interface{}{"$schema": string(r)} 129 return json.Marshal(v) 130} 131 132// UnmarshalJSON unmarshal this from JSON 133func (r *SchemaURL) UnmarshalJSON(data []byte) error { 134 var v map[string]interface{} 135 if err := json.Unmarshal(data, &v); err != nil { 136 return err 137 } 138 if v == nil { 139 return nil 140 } 141 if vv, ok := v["$schema"]; ok { 142 if str, ok := vv.(string); ok { 143 u, err := url.Parse(str) 144 if err != nil { 145 return err 146 } 147 148 *r = SchemaURL(u.String()) 149 } 150 } 151 return nil 152} 153 154// type ExtraSchemaProps map[string]interface{} 155 156// // JSONSchema represents a structure that is a json schema draft 04 157// type JSONSchema struct { 158// SchemaProps 159// ExtraSchemaProps 160// } 161 162// // MarshalJSON marshal this to JSON 163// func (s JSONSchema) MarshalJSON() ([]byte, error) { 164// b1, err := json.Marshal(s.SchemaProps) 165// if err != nil { 166// return nil, err 167// } 168// b2, err := s.Ref.MarshalJSON() 169// if err != nil { 170// return nil, err 171// } 172// b3, err := s.Schema.MarshalJSON() 173// if err != nil { 174// return nil, err 175// } 176// b4, err := json.Marshal(s.ExtraSchemaProps) 177// if err != nil { 178// return nil, err 179// } 180// return swag.ConcatJSON(b1, b2, b3, b4), nil 181// } 182 183// // UnmarshalJSON marshal this from JSON 184// func (s *JSONSchema) UnmarshalJSON(data []byte) error { 185// var sch JSONSchema 186// if err := json.Unmarshal(data, &sch.SchemaProps); err != nil { 187// return err 188// } 189// if err := json.Unmarshal(data, &sch.Ref); err != nil { 190// return err 191// } 192// if err := json.Unmarshal(data, &sch.Schema); err != nil { 193// return err 194// } 195// if err := json.Unmarshal(data, &sch.ExtraSchemaProps); err != nil { 196// return err 197// } 198// *s = sch 199// return nil 200// } 201 202type SchemaProps struct { 203 ID string `json:"id,omitempty"` 204 Ref Ref `json:"-,omitempty"` 205 Schema SchemaURL `json:"-,omitempty"` 206 Description string `json:"description,omitempty"` 207 Type StringOrArray `json:"type,omitempty"` 208 Format string `json:"format,omitempty"` 209 Title string `json:"title,omitempty"` 210 Default interface{} `json:"default,omitempty"` 211 Maximum *float64 `json:"maximum,omitempty"` 212 ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` 213 Minimum *float64 `json:"minimum,omitempty"` 214 ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` 215 MaxLength *int64 `json:"maxLength,omitempty"` 216 MinLength *int64 `json:"minLength,omitempty"` 217 Pattern string `json:"pattern,omitempty"` 218 MaxItems *int64 `json:"maxItems,omitempty"` 219 MinItems *int64 `json:"minItems,omitempty"` 220 UniqueItems bool `json:"uniqueItems,omitempty"` 221 MultipleOf *float64 `json:"multipleOf,omitempty"` 222 Enum []interface{} `json:"enum,omitempty"` 223 MaxProperties *int64 `json:"maxProperties,omitempty"` 224 MinProperties *int64 `json:"minProperties,omitempty"` 225 Required []string `json:"required,omitempty"` 226 Items *SchemaOrArray `json:"items,omitempty"` 227 AllOf []Schema `json:"allOf,omitempty"` 228 OneOf []Schema `json:"oneOf,omitempty"` 229 AnyOf []Schema `json:"anyOf,omitempty"` 230 Not *Schema `json:"not,omitempty"` 231 Properties map[string]Schema `json:"properties,omitempty"` 232 AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"` 233 PatternProperties map[string]Schema `json:"patternProperties,omitempty"` 234 Dependencies Dependencies `json:"dependencies,omitempty"` 235 AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"` 236 Definitions Definitions `json:"definitions,omitempty"` 237} 238 239type SwaggerSchemaProps struct { 240 Discriminator string `json:"discriminator,omitempty"` 241 ReadOnly bool `json:"readOnly,omitempty"` 242 XML *XMLObject `json:"xml,omitempty"` 243 ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` 244 Example interface{} `json:"example,omitempty"` 245} 246 247// Schema the schema object allows the definition of input and output data types. 248// These types can be objects, but also primitives and arrays. 249// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/) 250// and uses a predefined subset of it. 251// On top of this subset, there are extensions provided by this specification to allow for more complete documentation. 252// 253// For more information: http://goo.gl/8us55a#schemaObject 254type Schema struct { 255 VendorExtensible 256 SchemaProps 257 SwaggerSchemaProps 258 ExtraProps map[string]interface{} `json:"-"` 259} 260 261// JSONLookup implements an interface to customize json pointer lookup 262func (s Schema) JSONLookup(token string) (interface{}, error) { 263 if ex, ok := s.Extensions[token]; ok { 264 return &ex, nil 265 } 266 267 if ex, ok := s.ExtraProps[token]; ok { 268 return &ex, nil 269 } 270 271 r, _, err := jsonpointer.GetForToken(s.SchemaProps, token) 272 if r != nil || (err != nil && !strings.HasPrefix(err.Error(), "object has no field")) { 273 return r, err 274 } 275 r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token) 276 return r, err 277} 278 279// WithID sets the id for this schema, allows for chaining 280func (s *Schema) WithID(id string) *Schema { 281 s.ID = id 282 return s 283} 284 285// WithTitle sets the title for this schema, allows for chaining 286func (s *Schema) WithTitle(title string) *Schema { 287 s.Title = title 288 return s 289} 290 291// WithDescription sets the description for this schema, allows for chaining 292func (s *Schema) WithDescription(description string) *Schema { 293 s.Description = description 294 return s 295} 296 297// WithProperties sets the properties for this schema 298func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { 299 s.Properties = schemas 300 return s 301} 302 303// SetProperty sets a property on this schema 304func (s *Schema) SetProperty(name string, schema Schema) *Schema { 305 if s.Properties == nil { 306 s.Properties = make(map[string]Schema) 307 } 308 s.Properties[name] = schema 309 return s 310} 311 312// WithAllOf sets the all of property 313func (s *Schema) WithAllOf(schemas ...Schema) *Schema { 314 s.AllOf = schemas 315 return s 316} 317 318// WithMaxProperties sets the max number of properties an object can have 319func (s *Schema) WithMaxProperties(max int64) *Schema { 320 s.MaxProperties = &max 321 return s 322} 323 324// WithMinProperties sets the min number of properties an object must have 325func (s *Schema) WithMinProperties(min int64) *Schema { 326 s.MinProperties = &min 327 return s 328} 329 330// Typed sets the type of this schema for a single value item 331func (s *Schema) Typed(tpe, format string) *Schema { 332 s.Type = []string{tpe} 333 s.Format = format 334 return s 335} 336 337// AddType adds a type with potential format to the types for this schema 338func (s *Schema) AddType(tpe, format string) *Schema { 339 s.Type = append(s.Type, tpe) 340 if format != "" { 341 s.Format = format 342 } 343 return s 344} 345 346// CollectionOf a fluent builder method for an array parameter 347func (s *Schema) CollectionOf(items Schema) *Schema { 348 s.Type = []string{"array"} 349 s.Items = &SchemaOrArray{Schema: &items} 350 return s 351} 352 353// WithDefault sets the default value on this parameter 354func (s *Schema) WithDefault(defaultValue interface{}) *Schema { 355 s.Default = defaultValue 356 return s 357} 358 359// WithRequired flags this parameter as required 360func (s *Schema) WithRequired(items ...string) *Schema { 361 s.Required = items 362 return s 363} 364 365// AddRequired adds field names to the required properties array 366func (s *Schema) AddRequired(items ...string) *Schema { 367 s.Required = append(s.Required, items...) 368 return s 369} 370 371// WithMaxLength sets a max length value 372func (s *Schema) WithMaxLength(max int64) *Schema { 373 s.MaxLength = &max 374 return s 375} 376 377// WithMinLength sets a min length value 378func (s *Schema) WithMinLength(min int64) *Schema { 379 s.MinLength = &min 380 return s 381} 382 383// WithPattern sets a pattern value 384func (s *Schema) WithPattern(pattern string) *Schema { 385 s.Pattern = pattern 386 return s 387} 388 389// WithMultipleOf sets a multiple of value 390func (s *Schema) WithMultipleOf(number float64) *Schema { 391 s.MultipleOf = &number 392 return s 393} 394 395// WithMaximum sets a maximum number value 396func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { 397 s.Maximum = &max 398 s.ExclusiveMaximum = exclusive 399 return s 400} 401 402// WithMinimum sets a minimum number value 403func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { 404 s.Minimum = &min 405 s.ExclusiveMinimum = exclusive 406 return s 407} 408 409// WithEnum sets a the enum values (replace) 410func (s *Schema) WithEnum(values ...interface{}) *Schema { 411 s.Enum = append([]interface{}{}, values...) 412 return s 413} 414 415// WithMaxItems sets the max items 416func (s *Schema) WithMaxItems(size int64) *Schema { 417 s.MaxItems = &size 418 return s 419} 420 421// WithMinItems sets the min items 422func (s *Schema) WithMinItems(size int64) *Schema { 423 s.MinItems = &size 424 return s 425} 426 427// UniqueValues dictates that this array can only have unique items 428func (s *Schema) UniqueValues() *Schema { 429 s.UniqueItems = true 430 return s 431} 432 433// AllowDuplicates this array can have duplicates 434func (s *Schema) AllowDuplicates() *Schema { 435 s.UniqueItems = false 436 return s 437} 438 439// AddToAllOf adds a schema to the allOf property 440func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { 441 s.AllOf = append(s.AllOf, schemas...) 442 return s 443} 444 445// WithDiscriminator sets the name of the discriminator field 446func (s *Schema) WithDiscriminator(discriminator string) *Schema { 447 s.Discriminator = discriminator 448 return s 449} 450 451// AsReadOnly flags this schema as readonly 452func (s *Schema) AsReadOnly() *Schema { 453 s.ReadOnly = true 454 return s 455} 456 457// AsWritable flags this schema as writeable (not read-only) 458func (s *Schema) AsWritable() *Schema { 459 s.ReadOnly = false 460 return s 461} 462 463// WithExample sets the example for this schema 464func (s *Schema) WithExample(example interface{}) *Schema { 465 s.Example = example 466 return s 467} 468 469// WithExternalDocs sets/removes the external docs for/from this schema. 470// When you pass empty strings as params the external documents will be removed. 471// When you pass non-empty string as one value then those values will be used on the external docs object. 472// So when you pass a non-empty description, you should also pass the url and vice versa. 473func (s *Schema) WithExternalDocs(description, url string) *Schema { 474 if description == "" && url == "" { 475 s.ExternalDocs = nil 476 return s 477 } 478 479 if s.ExternalDocs == nil { 480 s.ExternalDocs = &ExternalDocumentation{} 481 } 482 s.ExternalDocs.Description = description 483 s.ExternalDocs.URL = url 484 return s 485} 486 487// WithXMLName sets the xml name for the object 488func (s *Schema) WithXMLName(name string) *Schema { 489 if s.XML == nil { 490 s.XML = new(XMLObject) 491 } 492 s.XML.Name = name 493 return s 494} 495 496// WithXMLNamespace sets the xml namespace for the object 497func (s *Schema) WithXMLNamespace(namespace string) *Schema { 498 if s.XML == nil { 499 s.XML = new(XMLObject) 500 } 501 s.XML.Namespace = namespace 502 return s 503} 504 505// WithXMLPrefix sets the xml prefix for the object 506func (s *Schema) WithXMLPrefix(prefix string) *Schema { 507 if s.XML == nil { 508 s.XML = new(XMLObject) 509 } 510 s.XML.Prefix = prefix 511 return s 512} 513 514// AsXMLAttribute flags this object as xml attribute 515func (s *Schema) AsXMLAttribute() *Schema { 516 if s.XML == nil { 517 s.XML = new(XMLObject) 518 } 519 s.XML.Attribute = true 520 return s 521} 522 523// AsXMLElement flags this object as an xml node 524func (s *Schema) AsXMLElement() *Schema { 525 if s.XML == nil { 526 s.XML = new(XMLObject) 527 } 528 s.XML.Attribute = false 529 return s 530} 531 532// AsWrappedXML flags this object as wrapped, this is mostly useful for array types 533func (s *Schema) AsWrappedXML() *Schema { 534 if s.XML == nil { 535 s.XML = new(XMLObject) 536 } 537 s.XML.Wrapped = true 538 return s 539} 540 541// AsUnwrappedXML flags this object as an xml node 542func (s *Schema) AsUnwrappedXML() *Schema { 543 if s.XML == nil { 544 s.XML = new(XMLObject) 545 } 546 s.XML.Wrapped = false 547 return s 548} 549 550// MarshalJSON marshal this to JSON 551func (s Schema) MarshalJSON() ([]byte, error) { 552 b1, err := json.Marshal(s.SchemaProps) 553 if err != nil { 554 return nil, fmt.Errorf("schema props %v", err) 555 } 556 b2, err := json.Marshal(s.VendorExtensible) 557 if err != nil { 558 return nil, fmt.Errorf("vendor props %v", err) 559 } 560 b3, err := s.Ref.MarshalJSON() 561 if err != nil { 562 return nil, fmt.Errorf("ref prop %v", err) 563 } 564 b4, err := s.Schema.MarshalJSON() 565 if err != nil { 566 return nil, fmt.Errorf("schema prop %v", err) 567 } 568 b5, err := json.Marshal(s.SwaggerSchemaProps) 569 if err != nil { 570 return nil, fmt.Errorf("common validations %v", err) 571 } 572 var b6 []byte 573 if s.ExtraProps != nil { 574 jj, err := json.Marshal(s.ExtraProps) 575 if err != nil { 576 return nil, fmt.Errorf("extra props %v", err) 577 } 578 b6 = jj 579 } 580 return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil 581} 582 583// UnmarshalJSON marshal this from JSON 584func (s *Schema) UnmarshalJSON(data []byte) error { 585 var sch Schema 586 if err := json.Unmarshal(data, &sch.SchemaProps); err != nil { 587 return err 588 } 589 if err := json.Unmarshal(data, &sch.Ref); err != nil { 590 return err 591 } 592 if err := json.Unmarshal(data, &sch.Schema); err != nil { 593 return err 594 } 595 if err := json.Unmarshal(data, &sch.SwaggerSchemaProps); err != nil { 596 return err 597 } 598 599 var d map[string]interface{} 600 if err := json.Unmarshal(data, &d); err != nil { 601 return err 602 } 603 604 delete(d, "$ref") 605 delete(d, "$schema") 606 for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { 607 delete(d, pn) 608 } 609 610 for k, vv := range d { 611 lk := strings.ToLower(k) 612 if strings.HasPrefix(lk, "x-") { 613 if sch.Extensions == nil { 614 sch.Extensions = map[string]interface{}{} 615 } 616 sch.Extensions[k] = vv 617 continue 618 } 619 if sch.ExtraProps == nil { 620 sch.ExtraProps = map[string]interface{}{} 621 } 622 sch.ExtraProps[k] = vv 623 } 624 625 *s = sch 626 627 return nil 628} 629