operation.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. "bytes"
  17. "encoding/gob"
  18. "encoding/json"
  19. "sort"
  20. "github.com/go-openapi/jsonpointer"
  21. "github.com/go-openapi/swag"
  22. )
  23. func init() {
  24. gob.Register(map[string]interface{}{})
  25. gob.Register([]interface{}{})
  26. }
  27. // OperationProps describes an operation
  28. //
  29. // NOTES:
  30. // - schemes, when present must be from [http, https, ws, wss]: see validate
  31. // - Security is handled as a special case: see MarshalJSON function
  32. type OperationProps struct {
  33. Description string `json:"description,omitempty"`
  34. Consumes []string `json:"consumes,omitempty"`
  35. Produces []string `json:"produces,omitempty"`
  36. Schemes []string `json:"schemes,omitempty"`
  37. Tags []string `json:"tags,omitempty"`
  38. Summary string `json:"summary,omitempty"`
  39. ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
  40. ID string `json:"operationId,omitempty"`
  41. Deprecated bool `json:"deprecated,omitempty"`
  42. Security []map[string][]string `json:"security,omitempty"`
  43. Parameters []Parameter `json:"parameters,omitempty"`
  44. Responses *Responses `json:"responses,omitempty"`
  45. }
  46. // MarshalJSON takes care of serializing operation properties to JSON
  47. //
  48. // We use a custom marhaller here to handle a special cases related to
  49. // the Security field. We need to preserve zero length slice
  50. // while omitting the field when the value is nil/unset.
  51. func (op OperationProps) MarshalJSON() ([]byte, error) {
  52. type Alias OperationProps
  53. if op.Security == nil {
  54. return json.Marshal(&struct {
  55. Security []map[string][]string `json:"security,omitempty"`
  56. *Alias
  57. }{
  58. Security: op.Security,
  59. Alias: (*Alias)(&op),
  60. })
  61. }
  62. return json.Marshal(&struct {
  63. Security []map[string][]string `json:"security"`
  64. *Alias
  65. }{
  66. Security: op.Security,
  67. Alias: (*Alias)(&op),
  68. })
  69. }
  70. // Operation describes a single API operation on a path.
  71. //
  72. // For more information: http://goo.gl/8us55a#operationObject
  73. type Operation struct {
  74. VendorExtensible
  75. OperationProps
  76. }
  77. // SuccessResponse gets a success response model
  78. func (o *Operation) SuccessResponse() (*Response, int, bool) {
  79. if o.Responses == nil {
  80. return nil, 0, false
  81. }
  82. responseCodes := make([]int, 0, len(o.Responses.StatusCodeResponses))
  83. for k := range o.Responses.StatusCodeResponses {
  84. if k >= 200 && k < 300 {
  85. responseCodes = append(responseCodes, k)
  86. }
  87. }
  88. if len(responseCodes) > 0 {
  89. sort.Ints(responseCodes)
  90. v := o.Responses.StatusCodeResponses[responseCodes[0]]
  91. return &v, responseCodes[0], true
  92. }
  93. return o.Responses.Default, 0, false
  94. }
  95. // JSONLookup look up a value by the json property name
  96. func (o Operation) JSONLookup(token string) (interface{}, error) {
  97. if ex, ok := o.Extensions[token]; ok {
  98. return &ex, nil
  99. }
  100. r, _, err := jsonpointer.GetForToken(o.OperationProps, token)
  101. return r, err
  102. }
  103. // UnmarshalJSON hydrates this items instance with the data from JSON
  104. func (o *Operation) UnmarshalJSON(data []byte) error {
  105. if err := json.Unmarshal(data, &o.OperationProps); err != nil {
  106. return err
  107. }
  108. return json.Unmarshal(data, &o.VendorExtensible)
  109. }
  110. // MarshalJSON converts this items object to JSON
  111. func (o Operation) MarshalJSON() ([]byte, error) {
  112. b1, err := json.Marshal(o.OperationProps)
  113. if err != nil {
  114. return nil, err
  115. }
  116. b2, err := json.Marshal(o.VendorExtensible)
  117. if err != nil {
  118. return nil, err
  119. }
  120. concated := swag.ConcatJSON(b1, b2)
  121. return concated, nil
  122. }
  123. // NewOperation creates a new operation instance.
  124. // It expects an ID as parameter but not passing an ID is also valid.
  125. func NewOperation(id string) *Operation {
  126. op := new(Operation)
  127. op.ID = id
  128. return op
  129. }
  130. // WithID sets the ID property on this operation, allows for chaining.
  131. func (o *Operation) WithID(id string) *Operation {
  132. o.ID = id
  133. return o
  134. }
  135. // WithDescription sets the description on this operation, allows for chaining
  136. func (o *Operation) WithDescription(description string) *Operation {
  137. o.Description = description
  138. return o
  139. }
  140. // WithSummary sets the summary on this operation, allows for chaining
  141. func (o *Operation) WithSummary(summary string) *Operation {
  142. o.Summary = summary
  143. return o
  144. }
  145. // WithExternalDocs sets/removes the external docs for/from this operation.
  146. // When you pass empty strings as params the external documents will be removed.
  147. // When you pass non-empty string as one value then those values will be used on the external docs object.
  148. // So when you pass a non-empty description, you should also pass the url and vice versa.
  149. func (o *Operation) WithExternalDocs(description, url string) *Operation {
  150. if description == "" && url == "" {
  151. o.ExternalDocs = nil
  152. return o
  153. }
  154. if o.ExternalDocs == nil {
  155. o.ExternalDocs = &ExternalDocumentation{}
  156. }
  157. o.ExternalDocs.Description = description
  158. o.ExternalDocs.URL = url
  159. return o
  160. }
  161. // Deprecate marks the operation as deprecated
  162. func (o *Operation) Deprecate() *Operation {
  163. o.Deprecated = true
  164. return o
  165. }
  166. // Undeprecate marks the operation as not deprected
  167. func (o *Operation) Undeprecate() *Operation {
  168. o.Deprecated = false
  169. return o
  170. }
  171. // WithConsumes adds media types for incoming body values
  172. func (o *Operation) WithConsumes(mediaTypes ...string) *Operation {
  173. o.Consumes = append(o.Consumes, mediaTypes...)
  174. return o
  175. }
  176. // WithProduces adds media types for outgoing body values
  177. func (o *Operation) WithProduces(mediaTypes ...string) *Operation {
  178. o.Produces = append(o.Produces, mediaTypes...)
  179. return o
  180. }
  181. // WithTags adds tags for this operation
  182. func (o *Operation) WithTags(tags ...string) *Operation {
  183. o.Tags = append(o.Tags, tags...)
  184. return o
  185. }
  186. // AddParam adds a parameter to this operation, when a parameter for that location
  187. // and with that name already exists it will be replaced
  188. func (o *Operation) AddParam(param *Parameter) *Operation {
  189. if param == nil {
  190. return o
  191. }
  192. for i, p := range o.Parameters {
  193. if p.Name == param.Name && p.In == param.In {
  194. params := append(o.Parameters[:i], *param)
  195. params = append(params, o.Parameters[i+1:]...)
  196. o.Parameters = params
  197. return o
  198. }
  199. }
  200. o.Parameters = append(o.Parameters, *param)
  201. return o
  202. }
  203. // RemoveParam removes a parameter from the operation
  204. func (o *Operation) RemoveParam(name, in string) *Operation {
  205. for i, p := range o.Parameters {
  206. if p.Name == name && p.In == in {
  207. o.Parameters = append(o.Parameters[:i], o.Parameters[i+1:]...)
  208. return o
  209. }
  210. }
  211. return o
  212. }
  213. // SecuredWith adds a security scope to this operation.
  214. func (o *Operation) SecuredWith(name string, scopes ...string) *Operation {
  215. o.Security = append(o.Security, map[string][]string{name: scopes})
  216. return o
  217. }
  218. // WithDefaultResponse adds a default response to the operation.
  219. // Passing a nil value will remove the response
  220. func (o *Operation) WithDefaultResponse(response *Response) *Operation {
  221. return o.RespondsWith(0, response)
  222. }
  223. // RespondsWith adds a status code response to the operation.
  224. // When the code is 0 the value of the response will be used as default response value.
  225. // When the value of the response is nil it will be removed from the operation
  226. func (o *Operation) RespondsWith(code int, response *Response) *Operation {
  227. if o.Responses == nil {
  228. o.Responses = new(Responses)
  229. }
  230. if code == 0 {
  231. o.Responses.Default = response
  232. return o
  233. }
  234. if response == nil {
  235. delete(o.Responses.StatusCodeResponses, code)
  236. return o
  237. }
  238. if o.Responses.StatusCodeResponses == nil {
  239. o.Responses.StatusCodeResponses = make(map[int]Response)
  240. }
  241. o.Responses.StatusCodeResponses[code] = *response
  242. return o
  243. }
  244. type opsAlias OperationProps
  245. type gobAlias struct {
  246. Security []map[string]struct {
  247. List []string
  248. Pad bool
  249. }
  250. Alias *opsAlias
  251. SecurityIsEmpty bool
  252. }
  253. // GobEncode provides a safe gob encoder for Operation, including empty security requirements
  254. func (o Operation) GobEncode() ([]byte, error) {
  255. raw := struct {
  256. Ext VendorExtensible
  257. Props OperationProps
  258. }{
  259. Ext: o.VendorExtensible,
  260. Props: o.OperationProps,
  261. }
  262. var b bytes.Buffer
  263. err := gob.NewEncoder(&b).Encode(raw)
  264. return b.Bytes(), err
  265. }
  266. // GobDecode provides a safe gob decoder for Operation, including empty security requirements
  267. func (o *Operation) GobDecode(b []byte) error {
  268. var raw struct {
  269. Ext VendorExtensible
  270. Props OperationProps
  271. }
  272. buf := bytes.NewBuffer(b)
  273. err := gob.NewDecoder(buf).Decode(&raw)
  274. if err != nil {
  275. return err
  276. }
  277. o.VendorExtensible = raw.Ext
  278. o.OperationProps = raw.Props
  279. return nil
  280. }
  281. // GobEncode provides a safe gob encoder for Operation, including empty security requirements
  282. func (op OperationProps) GobEncode() ([]byte, error) {
  283. raw := gobAlias{
  284. Alias: (*opsAlias)(&op),
  285. }
  286. var b bytes.Buffer
  287. if op.Security == nil {
  288. // nil security requirement
  289. err := gob.NewEncoder(&b).Encode(raw)
  290. return b.Bytes(), err
  291. }
  292. if len(op.Security) == 0 {
  293. // empty, but non-nil security requirement
  294. raw.SecurityIsEmpty = true
  295. raw.Alias.Security = nil
  296. err := gob.NewEncoder(&b).Encode(raw)
  297. return b.Bytes(), err
  298. }
  299. raw.Security = make([]map[string]struct {
  300. List []string
  301. Pad bool
  302. }, 0, len(op.Security))
  303. for _, req := range op.Security {
  304. v := make(map[string]struct {
  305. List []string
  306. Pad bool
  307. }, len(req))
  308. for k, val := range req {
  309. v[k] = struct {
  310. List []string
  311. Pad bool
  312. }{
  313. List: val,
  314. }
  315. }
  316. raw.Security = append(raw.Security, v)
  317. }
  318. err := gob.NewEncoder(&b).Encode(raw)
  319. return b.Bytes(), err
  320. }
  321. // GobDecode provides a safe gob decoder for Operation, including empty security requirements
  322. func (op *OperationProps) GobDecode(b []byte) error {
  323. var raw gobAlias
  324. buf := bytes.NewBuffer(b)
  325. err := gob.NewDecoder(buf).Decode(&raw)
  326. if err != nil {
  327. return err
  328. }
  329. if raw.Alias == nil {
  330. return nil
  331. }
  332. switch {
  333. case raw.SecurityIsEmpty:
  334. // empty, but non-nil security requirement
  335. raw.Alias.Security = []map[string][]string{}
  336. case len(raw.Alias.Security) == 0:
  337. // nil security requirement
  338. raw.Alias.Security = nil
  339. default:
  340. raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security))
  341. for _, req := range raw.Security {
  342. v := make(map[string][]string, len(req))
  343. for k, val := range req {
  344. v[k] = make([]string, 0, len(val.List))
  345. v[k] = append(v[k], val.List...)
  346. }
  347. raw.Alias.Security = append(raw.Alias.Security, v)
  348. }
  349. }
  350. *op = *(*OperationProps)(raw.Alias)
  351. return nil
  352. }