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