You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

analyzer.go 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  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 analysis
  15. import (
  16. "fmt"
  17. slashpath "path"
  18. "strconv"
  19. "strings"
  20. "github.com/go-openapi/jsonpointer"
  21. "github.com/go-openapi/spec"
  22. "github.com/go-openapi/swag"
  23. )
  24. type referenceAnalysis struct {
  25. schemas map[string]spec.Ref
  26. responses map[string]spec.Ref
  27. parameters map[string]spec.Ref
  28. items map[string]spec.Ref
  29. headerItems map[string]spec.Ref
  30. parameterItems map[string]spec.Ref
  31. allRefs map[string]spec.Ref
  32. pathItems map[string]spec.Ref
  33. }
  34. func (r *referenceAnalysis) addRef(key string, ref spec.Ref) {
  35. r.allRefs["#"+key] = ref
  36. }
  37. func (r *referenceAnalysis) addItemsRef(key string, items *spec.Items, location string) {
  38. r.items["#"+key] = items.Ref
  39. r.addRef(key, items.Ref)
  40. if location == "header" {
  41. // NOTE: in swagger 2.0, headers and parameters (but not body param schemas) are simple schemas
  42. // and $ref are not supported here. However it is possible to analyze this.
  43. r.headerItems["#"+key] = items.Ref
  44. } else {
  45. r.parameterItems["#"+key] = items.Ref
  46. }
  47. }
  48. func (r *referenceAnalysis) addSchemaRef(key string, ref SchemaRef) {
  49. r.schemas["#"+key] = ref.Schema.Ref
  50. r.addRef(key, ref.Schema.Ref)
  51. }
  52. func (r *referenceAnalysis) addResponseRef(key string, resp *spec.Response) {
  53. r.responses["#"+key] = resp.Ref
  54. r.addRef(key, resp.Ref)
  55. }
  56. func (r *referenceAnalysis) addParamRef(key string, param *spec.Parameter) {
  57. r.parameters["#"+key] = param.Ref
  58. r.addRef(key, param.Ref)
  59. }
  60. func (r *referenceAnalysis) addPathItemRef(key string, pathItem *spec.PathItem) {
  61. r.pathItems["#"+key] = pathItem.Ref
  62. r.addRef(key, pathItem.Ref)
  63. }
  64. type patternAnalysis struct {
  65. parameters map[string]string
  66. headers map[string]string
  67. items map[string]string
  68. schemas map[string]string
  69. allPatterns map[string]string
  70. }
  71. func (p *patternAnalysis) addPattern(key, pattern string) {
  72. p.allPatterns["#"+key] = pattern
  73. }
  74. func (p *patternAnalysis) addParameterPattern(key, pattern string) {
  75. p.parameters["#"+key] = pattern
  76. p.addPattern(key, pattern)
  77. }
  78. func (p *patternAnalysis) addHeaderPattern(key, pattern string) {
  79. p.headers["#"+key] = pattern
  80. p.addPattern(key, pattern)
  81. }
  82. func (p *patternAnalysis) addItemsPattern(key, pattern string) {
  83. p.items["#"+key] = pattern
  84. p.addPattern(key, pattern)
  85. }
  86. func (p *patternAnalysis) addSchemaPattern(key, pattern string) {
  87. p.schemas["#"+key] = pattern
  88. p.addPattern(key, pattern)
  89. }
  90. type enumAnalysis struct {
  91. parameters map[string][]interface{}
  92. headers map[string][]interface{}
  93. items map[string][]interface{}
  94. schemas map[string][]interface{}
  95. allEnums map[string][]interface{}
  96. }
  97. func (p *enumAnalysis) addEnum(key string, enum []interface{}) {
  98. p.allEnums["#"+key] = enum
  99. }
  100. func (p *enumAnalysis) addParameterEnum(key string, enum []interface{}) {
  101. p.parameters["#"+key] = enum
  102. p.addEnum(key, enum)
  103. }
  104. func (p *enumAnalysis) addHeaderEnum(key string, enum []interface{}) {
  105. p.headers["#"+key] = enum
  106. p.addEnum(key, enum)
  107. }
  108. func (p *enumAnalysis) addItemsEnum(key string, enum []interface{}) {
  109. p.items["#"+key] = enum
  110. p.addEnum(key, enum)
  111. }
  112. func (p *enumAnalysis) addSchemaEnum(key string, enum []interface{}) {
  113. p.schemas["#"+key] = enum
  114. p.addEnum(key, enum)
  115. }
  116. // New takes a swagger spec object and returns an analyzed spec document.
  117. // The analyzed document contains a number of indices that make it easier to
  118. // reason about semantics of a swagger specification for use in code generation
  119. // or validation etc.
  120. func New(doc *spec.Swagger) *Spec {
  121. a := &Spec{
  122. spec: doc,
  123. references: referenceAnalysis{},
  124. patterns: patternAnalysis{},
  125. enums: enumAnalysis{},
  126. }
  127. a.reset()
  128. a.initialize()
  129. return a
  130. }
  131. // Spec is an analyzed specification object. It takes a swagger spec object and turns it into a registry
  132. // with a bunch of utility methods to act on the information in the spec.
  133. type Spec struct {
  134. spec *spec.Swagger
  135. consumes map[string]struct{}
  136. produces map[string]struct{}
  137. authSchemes map[string]struct{}
  138. operations map[string]map[string]*spec.Operation
  139. references referenceAnalysis
  140. patterns patternAnalysis
  141. enums enumAnalysis
  142. allSchemas map[string]SchemaRef
  143. allOfs map[string]SchemaRef
  144. }
  145. func (s *Spec) reset() {
  146. s.consumes = make(map[string]struct{}, 150)
  147. s.produces = make(map[string]struct{}, 150)
  148. s.authSchemes = make(map[string]struct{}, 150)
  149. s.operations = make(map[string]map[string]*spec.Operation, 150)
  150. s.allSchemas = make(map[string]SchemaRef, 150)
  151. s.allOfs = make(map[string]SchemaRef, 150)
  152. s.references.schemas = make(map[string]spec.Ref, 150)
  153. s.references.pathItems = make(map[string]spec.Ref, 150)
  154. s.references.responses = make(map[string]spec.Ref, 150)
  155. s.references.parameters = make(map[string]spec.Ref, 150)
  156. s.references.items = make(map[string]spec.Ref, 150)
  157. s.references.headerItems = make(map[string]spec.Ref, 150)
  158. s.references.parameterItems = make(map[string]spec.Ref, 150)
  159. s.references.allRefs = make(map[string]spec.Ref, 150)
  160. s.patterns.parameters = make(map[string]string, 150)
  161. s.patterns.headers = make(map[string]string, 150)
  162. s.patterns.items = make(map[string]string, 150)
  163. s.patterns.schemas = make(map[string]string, 150)
  164. s.patterns.allPatterns = make(map[string]string, 150)
  165. s.enums.parameters = make(map[string][]interface{}, 150)
  166. s.enums.headers = make(map[string][]interface{}, 150)
  167. s.enums.items = make(map[string][]interface{}, 150)
  168. s.enums.schemas = make(map[string][]interface{}, 150)
  169. s.enums.allEnums = make(map[string][]interface{}, 150)
  170. }
  171. func (s *Spec) reload() {
  172. s.reset()
  173. s.initialize()
  174. }
  175. func (s *Spec) initialize() {
  176. for _, c := range s.spec.Consumes {
  177. s.consumes[c] = struct{}{}
  178. }
  179. for _, c := range s.spec.Produces {
  180. s.produces[c] = struct{}{}
  181. }
  182. for _, ss := range s.spec.Security {
  183. for k := range ss {
  184. s.authSchemes[k] = struct{}{}
  185. }
  186. }
  187. for path, pathItem := range s.AllPaths() {
  188. s.analyzeOperations(path, &pathItem)
  189. }
  190. for name, parameter := range s.spec.Parameters {
  191. refPref := slashpath.Join("/parameters", jsonpointer.Escape(name))
  192. if parameter.Items != nil {
  193. s.analyzeItems("items", parameter.Items, refPref, "parameter")
  194. }
  195. if parameter.In == "body" && parameter.Schema != nil {
  196. s.analyzeSchema("schema", *parameter.Schema, refPref)
  197. }
  198. if parameter.Pattern != "" {
  199. s.patterns.addParameterPattern(refPref, parameter.Pattern)
  200. }
  201. if len(parameter.Enum) > 0 {
  202. s.enums.addParameterEnum(refPref, parameter.Enum)
  203. }
  204. }
  205. for name, response := range s.spec.Responses {
  206. refPref := slashpath.Join("/responses", jsonpointer.Escape(name))
  207. for k, v := range response.Headers {
  208. hRefPref := slashpath.Join(refPref, "headers", k)
  209. if v.Items != nil {
  210. s.analyzeItems("items", v.Items, hRefPref, "header")
  211. }
  212. if v.Pattern != "" {
  213. s.patterns.addHeaderPattern(hRefPref, v.Pattern)
  214. }
  215. if len(v.Enum) > 0 {
  216. s.enums.addHeaderEnum(hRefPref, v.Enum)
  217. }
  218. }
  219. if response.Schema != nil {
  220. s.analyzeSchema("schema", *response.Schema, refPref)
  221. }
  222. }
  223. for name, schema := range s.spec.Definitions {
  224. s.analyzeSchema(name, schema, "/definitions")
  225. }
  226. // TODO: after analyzing all things and flattening schemas etc
  227. // resolve all the collected references to their final representations
  228. // best put in a separate method because this could get expensive
  229. }
  230. func (s *Spec) analyzeOperations(path string, pi *spec.PathItem) {
  231. // TODO: resolve refs here?
  232. // Currently, operations declared via pathItem $ref are known only after expansion
  233. op := pi
  234. if pi.Ref.String() != "" {
  235. key := slashpath.Join("/paths", jsonpointer.Escape(path))
  236. s.references.addPathItemRef(key, pi)
  237. }
  238. s.analyzeOperation("GET", path, op.Get)
  239. s.analyzeOperation("PUT", path, op.Put)
  240. s.analyzeOperation("POST", path, op.Post)
  241. s.analyzeOperation("PATCH", path, op.Patch)
  242. s.analyzeOperation("DELETE", path, op.Delete)
  243. s.analyzeOperation("HEAD", path, op.Head)
  244. s.analyzeOperation("OPTIONS", path, op.Options)
  245. for i, param := range op.Parameters {
  246. refPref := slashpath.Join("/paths", jsonpointer.Escape(path), "parameters", strconv.Itoa(i))
  247. if param.Ref.String() != "" {
  248. s.references.addParamRef(refPref, &param)
  249. }
  250. if param.Pattern != "" {
  251. s.patterns.addParameterPattern(refPref, param.Pattern)
  252. }
  253. if len(param.Enum) > 0 {
  254. s.enums.addParameterEnum(refPref, param.Enum)
  255. }
  256. if param.Items != nil {
  257. s.analyzeItems("items", param.Items, refPref, "parameter")
  258. }
  259. if param.Schema != nil {
  260. s.analyzeSchema("schema", *param.Schema, refPref)
  261. }
  262. }
  263. }
  264. func (s *Spec) analyzeItems(name string, items *spec.Items, prefix, location string) {
  265. if items == nil {
  266. return
  267. }
  268. refPref := slashpath.Join(prefix, name)
  269. s.analyzeItems(name, items.Items, refPref, location)
  270. if items.Ref.String() != "" {
  271. s.references.addItemsRef(refPref, items, location)
  272. }
  273. if items.Pattern != "" {
  274. s.patterns.addItemsPattern(refPref, items.Pattern)
  275. }
  276. if len(items.Enum) > 0 {
  277. s.enums.addItemsEnum(refPref, items.Enum)
  278. }
  279. }
  280. func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) {
  281. if op == nil {
  282. return
  283. }
  284. for _, c := range op.Consumes {
  285. s.consumes[c] = struct{}{}
  286. }
  287. for _, c := range op.Produces {
  288. s.produces[c] = struct{}{}
  289. }
  290. for _, ss := range op.Security {
  291. for k := range ss {
  292. s.authSchemes[k] = struct{}{}
  293. }
  294. }
  295. if _, ok := s.operations[method]; !ok {
  296. s.operations[method] = make(map[string]*spec.Operation)
  297. }
  298. s.operations[method][path] = op
  299. prefix := slashpath.Join("/paths", jsonpointer.Escape(path), strings.ToLower(method))
  300. for i, param := range op.Parameters {
  301. refPref := slashpath.Join(prefix, "parameters", strconv.Itoa(i))
  302. if param.Ref.String() != "" {
  303. s.references.addParamRef(refPref, &param)
  304. }
  305. if param.Pattern != "" {
  306. s.patterns.addParameterPattern(refPref, param.Pattern)
  307. }
  308. if len(param.Enum) > 0 {
  309. s.enums.addParameterEnum(refPref, param.Enum)
  310. }
  311. s.analyzeItems("items", param.Items, refPref, "parameter")
  312. if param.In == "body" && param.Schema != nil {
  313. s.analyzeSchema("schema", *param.Schema, refPref)
  314. }
  315. }
  316. if op.Responses != nil {
  317. if op.Responses.Default != nil {
  318. refPref := slashpath.Join(prefix, "responses", "default")
  319. if op.Responses.Default.Ref.String() != "" {
  320. s.references.addResponseRef(refPref, op.Responses.Default)
  321. }
  322. for k, v := range op.Responses.Default.Headers {
  323. hRefPref := slashpath.Join(refPref, "headers", k)
  324. s.analyzeItems("items", v.Items, hRefPref, "header")
  325. if v.Pattern != "" {
  326. s.patterns.addHeaderPattern(hRefPref, v.Pattern)
  327. }
  328. }
  329. if op.Responses.Default.Schema != nil {
  330. s.analyzeSchema("schema", *op.Responses.Default.Schema, refPref)
  331. }
  332. }
  333. for k, res := range op.Responses.StatusCodeResponses {
  334. refPref := slashpath.Join(prefix, "responses", strconv.Itoa(k))
  335. if res.Ref.String() != "" {
  336. s.references.addResponseRef(refPref, &res)
  337. }
  338. for k, v := range res.Headers {
  339. hRefPref := slashpath.Join(refPref, "headers", k)
  340. s.analyzeItems("items", v.Items, hRefPref, "header")
  341. if v.Pattern != "" {
  342. s.patterns.addHeaderPattern(hRefPref, v.Pattern)
  343. }
  344. if len(v.Enum) > 0 {
  345. s.enums.addHeaderEnum(hRefPref, v.Enum)
  346. }
  347. }
  348. if res.Schema != nil {
  349. s.analyzeSchema("schema", *res.Schema, refPref)
  350. }
  351. }
  352. }
  353. }
  354. func (s *Spec) analyzeSchema(name string, schema spec.Schema, prefix string) {
  355. refURI := slashpath.Join(prefix, jsonpointer.Escape(name))
  356. schRef := SchemaRef{
  357. Name: name,
  358. Schema: &schema,
  359. Ref: spec.MustCreateRef("#" + refURI),
  360. TopLevel: prefix == "/definitions",
  361. }
  362. s.allSchemas["#"+refURI] = schRef
  363. if schema.Ref.String() != "" {
  364. s.references.addSchemaRef(refURI, schRef)
  365. }
  366. if schema.Pattern != "" {
  367. s.patterns.addSchemaPattern(refURI, schema.Pattern)
  368. }
  369. if len(schema.Enum) > 0 {
  370. s.enums.addSchemaEnum(refURI, schema.Enum)
  371. }
  372. for k, v := range schema.Definitions {
  373. s.analyzeSchema(k, v, slashpath.Join(refURI, "definitions"))
  374. }
  375. for k, v := range schema.Properties {
  376. s.analyzeSchema(k, v, slashpath.Join(refURI, "properties"))
  377. }
  378. for k, v := range schema.PatternProperties {
  379. // NOTE: swagger 2.0 does not support PatternProperties.
  380. // However it is possible to analyze this in a schema
  381. s.analyzeSchema(k, v, slashpath.Join(refURI, "patternProperties"))
  382. }
  383. for i, v := range schema.AllOf {
  384. s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "allOf"))
  385. }
  386. if len(schema.AllOf) > 0 {
  387. s.allOfs["#"+refURI] = schRef
  388. }
  389. for i, v := range schema.AnyOf {
  390. // NOTE: swagger 2.0 does not support anyOf constructs.
  391. // However it is possible to analyze this in a schema
  392. s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "anyOf"))
  393. }
  394. for i, v := range schema.OneOf {
  395. // NOTE: swagger 2.0 does not support oneOf constructs.
  396. // However it is possible to analyze this in a schema
  397. s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "oneOf"))
  398. }
  399. if schema.Not != nil {
  400. // NOTE: swagger 2.0 does not support "not" constructs.
  401. // However it is possible to analyze this in a schema
  402. s.analyzeSchema("not", *schema.Not, refURI)
  403. }
  404. if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
  405. s.analyzeSchema("additionalProperties", *schema.AdditionalProperties.Schema, refURI)
  406. }
  407. if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
  408. // NOTE: swagger 2.0 does not support AdditionalItems.
  409. // However it is possible to analyze this in a schema
  410. s.analyzeSchema("additionalItems", *schema.AdditionalItems.Schema, refURI)
  411. }
  412. if schema.Items != nil {
  413. if schema.Items.Schema != nil {
  414. s.analyzeSchema("items", *schema.Items.Schema, refURI)
  415. }
  416. for i, sch := range schema.Items.Schemas {
  417. s.analyzeSchema(strconv.Itoa(i), sch, slashpath.Join(refURI, "items"))
  418. }
  419. }
  420. }
  421. // SecurityRequirement is a representation of a security requirement for an operation
  422. type SecurityRequirement struct {
  423. Name string
  424. Scopes []string
  425. }
  426. // SecurityRequirementsFor gets the security requirements for the operation
  427. func (s *Spec) SecurityRequirementsFor(operation *spec.Operation) [][]SecurityRequirement {
  428. if s.spec.Security == nil && operation.Security == nil {
  429. return nil
  430. }
  431. schemes := s.spec.Security
  432. if operation.Security != nil {
  433. schemes = operation.Security
  434. }
  435. result := [][]SecurityRequirement{}
  436. for _, scheme := range schemes {
  437. if len(scheme) == 0 {
  438. // append a zero object for anonymous
  439. result = append(result, []SecurityRequirement{{}})
  440. continue
  441. }
  442. var reqs []SecurityRequirement
  443. for k, v := range scheme {
  444. if v == nil {
  445. v = []string{}
  446. }
  447. reqs = append(reqs, SecurityRequirement{Name: k, Scopes: v})
  448. }
  449. result = append(result, reqs)
  450. }
  451. return result
  452. }
  453. // SecurityDefinitionsForRequirements gets the matching security definitions for a set of requirements
  454. func (s *Spec) SecurityDefinitionsForRequirements(requirements []SecurityRequirement) map[string]spec.SecurityScheme {
  455. result := make(map[string]spec.SecurityScheme)
  456. for _, v := range requirements {
  457. if definition, ok := s.spec.SecurityDefinitions[v.Name]; ok {
  458. if definition != nil {
  459. result[v.Name] = *definition
  460. }
  461. }
  462. }
  463. return result
  464. }
  465. // SecurityDefinitionsFor gets the matching security definitions for a set of requirements
  466. func (s *Spec) SecurityDefinitionsFor(operation *spec.Operation) map[string]spec.SecurityScheme {
  467. requirements := s.SecurityRequirementsFor(operation)
  468. if len(requirements) == 0 {
  469. return nil
  470. }
  471. result := make(map[string]spec.SecurityScheme)
  472. for _, reqs := range requirements {
  473. for _, v := range reqs {
  474. if v.Name == "" {
  475. // optional requirement
  476. continue
  477. }
  478. if _, ok := result[v.Name]; ok {
  479. // duplicate requirement
  480. continue
  481. }
  482. if definition, ok := s.spec.SecurityDefinitions[v.Name]; ok {
  483. if definition != nil {
  484. result[v.Name] = *definition
  485. }
  486. }
  487. }
  488. }
  489. return result
  490. }
  491. // ConsumesFor gets the mediatypes for the operation
  492. func (s *Spec) ConsumesFor(operation *spec.Operation) []string {
  493. if len(operation.Consumes) == 0 {
  494. cons := make(map[string]struct{}, len(s.spec.Consumes))
  495. for _, k := range s.spec.Consumes {
  496. cons[k] = struct{}{}
  497. }
  498. return s.structMapKeys(cons)
  499. }
  500. cons := make(map[string]struct{}, len(operation.Consumes))
  501. for _, c := range operation.Consumes {
  502. cons[c] = struct{}{}
  503. }
  504. return s.structMapKeys(cons)
  505. }
  506. // ProducesFor gets the mediatypes for the operation
  507. func (s *Spec) ProducesFor(operation *spec.Operation) []string {
  508. if len(operation.Produces) == 0 {
  509. prod := make(map[string]struct{}, len(s.spec.Produces))
  510. for _, k := range s.spec.Produces {
  511. prod[k] = struct{}{}
  512. }
  513. return s.structMapKeys(prod)
  514. }
  515. prod := make(map[string]struct{}, len(operation.Produces))
  516. for _, c := range operation.Produces {
  517. prod[c] = struct{}{}
  518. }
  519. return s.structMapKeys(prod)
  520. }
  521. func mapKeyFromParam(param *spec.Parameter) string {
  522. return fmt.Sprintf("%s#%s", param.In, fieldNameFromParam(param))
  523. }
  524. func fieldNameFromParam(param *spec.Parameter) string {
  525. // TODO: this should be x-go-name
  526. if nm, ok := param.Extensions.GetString("go-name"); ok {
  527. return nm
  528. }
  529. return swag.ToGoName(param.Name)
  530. }
  531. // ErrorOnParamFunc is a callback function to be invoked
  532. // whenever an error is encountered while resolving references
  533. // on parameters.
  534. //
  535. // This function takes as input the spec.Parameter which triggered the
  536. // error and the error itself.
  537. //
  538. // If the callback function returns false, the calling function should bail.
  539. //
  540. // If it returns true, the calling function should continue evaluating parameters.
  541. // A nil ErrorOnParamFunc must be evaluated as equivalent to panic().
  542. type ErrorOnParamFunc func(spec.Parameter, error) bool
  543. func (s *Spec) paramsAsMap(parameters []spec.Parameter, res map[string]spec.Parameter, callmeOnError ErrorOnParamFunc) {
  544. for _, param := range parameters {
  545. pr := param
  546. if pr.Ref.String() != "" {
  547. obj, _, err := pr.Ref.GetPointer().Get(s.spec)
  548. if err != nil {
  549. if callmeOnError != nil {
  550. if callmeOnError(param, fmt.Errorf("invalid reference: %q", pr.Ref.String())) {
  551. continue
  552. }
  553. break
  554. } else {
  555. panic(fmt.Sprintf("invalid reference: %q", pr.Ref.String()))
  556. }
  557. }
  558. if objAsParam, ok := obj.(spec.Parameter); ok {
  559. pr = objAsParam
  560. } else {
  561. if callmeOnError != nil {
  562. if callmeOnError(param, fmt.Errorf("resolved reference is not a parameter: %q", pr.Ref.String())) {
  563. continue
  564. }
  565. break
  566. } else {
  567. panic(fmt.Sprintf("resolved reference is not a parameter: %q", pr.Ref.String()))
  568. }
  569. }
  570. }
  571. res[mapKeyFromParam(&pr)] = pr
  572. }
  573. }
  574. // ParametersFor the specified operation id.
  575. //
  576. // Assumes parameters properly resolve references if any and that
  577. // such references actually resolve to a parameter object.
  578. // Otherwise, panics.
  579. func (s *Spec) ParametersFor(operationID string) []spec.Parameter {
  580. return s.SafeParametersFor(operationID, nil)
  581. }
  582. // SafeParametersFor the specified operation id.
  583. //
  584. // Does not assume parameters properly resolve references or that
  585. // such references actually resolve to a parameter object.
  586. //
  587. // Upon error, invoke a ErrorOnParamFunc callback with the erroneous
  588. // parameters. If the callback is set to nil, panics upon errors.
  589. func (s *Spec) SafeParametersFor(operationID string, callmeOnError ErrorOnParamFunc) []spec.Parameter {
  590. gatherParams := func(pi *spec.PathItem, op *spec.Operation) []spec.Parameter {
  591. bag := make(map[string]spec.Parameter)
  592. s.paramsAsMap(pi.Parameters, bag, callmeOnError)
  593. s.paramsAsMap(op.Parameters, bag, callmeOnError)
  594. var res []spec.Parameter
  595. for _, v := range bag {
  596. res = append(res, v)
  597. }
  598. return res
  599. }
  600. for _, pi := range s.spec.Paths.Paths {
  601. if pi.Get != nil && pi.Get.ID == operationID {
  602. return gatherParams(&pi, pi.Get)
  603. }
  604. if pi.Head != nil && pi.Head.ID == operationID {
  605. return gatherParams(&pi, pi.Head)
  606. }
  607. if pi.Options != nil && pi.Options.ID == operationID {
  608. return gatherParams(&pi, pi.Options)
  609. }
  610. if pi.Post != nil && pi.Post.ID == operationID {
  611. return gatherParams(&pi, pi.Post)
  612. }
  613. if pi.Patch != nil && pi.Patch.ID == operationID {
  614. return gatherParams(&pi, pi.Patch)
  615. }
  616. if pi.Put != nil && pi.Put.ID == operationID {
  617. return gatherParams(&pi, pi.Put)
  618. }
  619. if pi.Delete != nil && pi.Delete.ID == operationID {
  620. return gatherParams(&pi, pi.Delete)
  621. }
  622. }
  623. return nil
  624. }
  625. // ParamsFor the specified method and path. Aggregates them with the defaults etc, so it's all the params that
  626. // apply for the method and path.
  627. //
  628. // Assumes parameters properly resolve references if any and that
  629. // such references actually resolve to a parameter object.
  630. // Otherwise, panics.
  631. func (s *Spec) ParamsFor(method, path string) map[string]spec.Parameter {
  632. return s.SafeParamsFor(method, path, nil)
  633. }
  634. // SafeParamsFor the specified method and path. Aggregates them with the defaults etc, so it's all the params that
  635. // apply for the method and path.
  636. //
  637. // Does not assume parameters properly resolve references or that
  638. // such references actually resolve to a parameter object.
  639. //
  640. // Upon error, invoke a ErrorOnParamFunc callback with the erroneous
  641. // parameters. If the callback is set to nil, panics upon errors.
  642. func (s *Spec) SafeParamsFor(method, path string, callmeOnError ErrorOnParamFunc) map[string]spec.Parameter {
  643. res := make(map[string]spec.Parameter)
  644. if pi, ok := s.spec.Paths.Paths[path]; ok {
  645. s.paramsAsMap(pi.Parameters, res, callmeOnError)
  646. s.paramsAsMap(s.operations[strings.ToUpper(method)][path].Parameters, res, callmeOnError)
  647. }
  648. return res
  649. }
  650. // OperationForName gets the operation for the given id
  651. func (s *Spec) OperationForName(operationID string) (string, string, *spec.Operation, bool) {
  652. for method, pathItem := range s.operations {
  653. for path, op := range pathItem {
  654. if operationID == op.ID {
  655. return method, path, op, true
  656. }
  657. }
  658. }
  659. return "", "", nil, false
  660. }
  661. // OperationFor the given method and path
  662. func (s *Spec) OperationFor(method, path string) (*spec.Operation, bool) {
  663. if mp, ok := s.operations[strings.ToUpper(method)]; ok {
  664. op, fn := mp[path]
  665. return op, fn
  666. }
  667. return nil, false
  668. }
  669. // Operations gathers all the operations specified in the spec document
  670. func (s *Spec) Operations() map[string]map[string]*spec.Operation {
  671. return s.operations
  672. }
  673. func (s *Spec) structMapKeys(mp map[string]struct{}) []string {
  674. if len(mp) == 0 {
  675. return nil
  676. }
  677. result := make([]string, 0, len(mp))
  678. for k := range mp {
  679. result = append(result, k)
  680. }
  681. return result
  682. }
  683. // AllPaths returns all the paths in the swagger spec
  684. func (s *Spec) AllPaths() map[string]spec.PathItem {
  685. if s.spec == nil || s.spec.Paths == nil {
  686. return nil
  687. }
  688. return s.spec.Paths.Paths
  689. }
  690. // OperationIDs gets all the operation ids based on method an dpath
  691. func (s *Spec) OperationIDs() []string {
  692. if len(s.operations) == 0 {
  693. return nil
  694. }
  695. result := make([]string, 0, len(s.operations))
  696. for method, v := range s.operations {
  697. for p, o := range v {
  698. if o.ID != "" {
  699. result = append(result, o.ID)
  700. } else {
  701. result = append(result, fmt.Sprintf("%s %s", strings.ToUpper(method), p))
  702. }
  703. }
  704. }
  705. return result
  706. }
  707. // OperationMethodPaths gets all the operation ids based on method an dpath
  708. func (s *Spec) OperationMethodPaths() []string {
  709. if len(s.operations) == 0 {
  710. return nil
  711. }
  712. result := make([]string, 0, len(s.operations))
  713. for method, v := range s.operations {
  714. for p := range v {
  715. result = append(result, fmt.Sprintf("%s %s", strings.ToUpper(method), p))
  716. }
  717. }
  718. return result
  719. }
  720. // RequiredConsumes gets all the distinct consumes that are specified in the specification document
  721. func (s *Spec) RequiredConsumes() []string {
  722. return s.structMapKeys(s.consumes)
  723. }
  724. // RequiredProduces gets all the distinct produces that are specified in the specification document
  725. func (s *Spec) RequiredProduces() []string {
  726. return s.structMapKeys(s.produces)
  727. }
  728. // RequiredSecuritySchemes gets all the distinct security schemes that are specified in the swagger spec
  729. func (s *Spec) RequiredSecuritySchemes() []string {
  730. return s.structMapKeys(s.authSchemes)
  731. }
  732. // SchemaRef is a reference to a schema
  733. type SchemaRef struct {
  734. Name string
  735. Ref spec.Ref
  736. Schema *spec.Schema
  737. TopLevel bool
  738. }
  739. // SchemasWithAllOf returns schema references to all schemas that are defined
  740. // with an allOf key
  741. func (s *Spec) SchemasWithAllOf() (result []SchemaRef) {
  742. for _, v := range s.allOfs {
  743. result = append(result, v)
  744. }
  745. return
  746. }
  747. // AllDefinitions returns schema references for all the definitions that were discovered
  748. func (s *Spec) AllDefinitions() (result []SchemaRef) {
  749. for _, v := range s.allSchemas {
  750. result = append(result, v)
  751. }
  752. return
  753. }
  754. // AllDefinitionReferences returns json refs for all the discovered schemas
  755. func (s *Spec) AllDefinitionReferences() (result []string) {
  756. for _, v := range s.references.schemas {
  757. result = append(result, v.String())
  758. }
  759. return
  760. }
  761. // AllParameterReferences returns json refs for all the discovered parameters
  762. func (s *Spec) AllParameterReferences() (result []string) {
  763. for _, v := range s.references.parameters {
  764. result = append(result, v.String())
  765. }
  766. return
  767. }
  768. // AllResponseReferences returns json refs for all the discovered responses
  769. func (s *Spec) AllResponseReferences() (result []string) {
  770. for _, v := range s.references.responses {
  771. result = append(result, v.String())
  772. }
  773. return
  774. }
  775. // AllPathItemReferences returns the references for all the items
  776. func (s *Spec) AllPathItemReferences() (result []string) {
  777. for _, v := range s.references.pathItems {
  778. result = append(result, v.String())
  779. }
  780. return
  781. }
  782. // AllItemsReferences returns the references for all the items in simple schemas (parameters or headers).
  783. //
  784. // NOTE: since Swagger 2.0 forbids $ref in simple params, this should always yield an empty slice for a valid
  785. // Swagger 2.0 spec.
  786. func (s *Spec) AllItemsReferences() (result []string) {
  787. for _, v := range s.references.items {
  788. result = append(result, v.String())
  789. }
  790. return
  791. }
  792. // AllReferences returns all the references found in the document, with possible duplicates
  793. func (s *Spec) AllReferences() (result []string) {
  794. for _, v := range s.references.allRefs {
  795. result = append(result, v.String())
  796. }
  797. return
  798. }
  799. // AllRefs returns all the unique references found in the document
  800. func (s *Spec) AllRefs() (result []spec.Ref) {
  801. set := make(map[string]struct{})
  802. for _, v := range s.references.allRefs {
  803. a := v.String()
  804. if a == "" {
  805. continue
  806. }
  807. if _, ok := set[a]; !ok {
  808. set[a] = struct{}{}
  809. result = append(result, v)
  810. }
  811. }
  812. return
  813. }
  814. func cloneStringMap(source map[string]string) map[string]string {
  815. res := make(map[string]string, len(source))
  816. for k, v := range source {
  817. res[k] = v
  818. }
  819. return res
  820. }
  821. func cloneEnumMap(source map[string][]interface{}) map[string][]interface{} {
  822. res := make(map[string][]interface{}, len(source))
  823. for k, v := range source {
  824. res[k] = v
  825. }
  826. return res
  827. }
  828. // ParameterPatterns returns all the patterns found in parameters
  829. // the map is cloned to avoid accidental changes
  830. func (s *Spec) ParameterPatterns() map[string]string {
  831. return cloneStringMap(s.patterns.parameters)
  832. }
  833. // HeaderPatterns returns all the patterns found in response headers
  834. // the map is cloned to avoid accidental changes
  835. func (s *Spec) HeaderPatterns() map[string]string {
  836. return cloneStringMap(s.patterns.headers)
  837. }
  838. // ItemsPatterns returns all the patterns found in simple array items
  839. // the map is cloned to avoid accidental changes
  840. func (s *Spec) ItemsPatterns() map[string]string {
  841. return cloneStringMap(s.patterns.items)
  842. }
  843. // SchemaPatterns returns all the patterns found in schemas
  844. // the map is cloned to avoid accidental changes
  845. func (s *Spec) SchemaPatterns() map[string]string {
  846. return cloneStringMap(s.patterns.schemas)
  847. }
  848. // AllPatterns returns all the patterns found in the spec
  849. // the map is cloned to avoid accidental changes
  850. func (s *Spec) AllPatterns() map[string]string {
  851. return cloneStringMap(s.patterns.allPatterns)
  852. }
  853. // ParameterEnums returns all the enums found in parameters
  854. // the map is cloned to avoid accidental changes
  855. func (s *Spec) ParameterEnums() map[string][]interface{} {
  856. return cloneEnumMap(s.enums.parameters)
  857. }
  858. // HeaderEnums returns all the enums found in response headers
  859. // the map is cloned to avoid accidental changes
  860. func (s *Spec) HeaderEnums() map[string][]interface{} {
  861. return cloneEnumMap(s.enums.headers)
  862. }
  863. // ItemsEnums returns all the enums found in simple array items
  864. // the map is cloned to avoid accidental changes
  865. func (s *Spec) ItemsEnums() map[string][]interface{} {
  866. return cloneEnumMap(s.enums.items)
  867. }
  868. // SchemaEnums returns all the enums found in schemas
  869. // the map is cloned to avoid accidental changes
  870. func (s *Spec) SchemaEnums() map[string][]interface{} {
  871. return cloneEnumMap(s.enums.schemas)
  872. }
  873. // AllEnums returns all the enums found in the spec
  874. // the map is cloned to avoid accidental changes
  875. func (s *Spec) AllEnums() map[string][]interface{} {
  876. return cloneEnumMap(s.enums.allEnums)
  877. }