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 ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` 203 Example interface{} `json:"example,omitempty"` 204} 205 206// Schema the schema object allows the definition of input and output data types. 207// These types can be objects, but also primitives and arrays. 208// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/) 209// and uses a predefined subset of it. 210// On top of this subset, there are extensions provided by this specification to allow for more complete documentation. 211// 212// For more information: http://goo.gl/8us55a#schemaObject 213type Schema struct { 214 VendorExtensible 215 SchemaProps 216 SwaggerSchemaProps 217 ExtraProps map[string]interface{} `json:"-"` 218} 219 220// JSONLookup implements an interface to customize json pointer lookup 221func (s Schema) JSONLookup(token string) (interface{}, error) { 222 if ex, ok := s.Extensions[token]; ok { 223 return &ex, nil 224 } 225 226 if ex, ok := s.ExtraProps[token]; ok { 227 return &ex, nil 228 } 229 230 r, _, err := jsonpointer.GetForToken(s.SchemaProps, token) 231 if r != nil || (err != nil && !strings.HasPrefix(err.Error(), "object has no field")) { 232 return r, err 233 } 234 r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token) 235 return r, err 236} 237 238// WithID sets the id for this schema, allows for chaining 239func (s *Schema) WithID(id string) *Schema { 240 s.ID = id 241 return s 242} 243 244// WithTitle sets the title for this schema, allows for chaining 245func (s *Schema) WithTitle(title string) *Schema { 246 s.Title = title 247 return s 248} 249 250// WithDescription sets the description for this schema, allows for chaining 251func (s *Schema) WithDescription(description string) *Schema { 252 s.Description = description 253 return s 254} 255 256// WithProperties sets the properties for this schema 257func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { 258 s.Properties = schemas 259 return s 260} 261 262// SetProperty sets a property on this schema 263func (s *Schema) SetProperty(name string, schema Schema) *Schema { 264 if s.Properties == nil { 265 s.Properties = make(map[string]Schema) 266 } 267 s.Properties[name] = schema 268 return s 269} 270 271// WithAllOf sets the all of property 272func (s *Schema) WithAllOf(schemas ...Schema) *Schema { 273 s.AllOf = schemas 274 return s 275} 276 277// WithMaxProperties sets the max number of properties an object can have 278func (s *Schema) WithMaxProperties(max int64) *Schema { 279 s.MaxProperties = &max 280 return s 281} 282 283// WithMinProperties sets the min number of properties an object must have 284func (s *Schema) WithMinProperties(min int64) *Schema { 285 s.MinProperties = &min 286 return s 287} 288 289// Typed sets the type of this schema for a single value item 290func (s *Schema) Typed(tpe, format string) *Schema { 291 s.Type = []string{tpe} 292 s.Format = format 293 return s 294} 295 296// AddType adds a type with potential format to the types for this schema 297func (s *Schema) AddType(tpe, format string) *Schema { 298 s.Type = append(s.Type, tpe) 299 if format != "" { 300 s.Format = format 301 } 302 return s 303} 304 305// AsNullable flags this schema as nullable. 306func (s *Schema) AsNullable() *Schema { 307 s.Nullable = true 308 return s 309} 310 311// CollectionOf a fluent builder method for an array parameter 312func (s *Schema) CollectionOf(items Schema) *Schema { 313 s.Type = []string{jsonArray} 314 s.Items = &SchemaOrArray{Schema: &items} 315 return s 316} 317 318// WithDefault sets the default value on this parameter 319func (s *Schema) WithDefault(defaultValue interface{}) *Schema { 320 s.Default = defaultValue 321 return s 322} 323 324// WithRequired flags this parameter as required 325func (s *Schema) WithRequired(items ...string) *Schema { 326 s.Required = items 327 return s 328} 329 330// AddRequired adds field names to the required properties array 331func (s *Schema) AddRequired(items ...string) *Schema { 332 s.Required = append(s.Required, items...) 333 return s 334} 335 336// WithMaxLength sets a max length value 337func (s *Schema) WithMaxLength(max int64) *Schema { 338 s.MaxLength = &max 339 return s 340} 341 342// WithMinLength sets a min length value 343func (s *Schema) WithMinLength(min int64) *Schema { 344 s.MinLength = &min 345 return s 346} 347 348// WithPattern sets a pattern value 349func (s *Schema) WithPattern(pattern string) *Schema { 350 s.Pattern = pattern 351 return s 352} 353 354// WithMultipleOf sets a multiple of value 355func (s *Schema) WithMultipleOf(number float64) *Schema { 356 s.MultipleOf = &number 357 return s 358} 359 360// WithMaximum sets a maximum number value 361func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { 362 s.Maximum = &max 363 s.ExclusiveMaximum = exclusive 364 return s 365} 366 367// WithMinimum sets a minimum number value 368func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { 369 s.Minimum = &min 370 s.ExclusiveMinimum = exclusive 371 return s 372} 373 374// WithEnum sets a the enum values (replace) 375func (s *Schema) WithEnum(values ...interface{}) *Schema { 376 s.Enum = append([]interface{}{}, values...) 377 return s 378} 379 380// WithMaxItems sets the max items 381func (s *Schema) WithMaxItems(size int64) *Schema { 382 s.MaxItems = &size 383 return s 384} 385 386// WithMinItems sets the min items 387func (s *Schema) WithMinItems(size int64) *Schema { 388 s.MinItems = &size 389 return s 390} 391 392// UniqueValues dictates that this array can only have unique items 393func (s *Schema) UniqueValues() *Schema { 394 s.UniqueItems = true 395 return s 396} 397 398// AllowDuplicates this array can have duplicates 399func (s *Schema) AllowDuplicates() *Schema { 400 s.UniqueItems = false 401 return s 402} 403 404// AddToAllOf adds a schema to the allOf property 405func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { 406 s.AllOf = append(s.AllOf, schemas...) 407 return s 408} 409 410// WithDiscriminator sets the name of the discriminator field 411func (s *Schema) WithDiscriminator(discriminator string) *Schema { 412 s.Discriminator = discriminator 413 return s 414} 415 416// AsReadOnly flags this schema as readonly 417func (s *Schema) AsReadOnly() *Schema { 418 s.ReadOnly = true 419 return s 420} 421 422// AsWritable flags this schema as writeable (not read-only) 423func (s *Schema) AsWritable() *Schema { 424 s.ReadOnly = false 425 return s 426} 427 428// WithExample sets the example for this schema 429func (s *Schema) WithExample(example interface{}) *Schema { 430 s.Example = example 431 return s 432} 433 434// WithExternalDocs sets/removes the external docs for/from this schema. 435// When you pass empty strings as params the external documents will be removed. 436// When you pass non-empty string as one value then those values will be used on the external docs object. 437// So when you pass a non-empty description, you should also pass the url and vice versa. 438func (s *Schema) WithExternalDocs(description, url string) *Schema { 439 if description == "" && url == "" { 440 s.ExternalDocs = nil 441 return s 442 } 443 444 if s.ExternalDocs == nil { 445 s.ExternalDocs = &ExternalDocumentation{} 446 } 447 s.ExternalDocs.Description = description 448 s.ExternalDocs.URL = url 449 return s 450} 451 452// MarshalJSON marshal this to JSON 453func (s Schema) MarshalJSON() ([]byte, error) { 454 b1, err := json.Marshal(s.SchemaProps) 455 if err != nil { 456 return nil, fmt.Errorf("schema props %v", err) 457 } 458 b2, err := json.Marshal(s.VendorExtensible) 459 if err != nil { 460 return nil, fmt.Errorf("vendor props %v", err) 461 } 462 b3, err := s.Ref.MarshalJSON() 463 if err != nil { 464 return nil, fmt.Errorf("ref prop %v", err) 465 } 466 b4, err := s.Schema.MarshalJSON() 467 if err != nil { 468 return nil, fmt.Errorf("schema prop %v", err) 469 } 470 b5, err := json.Marshal(s.SwaggerSchemaProps) 471 if err != nil { 472 return nil, fmt.Errorf("common validations %v", err) 473 } 474 var b6 []byte 475 if s.ExtraProps != nil { 476 jj, err := json.Marshal(s.ExtraProps) 477 if err != nil { 478 return nil, fmt.Errorf("extra props %v", err) 479 } 480 b6 = jj 481 } 482 return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil 483} 484 485// UnmarshalJSON marshal this from JSON 486func (s *Schema) UnmarshalJSON(data []byte) error { 487 props := struct { 488 SchemaProps 489 SwaggerSchemaProps 490 }{} 491 if err := json.Unmarshal(data, &props); err != nil { 492 return err 493 } 494 495 sch := Schema{ 496 SchemaProps: props.SchemaProps, 497 SwaggerSchemaProps: props.SwaggerSchemaProps, 498 } 499 500 var d map[string]interface{} 501 if err := json.Unmarshal(data, &d); err != nil { 502 return err 503 } 504 505 _ = sch.Ref.fromMap(d) 506 _ = sch.Schema.fromMap(d) 507 508 delete(d, "$ref") 509 delete(d, "$schema") 510 for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { 511 delete(d, pn) 512 } 513 514 for k, vv := range d { 515 lk := strings.ToLower(k) 516 if strings.HasPrefix(lk, "x-") { 517 if sch.Extensions == nil { 518 sch.Extensions = map[string]interface{}{} 519 } 520 sch.Extensions[k] = vv 521 continue 522 } 523 if sch.ExtraProps == nil { 524 sch.ExtraProps = map[string]interface{}{} 525 } 526 sch.ExtraProps[k] = vv 527 } 528 529 *s = sch 530 531 return nil 532} 533