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