schema.go 16 KB

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