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