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