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/swag" 24) 25 26// BooleanProperty creates a boolean property 27func BooleanProperty() *Schema { 28 return &Schema{SchemaProps: SchemaProps{Type: []string{"boolean"}}} 29} 30 31// BoolProperty creates a boolean property 32func BoolProperty() *Schema { return BooleanProperty() } 33 34// StringProperty creates a string property 35func StringProperty() *Schema { 36 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} 37} 38 39// CharProperty creates a string property 40func CharProperty() *Schema { 41 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} 42} 43 44// Float64Property creates a float64/double property 45func Float64Property() *Schema { 46 return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "double"}} 47} 48 49// Float32Property creates a float32/float property 50func Float32Property() *Schema { 51 return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "float"}} 52} 53 54// Int8Property creates an int8 property 55func Int8Property() *Schema { 56 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int8"}} 57} 58 59// Int16Property creates an int16 property 60func Int16Property() *Schema { 61 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int16"}} 62} 63 64// Int32Property creates an int32 property 65func Int32Property() *Schema { 66 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int32"}} 67} 68 69// Int64Property creates an int64 property 70func Int64Property() *Schema { 71 return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int64"}} 72} 73 74// StrFmtProperty creates a property for the named string format 75func StrFmtProperty(format string) *Schema { 76 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: format}} 77} 78 79// DateProperty creates a date property 80func DateProperty() *Schema { 81 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date"}} 82} 83 84// DateTimeProperty creates a date time property 85func DateTimeProperty() *Schema { 86 return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date-time"}} 87} 88 89// MapProperty creates a map property 90func MapProperty(property *Schema) *Schema { 91 return &Schema{SchemaProps: SchemaProps{Type: []string{"object"}, 92 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 return r.fromMap(v) 139} 140 141func (r *SchemaURL) fromMap(v map[string]interface{}) error { 142 if v == nil { 143 return nil 144 } 145 if vv, ok := v["$schema"]; ok { 146 if str, ok := vv.(string); ok { 147 u, err := url.Parse(str) 148 if err != nil { 149 return err 150 } 151 152 *r = SchemaURL(u.String()) 153 } 154 } 155 return nil 156} 157 158// SchemaProps describes a JSON schema (draft 4) 159type SchemaProps struct { 160 ID string `json:"id,omitempty"` 161 Ref Ref `json:"-"` 162 Schema SchemaURL `json:"-"` 163 Description string `json:"description,omitempty"` 164 Type StringOrArray `json:"type,omitempty"` 165 Nullable bool `json:"nullable,omitempty"` 166 Format string `json:"format,omitempty"` 167 Title string `json:"title,omitempty"` 168 Default interface{} `json:"default,omitempty"` 169 Maximum *float64 `json:"maximum,omitempty"` 170 ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` 171 Minimum *float64 `json:"minimum,omitempty"` 172 ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` 173 MaxLength *int64 `json:"maxLength,omitempty"` 174 MinLength *int64 `json:"minLength,omitempty"` 175 Pattern string `json:"pattern,omitempty"` 176 MaxItems *int64 `json:"maxItems,omitempty"` 177 MinItems *int64 `json:"minItems,omitempty"` 178 UniqueItems bool `json:"uniqueItems,omitempty"` 179 MultipleOf *float64 `json:"multipleOf,omitempty"` 180 Enum []interface{} `json:"enum,omitempty"` 181 MaxProperties *int64 `json:"maxProperties,omitempty"` 182 MinProperties *int64 `json:"minProperties,omitempty"` 183 Required []string `json:"required,omitempty"` 184 Items *SchemaOrArray `json:"items,omitempty"` 185 AllOf []Schema `json:"allOf,omitempty"` 186 OneOf []Schema `json:"oneOf,omitempty"` 187 AnyOf []Schema `json:"anyOf,omitempty"` 188 Not *Schema `json:"not,omitempty"` 189 Properties map[string]Schema `json:"properties,omitempty"` 190 AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"` 191 PatternProperties map[string]Schema `json:"patternProperties,omitempty"` 192 Dependencies Dependencies `json:"dependencies,omitempty"` 193 AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"` 194 Definitions Definitions `json:"definitions,omitempty"` 195} 196 197// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4) 198type SwaggerSchemaProps struct { 199 Discriminator string `json:"discriminator,omitempty"` 200 ReadOnly bool `json:"readOnly,omitempty"` 201 ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` 202 Example interface{} `json:"example,omitempty"` 203} 204 205// Schema the schema object allows the definition of input and output data types. 206// These types can be objects, but also primitives and arrays. 207// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/) 208// and uses a predefined subset of it. 209// On top of this subset, there are extensions provided by this specification to allow for more complete documentation. 210// 211// For more information: http://goo.gl/8us55a#schemaObject 212type Schema struct { 213 VendorExtensible 214 SchemaProps 215 SwaggerSchemaProps 216 ExtraProps map[string]interface{} `json:"-"` 217} 218 219// WithID sets the id for this schema, allows for chaining 220func (s *Schema) WithID(id string) *Schema { 221 s.ID = id 222 return s 223} 224 225// WithTitle sets the title for this schema, allows for chaining 226func (s *Schema) WithTitle(title string) *Schema { 227 s.Title = title 228 return s 229} 230 231// WithDescription sets the description for this schema, allows for chaining 232func (s *Schema) WithDescription(description string) *Schema { 233 s.Description = description 234 return s 235} 236 237// WithProperties sets the properties for this schema 238func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { 239 s.Properties = schemas 240 return s 241} 242 243// SetProperty sets a property on this schema 244func (s *Schema) SetProperty(name string, schema Schema) *Schema { 245 if s.Properties == nil { 246 s.Properties = make(map[string]Schema) 247 } 248 s.Properties[name] = schema 249 return s 250} 251 252// WithAllOf sets the all of property 253func (s *Schema) WithAllOf(schemas ...Schema) *Schema { 254 s.AllOf = schemas 255 return s 256} 257 258// WithMaxProperties sets the max number of properties an object can have 259func (s *Schema) WithMaxProperties(max int64) *Schema { 260 s.MaxProperties = &max 261 return s 262} 263 264// WithMinProperties sets the min number of properties an object must have 265func (s *Schema) WithMinProperties(min int64) *Schema { 266 s.MinProperties = &min 267 return s 268} 269 270// Typed sets the type of this schema for a single value item 271func (s *Schema) Typed(tpe, format string) *Schema { 272 s.Type = []string{tpe} 273 s.Format = format 274 return s 275} 276 277// AddType adds a type with potential format to the types for this schema 278func (s *Schema) AddType(tpe, format string) *Schema { 279 s.Type = append(s.Type, tpe) 280 if format != "" { 281 s.Format = format 282 } 283 return s 284} 285 286// AsNullable flags this schema as nullable. 287func (s *Schema) AsNullable() *Schema { 288 s.Nullable = true 289 return s 290} 291 292// CollectionOf a fluent builder method for an array parameter 293func (s *Schema) CollectionOf(items Schema) *Schema { 294 s.Type = []string{jsonArray} 295 s.Items = &SchemaOrArray{Schema: &items} 296 return s 297} 298 299// WithDefault sets the default value on this parameter 300func (s *Schema) WithDefault(defaultValue interface{}) *Schema { 301 s.Default = defaultValue 302 return s 303} 304 305// WithRequired flags this parameter as required 306func (s *Schema) WithRequired(items ...string) *Schema { 307 s.Required = items 308 return s 309} 310 311// AddRequired adds field names to the required properties array 312func (s *Schema) AddRequired(items ...string) *Schema { 313 s.Required = append(s.Required, items...) 314 return s 315} 316 317// WithMaxLength sets a max length value 318func (s *Schema) WithMaxLength(max int64) *Schema { 319 s.MaxLength = &max 320 return s 321} 322 323// WithMinLength sets a min length value 324func (s *Schema) WithMinLength(min int64) *Schema { 325 s.MinLength = &min 326 return s 327} 328 329// WithPattern sets a pattern value 330func (s *Schema) WithPattern(pattern string) *Schema { 331 s.Pattern = pattern 332 return s 333} 334 335// WithMultipleOf sets a multiple of value 336func (s *Schema) WithMultipleOf(number float64) *Schema { 337 s.MultipleOf = &number 338 return s 339} 340 341// WithMaximum sets a maximum number value 342func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { 343 s.Maximum = &max 344 s.ExclusiveMaximum = exclusive 345 return s 346} 347 348// WithMinimum sets a minimum number value 349func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { 350 s.Minimum = &min 351 s.ExclusiveMinimum = exclusive 352 return s 353} 354 355// WithEnum sets a the enum values (replace) 356func (s *Schema) WithEnum(values ...interface{}) *Schema { 357 s.Enum = append([]interface{}{}, values...) 358 return s 359} 360 361// WithMaxItems sets the max items 362func (s *Schema) WithMaxItems(size int64) *Schema { 363 s.MaxItems = &size 364 return s 365} 366 367// WithMinItems sets the min items 368func (s *Schema) WithMinItems(size int64) *Schema { 369 s.MinItems = &size 370 return s 371} 372 373// UniqueValues dictates that this array can only have unique items 374func (s *Schema) UniqueValues() *Schema { 375 s.UniqueItems = true 376 return s 377} 378 379// AllowDuplicates this array can have duplicates 380func (s *Schema) AllowDuplicates() *Schema { 381 s.UniqueItems = false 382 return s 383} 384 385// AddToAllOf adds a schema to the allOf property 386func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { 387 s.AllOf = append(s.AllOf, schemas...) 388 return s 389} 390 391// WithDiscriminator sets the name of the discriminator field 392func (s *Schema) WithDiscriminator(discriminator string) *Schema { 393 s.Discriminator = discriminator 394 return s 395} 396 397// AsReadOnly flags this schema as readonly 398func (s *Schema) AsReadOnly() *Schema { 399 s.ReadOnly = true 400 return s 401} 402 403// AsWritable flags this schema as writeable (not read-only) 404func (s *Schema) AsWritable() *Schema { 405 s.ReadOnly = false 406 return s 407} 408 409// WithExample sets the example for this schema 410func (s *Schema) WithExample(example interface{}) *Schema { 411 s.Example = example 412 return s 413} 414 415// WithExternalDocs sets/removes the external docs for/from this schema. 416// When you pass empty strings as params the external documents will be removed. 417// When you pass non-empty string as one value then those values will be used on the external docs object. 418// So when you pass a non-empty description, you should also pass the url and vice versa. 419func (s *Schema) WithExternalDocs(description, url string) *Schema { 420 if description == "" && url == "" { 421 s.ExternalDocs = nil 422 return s 423 } 424 425 if s.ExternalDocs == nil { 426 s.ExternalDocs = &ExternalDocumentation{} 427 } 428 s.ExternalDocs.Description = description 429 s.ExternalDocs.URL = url 430 return s 431} 432 433// MarshalJSON marshal this to JSON 434func (s Schema) MarshalJSON() ([]byte, error) { 435 b1, err := json.Marshal(s.SchemaProps) 436 if err != nil { 437 return nil, fmt.Errorf("schema props %v", err) 438 } 439 b2, err := json.Marshal(s.VendorExtensible) 440 if err != nil { 441 return nil, fmt.Errorf("vendor props %v", err) 442 } 443 b3, err := s.Ref.MarshalJSON() 444 if err != nil { 445 return nil, fmt.Errorf("ref prop %v", err) 446 } 447 b4, err := s.Schema.MarshalJSON() 448 if err != nil { 449 return nil, fmt.Errorf("schema prop %v", err) 450 } 451 b5, err := json.Marshal(s.SwaggerSchemaProps) 452 if err != nil { 453 return nil, fmt.Errorf("common validations %v", err) 454 } 455 var b6 []byte 456 if s.ExtraProps != nil { 457 jj, err := json.Marshal(s.ExtraProps) 458 if err != nil { 459 return nil, fmt.Errorf("extra props %v", err) 460 } 461 b6 = jj 462 } 463 return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil 464} 465 466// UnmarshalJSON marshal this from JSON 467func (s *Schema) UnmarshalJSON(data []byte) error { 468 props := struct { 469 SchemaProps 470 SwaggerSchemaProps 471 }{} 472 if err := json.Unmarshal(data, &props); err != nil { 473 return err 474 } 475 476 sch := Schema{ 477 SchemaProps: props.SchemaProps, 478 SwaggerSchemaProps: props.SwaggerSchemaProps, 479 } 480 481 var d map[string]interface{} 482 if err := json.Unmarshal(data, &d); err != nil { 483 return err 484 } 485 486 _ = sch.Ref.fromMap(d) 487 _ = sch.Schema.fromMap(d) 488 489 delete(d, "$ref") 490 delete(d, "$schema") 491 for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { 492 delete(d, pn) 493 } 494 495 for k, vv := range d { 496 lk := strings.ToLower(k) 497 if strings.HasPrefix(lk, "x-") { 498 if sch.Extensions == nil { 499 sch.Extensions = map[string]interface{}{} 500 } 501 sch.Extensions[k] = vv 502 continue 503 } 504 if sch.ExtraProps == nil { 505 sch.ExtraProps = map[string]interface{}{} 506 } 507 sch.ExtraProps[k] = vv 508 } 509 510 *s = sch 511 512 return nil 513} 514