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