github.com/6543-forks/go-swagger@v0.26.0/generator/operation.go (about) 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 15 package generator 16 17 import ( 18 "encoding/json" 19 "errors" 20 "fmt" 21 "path/filepath" 22 "sort" 23 "strings" 24 25 "github.com/go-openapi/analysis" 26 "github.com/go-openapi/loads" 27 "github.com/go-openapi/runtime" 28 "github.com/go-openapi/spec" 29 "github.com/go-openapi/swag" 30 ) 31 32 type respSort struct { 33 Code int 34 Response spec.Response 35 } 36 37 type responses []respSort 38 39 func (s responses) Len() int { return len(s) } 40 func (s responses) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 41 func (s responses) Less(i, j int) bool { return s[i].Code < s[j].Code } 42 43 // sortedResponses produces a sorted list of responses. 44 // TODO: this is redundant with the definition given in struct.go 45 func sortedResponses(input map[int]spec.Response) responses { 46 var res responses 47 for k, v := range input { 48 if k > 0 { 49 res = append(res, respSort{k, v}) 50 } 51 } 52 sort.Sort(res) 53 return res 54 } 55 56 // GenerateServerOperation generates a parameter model, parameter validator, http handler implementations for a given operation. 57 // 58 // It also generates an operation handler interface that uses the parameter model for handling a valid request. 59 // Allows for specifying a list of tags to include only certain tags for the generation 60 func GenerateServerOperation(operationNames []string, opts *GenOpts) error { 61 if err := opts.CheckOpts(); err != nil { 62 return err 63 } 64 65 if err := opts.setTemplates(); err != nil { 66 return err 67 } 68 69 specDoc, analyzed, err := opts.analyzeSpec() 70 if err != nil { 71 return err 72 } 73 74 ops := gatherOperations(analyzed, operationNames) 75 76 if len(ops) == 0 { 77 return errors.New("no operations were selected") 78 } 79 80 for operationName, opRef := range ops { 81 method, path, operation := opRef.Method, opRef.Path, opRef.Op 82 83 serverPackage := opts.LanguageOpts.ManglePackagePath(opts.ServerPackage, defaultServerTarget) 84 generator := operationGenerator{ 85 Name: operationName, 86 Method: method, 87 Path: path, 88 BasePath: specDoc.BasePath(), 89 APIPackage: opts.LanguageOpts.ManglePackagePath(opts.APIPackage, defaultOperationsTarget), 90 ModelsPackage: opts.LanguageOpts.ManglePackagePath(opts.ModelPackage, defaultModelsTarget), 91 ClientPackage: opts.LanguageOpts.ManglePackagePath(opts.ClientPackage, defaultClientTarget), 92 ServerPackage: serverPackage, 93 Operation: *operation, 94 SecurityRequirements: analyzed.SecurityRequirementsFor(operation), 95 SecurityDefinitions: analyzed.SecurityDefinitionsFor(operation), 96 Principal: opts.PrincipalAlias(), 97 Target: filepath.Join(opts.Target, filepath.FromSlash(serverPackage)), 98 Base: opts.Target, 99 Tags: opts.Tags, 100 IncludeHandler: opts.IncludeHandler, 101 IncludeParameters: opts.IncludeParameters, 102 IncludeResponses: opts.IncludeResponses, 103 IncludeValidator: opts.IncludeValidator, 104 DumpData: opts.DumpData, 105 DefaultScheme: opts.DefaultScheme, 106 DefaultProduces: opts.DefaultProduces, 107 DefaultConsumes: opts.DefaultConsumes, 108 Doc: specDoc, 109 Analyzed: analyzed, 110 GenOpts: opts, 111 } 112 if err := generator.Generate(); err != nil { 113 return err 114 } 115 } 116 return nil 117 } 118 119 type operationGenerator struct { 120 Authorized bool 121 IncludeHandler bool 122 IncludeParameters bool 123 IncludeResponses bool 124 IncludeValidator bool 125 DumpData bool 126 127 Principal string 128 Target string 129 Base string 130 Name string 131 Method string 132 Path string 133 BasePath string 134 APIPackage string 135 ModelsPackage string 136 ServerPackage string 137 ClientPackage string 138 Operation spec.Operation 139 SecurityRequirements [][]analysis.SecurityRequirement 140 SecurityDefinitions map[string]spec.SecurityScheme 141 Tags []string 142 DefaultScheme string 143 DefaultProduces string 144 DefaultConsumes string 145 Doc *loads.Document 146 Analyzed *analysis.Spec 147 GenOpts *GenOpts 148 } 149 150 // Generate a single operation 151 func (o *operationGenerator) Generate() error { 152 153 defaultImports := o.GenOpts.defaultImports() 154 155 apiPackage := o.GenOpts.LanguageOpts.ManglePackagePath(o.GenOpts.APIPackage, defaultOperationsTarget) 156 imports := o.GenOpts.initImports( 157 filepath.Join(o.GenOpts.LanguageOpts.ManglePackagePath(o.GenOpts.ServerPackage, defaultServerTarget), apiPackage)) 158 159 bldr := codeGenOpBuilder{ 160 ModelsPackage: o.ModelsPackage, 161 Principal: o.GenOpts.PrincipalAlias(), 162 Target: o.Target, 163 DefaultImports: defaultImports, 164 Imports: imports, 165 DefaultScheme: o.DefaultScheme, 166 Doc: o.Doc, 167 Analyzed: o.Analyzed, 168 BasePath: o.BasePath, 169 GenOpts: o.GenOpts, 170 Name: o.Name, 171 Operation: o.Operation, 172 Method: o.Method, 173 Path: o.Path, 174 IncludeValidator: o.IncludeValidator, 175 APIPackage: o.APIPackage, // defaults to main operations package 176 DefaultProduces: o.DefaultProduces, 177 DefaultConsumes: o.DefaultConsumes, 178 Authed: len(o.Analyzed.SecurityRequirementsFor(&o.Operation)) > 0, 179 Security: o.Analyzed.SecurityRequirementsFor(&o.Operation), 180 SecurityDefinitions: o.Analyzed.SecurityDefinitionsFor(&o.Operation), 181 RootAPIPackage: o.GenOpts.LanguageOpts.ManglePackageName(o.ServerPackage, defaultServerTarget), 182 } 183 184 _, tags, _ := bldr.analyzeTags() 185 186 op, err := bldr.MakeOperation() 187 if err != nil { 188 return err 189 } 190 191 op.Tags = tags 192 operations := make(GenOperations, 0, 1) 193 operations = append(operations, op) 194 sort.Sort(operations) 195 196 for _, op := range operations { 197 if o.GenOpts.DumpData { 198 _ = dumpData(swag.ToDynamicJSON(op)) 199 continue 200 } 201 if err := o.GenOpts.renderOperation(&op); err != nil { 202 return err 203 } 204 } 205 206 return nil 207 } 208 209 type codeGenOpBuilder struct { 210 Authed bool 211 IncludeValidator bool 212 213 Name string 214 Method string 215 Path string 216 BasePath string 217 APIPackage string 218 APIPackageAlias string 219 RootAPIPackage string 220 ModelsPackage string 221 Principal string 222 Target string 223 Operation spec.Operation 224 Doc *loads.Document 225 PristineDoc *loads.Document 226 Analyzed *analysis.Spec 227 DefaultImports map[string]string 228 Imports map[string]string 229 DefaultScheme string 230 DefaultProduces string 231 DefaultConsumes string 232 Security [][]analysis.SecurityRequirement 233 SecurityDefinitions map[string]spec.SecurityScheme 234 ExtraSchemas map[string]GenSchema 235 GenOpts *GenOpts 236 } 237 238 // paramMappings yields a map of safe parameter names for an operation 239 func paramMappings(params map[string]spec.Parameter) (map[string]map[string]string, string) { 240 idMapping := map[string]map[string]string{ 241 "query": make(map[string]string, len(params)), 242 "path": make(map[string]string, len(params)), 243 "formData": make(map[string]string, len(params)), 244 "header": make(map[string]string, len(params)), 245 "body": make(map[string]string, len(params)), 246 } 247 248 // In order to avoid unstable generation, adopt same naming convention 249 // for all parameters with same name across locations. 250 seenIds := make(map[string]interface{}, len(params)) 251 for id, p := range params { 252 if val, ok := seenIds[p.Name]; ok { 253 previous := val.(struct{ id, in string }) 254 idMapping[p.In][p.Name] = swag.ToGoName(id) 255 // rewrite the previously found one 256 idMapping[previous.in][p.Name] = swag.ToGoName(previous.id) 257 } else { 258 idMapping[p.In][p.Name] = swag.ToGoName(p.Name) 259 } 260 seenIds[strings.ToLower(idMapping[p.In][p.Name])] = struct{ id, in string }{id: id, in: p.In} 261 } 262 263 // pick a deconflicted private name for timeout for this operation 264 timeoutName := renameTimeout(seenIds, "timeout") 265 266 return idMapping, timeoutName 267 } 268 269 // renameTimeout renames the variable in use by client template to avoid conflicting 270 // with param names. 271 // 272 // NOTE: this merely protects the timeout field in the client parameter struct, 273 // fields "Context" and "HTTPClient" remain exposed to name conflicts. 274 func renameTimeout(seenIds map[string]interface{}, timeoutName string) string { 275 if seenIds == nil { 276 return timeoutName 277 } 278 current := strings.ToLower(timeoutName) 279 if _, ok := seenIds[current]; !ok { 280 return timeoutName 281 } 282 var next string 283 switch current { 284 case "timeout": 285 next = "requestTimeout" 286 case "requesttimeout": 287 next = "httpRequestTimeout" 288 case "httprequesttimeout": 289 next = "swaggerTimeout" 290 case "swaggertimeout": 291 next = "operationTimeout" 292 case "operationtimeout": 293 next = "opTimeout" 294 case "optimeout": 295 next = "operTimeout" 296 default: 297 next = timeoutName + "1" 298 } 299 return renameTimeout(seenIds, next) 300 } 301 302 func (b *codeGenOpBuilder) MakeOperation() (GenOperation, error) { 303 debugLog("[%s %s] parsing operation (id: %q)", b.Method, b.Path, b.Operation.ID) 304 // NOTE: we assume flatten is enabled by default (i.e. complex constructs are resolved from the models package), 305 // but do not assume the spec is necessarily fully flattened (i.e. all schemas moved to definitions). 306 // 307 // Fully flattened means that all complex constructs are present as 308 // definitions and models produced accordingly in ModelsPackage, 309 // whereas minimal flatten simply ensures that there are no weird $ref's in the spec. 310 // 311 // When some complex anonymous constructs are specified, extra schemas are produced in the operations package. 312 // 313 // In all cases, resetting definitions to the _original_ (untransformed) spec is not an option: 314 // we take from there the spec possibly already transformed by the GenDefinitions stage. 315 resolver := newTypeResolver(b.GenOpts.LanguageOpts.ManglePackageName(b.ModelsPackage, defaultModelsTarget), b.Doc) 316 receiver := "o" 317 318 operation := b.Operation 319 var params, qp, pp, hp, fp GenParameters 320 var hasQueryParams, hasPathParams, hasHeaderParams, hasFormParams, hasFileParams, hasFormValueParams, hasBodyParams bool 321 paramsForOperation := b.Analyzed.ParamsFor(b.Method, b.Path) 322 323 idMapping, timeoutName := paramMappings(paramsForOperation) 324 325 for _, p := range paramsForOperation { 326 cp, err := b.MakeParameter(receiver, resolver, p, idMapping) 327 328 if err != nil { 329 return GenOperation{}, err 330 } 331 if cp.IsQueryParam() { 332 hasQueryParams = true 333 qp = append(qp, cp) 334 } 335 if cp.IsFormParam() { 336 if p.Type == file { 337 hasFileParams = true 338 } 339 if p.Type != file { 340 hasFormValueParams = true 341 } 342 hasFormParams = true 343 fp = append(fp, cp) 344 } 345 if cp.IsPathParam() { 346 hasPathParams = true 347 pp = append(pp, cp) 348 } 349 if cp.IsHeaderParam() { 350 hasHeaderParams = true 351 hp = append(hp, cp) 352 } 353 if cp.IsBodyParam() { 354 hasBodyParams = true 355 } 356 params = append(params, cp) 357 } 358 sort.Sort(params) 359 sort.Sort(qp) 360 sort.Sort(pp) 361 sort.Sort(hp) 362 sort.Sort(fp) 363 364 var srs responses 365 if operation.Responses != nil { 366 srs = sortedResponses(operation.Responses.StatusCodeResponses) 367 } 368 responses := make([]GenResponse, 0, len(srs)) 369 var defaultResponse *GenResponse 370 var successResponses []GenResponse 371 if operation.Responses != nil { 372 for _, v := range srs { 373 name, ok := v.Response.Extensions.GetString(xGoName) 374 if !ok { 375 // look for name of well-known codes 376 name = runtime.Statuses[v.Code] 377 if name == "" { 378 // non-standard codes deserve some name 379 name = fmt.Sprintf("Status %d", v.Code) 380 } 381 } 382 name = swag.ToJSONName(b.Name + " " + name) 383 isSuccess := v.Code/100 == 2 384 gr, err := b.MakeResponse(receiver, name, isSuccess, resolver, v.Code, v.Response) 385 if err != nil { 386 return GenOperation{}, err 387 } 388 if isSuccess { 389 successResponses = append(successResponses, gr) 390 } 391 responses = append(responses, gr) 392 } 393 394 if operation.Responses.Default != nil { 395 gr, err := b.MakeResponse(receiver, b.Name+" default", false, resolver, -1, *operation.Responses.Default) 396 if err != nil { 397 return GenOperation{}, err 398 } 399 defaultResponse = &gr 400 } 401 } 402 // Always render a default response, even when no responses were defined 403 if operation.Responses == nil || (operation.Responses.Default == nil && len(srs) == 0) { 404 gr, err := b.MakeResponse(receiver, b.Name+" default", false, resolver, -1, spec.Response{}) 405 if err != nil { 406 return GenOperation{}, err 407 } 408 defaultResponse = &gr 409 } 410 411 if b.Principal == "" { 412 b.Principal = iface 413 } 414 415 swsp := resolver.Doc.Spec() 416 var extraSchemes []string 417 if ess, ok := operation.Extensions.GetStringSlice(xSchemes); ok { 418 extraSchemes = append(extraSchemes, ess...) 419 } 420 421 if ess1, ok := swsp.Extensions.GetStringSlice(xSchemes); ok { 422 extraSchemes = concatUnique(ess1, extraSchemes) 423 } 424 sort.Strings(extraSchemes) 425 schemes := concatUnique(swsp.Schemes, operation.Schemes) 426 sort.Strings(schemes) 427 produces := producesOrDefault(operation.Produces, swsp.Produces, b.DefaultProduces) 428 sort.Strings(produces) 429 consumes := producesOrDefault(operation.Consumes, swsp.Consumes, b.DefaultConsumes) 430 sort.Strings(consumes) 431 432 var hasStreamingResponse bool 433 if defaultResponse != nil && defaultResponse.Schema != nil && defaultResponse.Schema.IsStream { 434 hasStreamingResponse = true 435 } 436 var successResponse *GenResponse 437 for _, sr := range successResponses { 438 if sr.IsSuccess { 439 successResponse = &sr 440 break 441 } 442 } 443 for _, sr := range successResponses { 444 if !hasStreamingResponse && sr.Schema != nil && sr.Schema.IsStream { 445 hasStreamingResponse = true 446 break 447 } 448 } 449 if !hasStreamingResponse { 450 for _, r := range responses { 451 if r.Schema != nil && r.Schema.IsStream { 452 hasStreamingResponse = true 453 break 454 } 455 } 456 } 457 458 return GenOperation{ 459 GenCommon: GenCommon{ 460 Copyright: b.GenOpts.Copyright, 461 TargetImportPath: b.GenOpts.LanguageOpts.baseImport(b.GenOpts.Target), 462 }, 463 Package: b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, defaultOperationsTarget), 464 PackageAlias: b.APIPackageAlias, 465 RootPackage: b.RootAPIPackage, 466 Name: b.Name, 467 Method: b.Method, 468 Path: b.Path, 469 BasePath: b.BasePath, 470 Tags: operation.Tags, 471 UseTags: len(operation.Tags) > 0 && !b.GenOpts.SkipTagPackages, 472 Description: trimBOM(operation.Description), 473 ReceiverName: receiver, 474 DefaultImports: b.DefaultImports, 475 Imports: b.Imports, 476 Params: params, 477 Summary: trimBOM(operation.Summary), 478 QueryParams: qp, 479 PathParams: pp, 480 HeaderParams: hp, 481 FormParams: fp, 482 HasQueryParams: hasQueryParams, 483 HasPathParams: hasPathParams, 484 HasHeaderParams: hasHeaderParams, 485 HasFormParams: hasFormParams, 486 HasFormValueParams: hasFormValueParams, 487 HasFileParams: hasFileParams, 488 HasBodyParams: hasBodyParams, 489 HasStreamingResponse: hasStreamingResponse, 490 Authorized: b.Authed, 491 Security: b.makeSecurityRequirements(receiver), 492 SecurityDefinitions: b.makeSecuritySchemes(receiver), 493 Principal: b.Principal, 494 Responses: responses, 495 DefaultResponse: defaultResponse, 496 SuccessResponse: successResponse, 497 SuccessResponses: successResponses, 498 ExtraSchemas: gatherExtraSchemas(b.ExtraSchemas), 499 Schemes: schemeOrDefault(schemes, b.DefaultScheme), 500 ProducesMediaTypes: produces, 501 ConsumesMediaTypes: consumes, 502 ExtraSchemes: extraSchemes, 503 TimeoutName: timeoutName, 504 Extensions: operation.Extensions, 505 StrictResponders: b.GenOpts.StrictResponders, 506 }, nil 507 } 508 509 func producesOrDefault(produces []string, fallback []string, defaultProduces string) []string { 510 if len(produces) > 0 { 511 return produces 512 } 513 if len(fallback) > 0 { 514 return fallback 515 } 516 return []string{defaultProduces} 517 } 518 519 func schemeOrDefault(schemes []string, defaultScheme string) []string { 520 if len(schemes) == 0 { 521 return []string{defaultScheme} 522 } 523 return schemes 524 } 525 526 func concatUnique(collections ...[]string) []string { 527 resultSet := make(map[string]struct{}) 528 for _, c := range collections { 529 for _, i := range c { 530 if _, ok := resultSet[i]; !ok { 531 resultSet[i] = struct{}{} 532 } 533 } 534 } 535 var result []string 536 for k := range resultSet { 537 result = append(result, k) 538 } 539 return result 540 } 541 542 func (b *codeGenOpBuilder) MakeResponse(receiver, name string, isSuccess bool, resolver *typeResolver, code int, resp spec.Response) (GenResponse, error) { 543 debugLog("[%s %s] making id %q", b.Method, b.Path, b.Operation.ID) 544 545 // assume minimal flattening has been carried on, so there is not $ref in response (but some may remain in response schema) 546 547 res := GenResponse{ 548 Package: b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, defaultOperationsTarget), 549 ModelsPackage: b.ModelsPackage, 550 ReceiverName: receiver, 551 Name: name, 552 Description: trimBOM(resp.Description), 553 DefaultImports: b.DefaultImports, 554 Imports: b.Imports, 555 IsSuccess: isSuccess, 556 Code: code, 557 Method: b.Method, 558 Path: b.Path, 559 Extensions: resp.Extensions, 560 StrictResponders: b.GenOpts.StrictResponders, 561 OperationName: b.Name, 562 } 563 564 // prepare response headers 565 for hName, header := range resp.Headers { 566 hdr, err := b.MakeHeader(receiver, hName, header) 567 if err != nil { 568 return GenResponse{}, err 569 } 570 res.Headers = append(res.Headers, hdr) 571 } 572 sort.Sort(res.Headers) 573 574 if resp.Schema != nil { 575 // resolve schema model 576 schema, ers := b.buildOperationSchema(fmt.Sprintf("%q", name), name+"Body", swag.ToGoName(name+"Body"), receiver, "i", resp.Schema, resolver) 577 if ers != nil { 578 return GenResponse{}, ers 579 } 580 res.Schema = &schema 581 } 582 return res, nil 583 } 584 585 func (b *codeGenOpBuilder) MakeHeader(receiver, name string, hdr spec.Header) (GenHeader, error) { 586 tpe := typeForHeader(hdr) //simpleResolvedType(hdr.Type, hdr.Format, hdr.Items) 587 588 id := swag.ToGoName(name) 589 res := GenHeader{ 590 sharedValidations: sharedValidationsFromSimple(hdr.CommonValidations, true), // NOTE: Required is not defined by the Swagger schema for header. Set arbitrarily to true for convenience in templates. 591 resolvedType: tpe, 592 Package: b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, defaultOperationsTarget), 593 ReceiverName: receiver, 594 ID: id, 595 Name: name, 596 Path: fmt.Sprintf("%q", name), 597 ValueExpression: fmt.Sprintf("%s.%s", receiver, id), 598 Description: trimBOM(hdr.Description), 599 Default: hdr.Default, 600 HasDefault: hdr.Default != nil, 601 Converter: stringConverters[tpe.GoType], 602 Formatter: stringFormatters[tpe.GoType], 603 ZeroValue: tpe.Zero(), 604 CollectionFormat: hdr.CollectionFormat, 605 IndexVar: "i", 606 } 607 res.HasValidations, res.HasSliceValidations = b.HasValidations(hdr.CommonValidations, res.resolvedType) 608 609 hasChildValidations := false 610 if hdr.Items != nil { 611 pi, err := b.MakeHeaderItem(receiver, name+" "+res.IndexVar, res.IndexVar+"i", "fmt.Sprintf(\"%s.%v\", \"header\", "+res.IndexVar+")", res.Name+"I", hdr.Items, nil) 612 if err != nil { 613 return GenHeader{}, err 614 } 615 res.Child = &pi 616 hasChildValidations = pi.HasValidations 617 } 618 // we feed the GenHeader structure the same way as we do for 619 // GenParameter, even though there is currently no actual validation 620 // for response headers. 621 res.HasValidations = res.HasValidations || hasChildValidations 622 623 return res, nil 624 } 625 626 func (b *codeGenOpBuilder) MakeHeaderItem(receiver, paramName, indexVar, path, valueExpression string, items, parent *spec.Items) (GenItems, error) { 627 var res GenItems 628 res.resolvedType = simpleResolvedType(items.Type, items.Format, items.Items) 629 res.sharedValidations = sharedValidationsFromSimple(items.CommonValidations, false) 630 res.Name = paramName 631 res.Path = path 632 res.Location = "header" 633 res.ValueExpression = swag.ToVarName(valueExpression) 634 res.CollectionFormat = items.CollectionFormat 635 res.Converter = stringConverters[res.GoType] 636 res.Formatter = stringFormatters[res.GoType] 637 res.IndexVar = indexVar 638 res.HasValidations, res.HasSliceValidations = b.HasValidations(items.CommonValidations, res.resolvedType) 639 res.IsEnumCI = b.GenOpts.AllowEnumCI || hasEnumCI(items.Extensions) 640 641 if items.Items != nil { 642 // Recursively follows nested arrays 643 // IMPORTANT! transmitting a ValueExpression consistent with the parent's one 644 hi, err := b.MakeHeaderItem(receiver, paramName+" "+indexVar, indexVar+"i", "fmt.Sprintf(\"%s.%v\", \"header\", "+indexVar+")", res.ValueExpression+"I", items.Items, items) 645 if err != nil { 646 return GenItems{}, err 647 } 648 res.Child = &hi 649 hi.Parent = &res 650 // Propagates HasValidations flag to outer Items definition (currently not in use: done to remain consistent with parameters) 651 res.HasValidations = res.HasValidations || hi.HasValidations 652 } 653 654 return res, nil 655 } 656 657 // HasValidations resolves the validation status for simple schema objects 658 func (b *codeGenOpBuilder) HasValidations(sh spec.CommonValidations, rt resolvedType) (hasValidations bool, hasSliceValidations bool) { 659 hasNumberValidation := sh.Maximum != nil || sh.Minimum != nil || sh.MultipleOf != nil 660 hasStringValidation := sh.MaxLength != nil || sh.MinLength != nil || sh.Pattern != "" 661 hasSliceValidations = sh.MaxItems != nil || sh.MinItems != nil || sh.UniqueItems || len(sh.Enum) > 0 662 hasValidations = hasNumberValidation || hasStringValidation || hasSliceValidations || hasFormatValidation(rt) 663 return 664 } 665 666 func (b *codeGenOpBuilder) MakeParameterItem(receiver, paramName, indexVar, path, valueExpression, location string, resolver *typeResolver, items, parent *spec.Items) (GenItems, error) { 667 debugLog("making parameter item recv=%s param=%s index=%s valueExpr=%s path=%s location=%s", receiver, paramName, indexVar, valueExpression, path, location) 668 var res GenItems 669 res.resolvedType = simpleResolvedType(items.Type, items.Format, items.Items) 670 res.sharedValidations = sharedValidationsFromSimple(items.CommonValidations, false) 671 res.Name = paramName 672 res.Path = path 673 res.Location = location 674 res.ValueExpression = swag.ToVarName(valueExpression) 675 res.CollectionFormat = items.CollectionFormat 676 res.Converter = stringConverters[res.GoType] 677 res.Formatter = stringFormatters[res.GoType] 678 res.IndexVar = indexVar 679 680 res.HasValidations, res.HasSliceValidations = b.HasValidations(items.CommonValidations, res.resolvedType) 681 res.IsEnumCI = b.GenOpts.AllowEnumCI || hasEnumCI(items.Extensions) 682 res.NeedsIndex = res.HasValidations || res.Converter != "" || (res.IsCustomFormatter && !res.SkipParse) 683 684 if items.Items != nil { 685 // Recursively follows nested arrays 686 // IMPORTANT! transmitting a ValueExpression consistent with the parent's one 687 pi, err := b.MakeParameterItem(receiver, paramName+" "+indexVar, indexVar+"i", "fmt.Sprintf(\"%s.%v\", "+path+", "+indexVar+")", res.ValueExpression+"I", location, resolver, items.Items, items) 688 if err != nil { 689 return GenItems{}, err 690 } 691 res.Child = &pi 692 pi.Parent = &res 693 // Propagates HasValidations flag to outer Items definition 694 res.HasValidations = res.HasValidations || pi.HasValidations 695 res.NeedsIndex = res.NeedsIndex || pi.NeedsIndex 696 } 697 698 return res, nil 699 } 700 701 func (b *codeGenOpBuilder) MakeParameter(receiver string, resolver *typeResolver, param spec.Parameter, idMapping map[string]map[string]string) (GenParameter, error) { 702 debugLog("[%s %s] making parameter %q", b.Method, b.Path, param.Name) 703 704 // assume minimal flattening has been carried on, so there is not $ref in response (but some may remain in response schema) 705 706 var child *GenItems 707 id := swag.ToGoName(param.Name) 708 if goName, ok := param.Extensions["x-go-name"]; ok { 709 id, ok = goName.(string) 710 if !ok { 711 return GenParameter{}, fmt.Errorf(`%s %s, parameter %q: "x-go-name" field must be a string, not a %T`, 712 b.Method, b.Path, param.Name, goName) 713 } 714 } else if len(idMapping) > 0 { 715 id = idMapping[param.In][param.Name] 716 } 717 718 res := GenParameter{ 719 ID: id, 720 Name: param.Name, 721 ModelsPackage: b.ModelsPackage, 722 Path: fmt.Sprintf("%q", param.Name), 723 ValueExpression: fmt.Sprintf("%s.%s", receiver, id), 724 IndexVar: "i", 725 Default: param.Default, 726 HasDefault: param.Default != nil, 727 Description: trimBOM(param.Description), 728 ReceiverName: receiver, 729 CollectionFormat: param.CollectionFormat, 730 Child: child, 731 Location: param.In, 732 AllowEmptyValue: (param.In == "query" || param.In == "formData") && param.AllowEmptyValue, 733 Extensions: param.Extensions, 734 } 735 736 if param.In == "body" { 737 // Process parameters declared in body (i.e. have a Schema) 738 res.Required = param.Required 739 if err := b.MakeBodyParameter(&res, resolver, param.Schema); err != nil { 740 return GenParameter{}, err 741 } 742 } else { 743 // Process parameters declared in other inputs: path, query, header (SimpleSchema) 744 res.resolvedType = simpleResolvedType(param.Type, param.Format, param.Items) 745 res.sharedValidations = sharedValidationsFromSimple(param.CommonValidations, param.Required) 746 747 res.ZeroValue = res.resolvedType.Zero() 748 749 hasChildValidations := false 750 if param.Items != nil { 751 // Follow Items definition for array parameters 752 pi, err := b.MakeParameterItem(receiver, param.Name+" "+res.IndexVar, res.IndexVar+"i", "fmt.Sprintf(\"%s.%v\", "+res.Path+", "+res.IndexVar+")", res.Name+"I", param.In, resolver, param.Items, nil) 753 if err != nil { 754 return GenParameter{}, err 755 } 756 res.Child = &pi 757 // Propagates HasValidations from from child array 758 hasChildValidations = pi.HasValidations 759 } 760 res.IsNullable = !param.Required && !param.AllowEmptyValue 761 res.HasValidations, res.HasSliceValidations = b.HasValidations(param.CommonValidations, res.resolvedType) 762 res.HasValidations = res.HasValidations || hasChildValidations 763 res.IsEnumCI = b.GenOpts.AllowEnumCI || hasEnumCI(param.Extensions) 764 } 765 766 // Select codegen strategy for body param validation 767 res.Converter = stringConverters[res.GoType] 768 res.Formatter = stringFormatters[res.GoType] 769 b.setBodyParamValidation(&res) 770 771 return res, nil 772 } 773 774 // MakeBodyParameter constructs a body parameter schema 775 func (b *codeGenOpBuilder) MakeBodyParameter(res *GenParameter, resolver *typeResolver, sch *spec.Schema) error { 776 // resolve schema model 777 schema, ers := b.buildOperationSchema(res.Path, b.Operation.ID+"ParamsBody", swag.ToGoName(b.Operation.ID+" Body"), res.ReceiverName, res.IndexVar, sch, resolver) 778 if ers != nil { 779 return ers 780 } 781 res.Schema = &schema 782 res.Schema.Required = res.Required // Required in body is managed independently from validations 783 784 // build Child items for nested slices and maps 785 var items *GenItems 786 res.KeyVar = "k" 787 res.Schema.KeyVar = "k" 788 switch { 789 case schema.IsMap && !schema.IsInterface: 790 items = b.MakeBodyParameterItemsAndMaps(res, res.Schema.AdditionalProperties) 791 case schema.IsArray: 792 items = b.MakeBodyParameterItemsAndMaps(res, res.Schema.Items) 793 default: 794 items = new(GenItems) 795 } 796 797 // templates assume at least one .Child != nil 798 res.Child = items 799 schema.HasValidations = schema.HasValidations || items.HasValidations 800 801 res.resolvedType = schema.resolvedType 802 803 // simple and schema views share the same validations 804 res.sharedValidations = schema.sharedValidations 805 res.ZeroValue = schema.Zero() 806 return nil 807 } 808 809 // MakeBodyParameterItemsAndMaps clones the .Items schema structure (resp. .AdditionalProperties) as a .GenItems structure 810 // for compatibility with simple param templates. 811 // 812 // Constructed children assume simple structures: any complex object is assumed to be resolved by a model or extra schema definition 813 func (b *codeGenOpBuilder) MakeBodyParameterItemsAndMaps(res *GenParameter, it *GenSchema) *GenItems { 814 items := new(GenItems) 815 if it != nil { 816 var prev *GenItems 817 next := items 818 if res.Schema.IsArray { 819 next.Path = "fmt.Sprintf(\"%s.%v\", " + res.Path + ", " + res.IndexVar + ")" 820 } else if res.Schema.IsMap { 821 next.Path = "fmt.Sprintf(\"%s.%v\", " + res.Path + ", " + res.KeyVar + ")" 822 } 823 next.Name = res.Name + " " + res.Schema.IndexVar 824 next.IndexVar = res.Schema.IndexVar + "i" 825 next.KeyVar = res.Schema.KeyVar + "k" 826 next.ValueExpression = swag.ToVarName(res.Name + "I") 827 next.Location = "body" 828 for it != nil { 829 next.resolvedType = it.resolvedType 830 next.sharedValidations = it.sharedValidations 831 next.Formatter = stringFormatters[it.SwaggerFormat] 832 next.Converter = stringConverters[res.GoType] 833 next.Parent = prev 834 _, next.IsCustomFormatter = customFormatters[it.GoType] 835 next.IsCustomFormatter = next.IsCustomFormatter && !it.IsStream 836 837 // special instruction to avoid using CollectionFormat for body params 838 next.SkipParse = true 839 840 if prev != nil { 841 if prev.IsArray { 842 next.Path = "fmt.Sprintf(\"%s.%v\", " + prev.Path + ", " + prev.IndexVar + ")" 843 } else if prev.IsMap { 844 next.Path = "fmt.Sprintf(\"%s.%v\", " + prev.Path + ", " + prev.KeyVar + ")" 845 } 846 next.Name = prev.Name + prev.IndexVar 847 next.IndexVar = prev.IndexVar + "i" 848 next.KeyVar = prev.KeyVar + "k" 849 next.ValueExpression = swag.ToVarName(prev.ValueExpression + "I") 850 prev.Child = next 851 } 852 853 // found a complex or aliased thing 854 // hide details from the aliased type and stop recursing 855 if next.IsAliased || next.IsComplexObject { 856 next.IsArray = false 857 next.IsMap = false 858 next.IsCustomFormatter = false 859 next.IsComplexObject = true 860 next.IsAliased = true 861 break 862 } 863 if next.IsInterface || next.IsStream || next.IsBase64 { 864 next.HasValidations = false 865 } 866 next.NeedsIndex = next.HasValidations || next.Converter != "" || (next.IsCustomFormatter && !next.SkipParse) 867 prev = next 868 next = new(GenItems) 869 870 switch { 871 case it.Items != nil: 872 it = it.Items 873 case it.AdditionalProperties != nil: 874 it = it.AdditionalProperties 875 default: 876 it = nil 877 } 878 } 879 // propagate HasValidations 880 var propag func(child *GenItems) (bool, bool) 881 propag = func(child *GenItems) (bool, bool) { 882 if child == nil { 883 return false, false 884 } 885 cValidations, cIndex := propag(child.Child) 886 child.HasValidations = child.HasValidations || cValidations 887 child.NeedsIndex = child.HasValidations || child.Converter != "" || (child.IsCustomFormatter && !child.SkipParse) || cIndex 888 return child.HasValidations, child.NeedsIndex 889 } 890 items.HasValidations, items.NeedsIndex = propag(items) 891 892 // resolve nullability conflicts when declaring body as a map of array of an anonymous complex object 893 // (e.g. refer to an extra schema type, which is nullable, but not rendered as a pointer in arrays or maps) 894 // Rule: outer type rules (with IsMapNullOverride), inner types are fixed 895 var fixNullable func(child *GenItems) string 896 fixNullable = func(child *GenItems) string { 897 if !child.IsArray && !child.IsMap { 898 if child.IsComplexObject { 899 return child.GoType 900 } 901 return "" 902 } 903 if innerType := fixNullable(child.Child); innerType != "" { 904 if child.IsMapNullOverride && child.IsArray { 905 child.GoType = "[]" + innerType 906 return child.GoType 907 } 908 } 909 return "" 910 } 911 fixNullable(items) 912 } 913 return items 914 } 915 916 func (b *codeGenOpBuilder) setBodyParamValidation(p *GenParameter) { 917 // Determine validation strategy for body param. 918 // 919 // Here are the distinct strategies: 920 // - the body parameter is a model object => delegates 921 // - the body parameter is an array of model objects => carry on slice validations, then iterate and delegate 922 // - the body parameter is a map of model objects => iterate and delegate 923 // - the body parameter is an array of simple objects (including maps) 924 // - the body parameter is a map of simple objects (including arrays) 925 if p.IsBodyParam() { 926 var hasSimpleBodyParams, hasSimpleBodyItems, hasSimpleBodyMap, hasModelBodyParams, hasModelBodyItems, hasModelBodyMap bool 927 s := p.Schema 928 if s != nil { 929 doNot := s.IsInterface || s.IsStream || s.IsBase64 930 // composition of primitive fields must be properly identified: hack this through 931 _, isPrimitive := primitives[s.GoType] 932 _, isFormatter := customFormatters[s.GoType] 933 isComposedPrimitive := s.IsPrimitive && !(isPrimitive || isFormatter) 934 935 hasSimpleBodyParams = !s.IsComplexObject && !s.IsAliased && !isComposedPrimitive && !doNot 936 hasModelBodyParams = (s.IsComplexObject || s.IsAliased || isComposedPrimitive) && !doNot 937 938 if s.IsArray && s.Items != nil { 939 it := s.Items 940 doNot = it.IsInterface || it.IsStream || it.IsBase64 941 hasSimpleBodyItems = !it.IsComplexObject && !(it.IsAliased || doNot) 942 hasModelBodyItems = (it.IsComplexObject || it.IsAliased) && !doNot 943 } 944 if s.IsMap && s.AdditionalProperties != nil { 945 it := s.AdditionalProperties 946 hasSimpleBodyMap = !it.IsComplexObject && !(it.IsAliased || doNot) 947 hasModelBodyMap = !hasSimpleBodyMap && !doNot 948 } 949 } 950 // set validation strategy for body param 951 p.HasSimpleBodyParams = hasSimpleBodyParams 952 p.HasSimpleBodyItems = hasSimpleBodyItems 953 p.HasModelBodyParams = hasModelBodyParams 954 p.HasModelBodyItems = hasModelBodyItems 955 p.HasModelBodyMap = hasModelBodyMap 956 p.HasSimpleBodyMap = hasSimpleBodyMap 957 } 958 959 } 960 961 // makeSecuritySchemes produces a sorted list of security schemes for this operation 962 func (b *codeGenOpBuilder) makeSecuritySchemes(receiver string) GenSecuritySchemes { 963 return gatherSecuritySchemes(b.SecurityDefinitions, b.Name, b.Principal, receiver) 964 } 965 966 // makeSecurityRequirements produces a sorted list of security requirements for this operation. 967 // As for current, these requirements are not used by codegen (sec. requirement is determined at runtime). 968 // We keep the order of the slice from the original spec, but sort the inner slice which comes from a map, 969 // as well as the map of scopes. 970 func (b *codeGenOpBuilder) makeSecurityRequirements(receiver string) []GenSecurityRequirements { 971 if b.Security == nil { 972 // nil (default requirement) is different than [] (no requirement) 973 return nil 974 } 975 976 securityRequirements := make([]GenSecurityRequirements, 0, len(b.Security)) 977 for _, req := range b.Security { 978 jointReq := make(GenSecurityRequirements, 0, len(req)) 979 for _, j := range req { 980 scopes := j.Scopes 981 sort.Strings(scopes) 982 jointReq = append(jointReq, GenSecurityRequirement{ 983 Name: j.Name, 984 Scopes: scopes, 985 }) 986 } 987 // sort joint requirements (come from a map in spec) 988 sort.Sort(jointReq) 989 securityRequirements = append(securityRequirements, jointReq) 990 } 991 return securityRequirements 992 } 993 994 // cloneSchema returns a deep copy of a schema 995 func (b *codeGenOpBuilder) cloneSchema(schema *spec.Schema) *spec.Schema { 996 savedSchema := &spec.Schema{} 997 schemaRep, _ := json.Marshal(schema) 998 _ = json.Unmarshal(schemaRep, savedSchema) 999 return savedSchema 1000 } 1001 1002 // saveResolveContext keeps a copy of known definitions and schema to properly roll back on a makeGenSchema() call 1003 // This uses a deep clone the spec document to construct a type resolver which knows about definitions when the making of this operation started, 1004 // and only these definitions. We are not interested in the "original spec", but in the already transformed spec. 1005 func (b *codeGenOpBuilder) saveResolveContext(resolver *typeResolver, schema *spec.Schema) (*typeResolver, *spec.Schema) { 1006 if b.PristineDoc == nil { 1007 b.PristineDoc = b.Doc.Pristine() 1008 } 1009 rslv := newTypeResolver(b.GenOpts.LanguageOpts.ManglePackageName(resolver.ModelsPackage, defaultModelsTarget), b.PristineDoc) 1010 1011 return rslv, b.cloneSchema(schema) 1012 } 1013 1014 // liftExtraSchemas constructs the schema for an anonymous construct with some ExtraSchemas. 1015 // 1016 // When some ExtraSchemas are produced from something else than a definition, 1017 // this indicates we are not running in fully flattened mode and we need to render 1018 // these ExtraSchemas in the operation's package. 1019 // We need to rebuild the schema with a new type resolver to reflect this change in the 1020 // models package. 1021 func (b *codeGenOpBuilder) liftExtraSchemas(resolver, rslv *typeResolver, bs *spec.Schema, sc *schemaGenContext) (schema *GenSchema, err error) { 1022 // restore resolving state before previous call to makeGenSchema() 1023 sc.Schema = *bs 1024 1025 pg := sc.shallowClone() 1026 pkg := b.GenOpts.LanguageOpts.ManglePackageName(resolver.ModelsPackage, defaultModelsTarget) 1027 1028 // make a resolver for current package (i.e. operations) 1029 pg.TypeResolver = newTypeResolver("", rslv.Doc).withKeepDefinitionsPackage(pkg) 1030 pg.ExtraSchemas = make(map[string]GenSchema, len(sc.ExtraSchemas)) 1031 pg.UseContainerInName = true 1032 1033 // rebuild schema within local package 1034 if err = pg.makeGenSchema(); err != nil { 1035 return 1036 } 1037 1038 // lift nested extra schemas (inlined types) 1039 if b.ExtraSchemas == nil { 1040 b.ExtraSchemas = make(map[string]GenSchema, len(pg.ExtraSchemas)) 1041 } 1042 for _, v := range pg.ExtraSchemas { 1043 vv := v 1044 if !v.IsStream { 1045 b.ExtraSchemas[vv.Name] = vv 1046 } 1047 } 1048 schema = &pg.GenSchema 1049 return 1050 } 1051 1052 // buildOperationSchema constructs a schema for an operation (for body params or responses). 1053 // It determines if the schema is readily available from the models package, 1054 // or if a schema has to be generated in the operations package (i.e. is anonymous). 1055 // Whenever an anonymous schema needs some extra schemas, we also determine if these extras are 1056 // available from models or must be generated alongside the schema in the operations package. 1057 // 1058 // Duplicate extra schemas are pruned later on, when operations grouping in packages (e.g. from tags) takes place. 1059 func (b *codeGenOpBuilder) buildOperationSchema(schemaPath, containerName, schemaName, receiverName, indexVar string, sch *spec.Schema, resolver *typeResolver) (GenSchema, error) { 1060 var schema GenSchema 1061 1062 if sch == nil { 1063 sch = &spec.Schema{} 1064 } 1065 rslv := resolver 1066 sc := schemaGenContext{ 1067 Path: schemaPath, 1068 Name: containerName, 1069 Receiver: receiverName, 1070 ValueExpr: receiverName, 1071 IndexVar: indexVar, 1072 Schema: *sch, 1073 Required: false, 1074 TypeResolver: rslv, 1075 Named: false, 1076 IncludeModel: true, 1077 IncludeValidator: b.GenOpts.IncludeValidator, 1078 StrictAdditionalProperties: b.GenOpts.StrictAdditionalProperties, 1079 ExtraSchemas: make(map[string]GenSchema), 1080 StructTags: b.GenOpts.StructTags, 1081 } 1082 1083 var ( 1084 br *typeResolver 1085 bs *spec.Schema 1086 ) 1087 1088 if sch.Ref.String() == "" { 1089 // backup the type resolver context 1090 // (not needed when the schema has a name) 1091 br, bs = b.saveResolveContext(rslv, sch) 1092 } 1093 1094 if err := sc.makeGenSchema(); err != nil { 1095 return GenSchema{}, err 1096 } 1097 for alias, pkg := range findImports(&sc.GenSchema) { 1098 b.Imports[alias] = pkg 1099 } 1100 1101 if sch.Ref.String() == "" && len(sc.ExtraSchemas) > 0 { 1102 newSchema, err := b.liftExtraSchemas(resolver, br, bs, &sc) 1103 if err != nil { 1104 return GenSchema{}, err 1105 } 1106 if newSchema != nil { 1107 schema = *newSchema 1108 } 1109 } else { 1110 schema = sc.GenSchema 1111 } 1112 1113 if schema.IsAnonymous { 1114 // a generated name for anonymous schema 1115 // TODO: support x-go-name 1116 hasProperties := len(schema.Properties) > 0 1117 isAllOf := len(schema.AllOf) > 0 1118 isInterface := schema.IsInterface 1119 hasValidations := schema.HasValidations 1120 1121 // for complex anonymous objects, produce an extra schema 1122 if hasProperties || isAllOf { 1123 if b.ExtraSchemas == nil { 1124 b.ExtraSchemas = make(map[string]GenSchema) 1125 } 1126 schema.Name = schemaName 1127 schema.GoType = schemaName 1128 schema.IsAnonymous = false 1129 b.ExtraSchemas[schemaName] = schema 1130 1131 // constructs new schema to refer to the newly created type 1132 schema = GenSchema{} 1133 schema.IsAnonymous = false 1134 schema.IsComplexObject = true 1135 schema.SwaggerType = schemaName 1136 schema.HasValidations = hasValidations 1137 schema.GoType = schemaName 1138 } else if isInterface { 1139 schema = GenSchema{} 1140 schema.IsAnonymous = false 1141 schema.IsComplexObject = false 1142 schema.IsInterface = true 1143 schema.HasValidations = false 1144 schema.GoType = iface 1145 } 1146 } 1147 return schema, nil 1148 } 1149 1150 func intersectTags(left, right []string) []string { 1151 // dedupe 1152 uniqueTags := make(map[string]struct{}, maxInt(len(left), len(right))) 1153 for _, l := range left { 1154 if len(right) == 0 || swag.ContainsStrings(right, l) { 1155 uniqueTags[l] = struct{}{} 1156 } 1157 } 1158 filtered := make([]string, 0, len(uniqueTags)) 1159 // stable output across generations, preserving original order 1160 for _, k := range left { 1161 if _, ok := uniqueTags[k]; !ok { 1162 continue 1163 } 1164 filtered = append(filtered, k) 1165 delete(uniqueTags, k) 1166 } 1167 return filtered 1168 } 1169 1170 // analyze tags for an operation 1171 func (b *codeGenOpBuilder) analyzeTags() (string, []string, bool) { 1172 var ( 1173 filter []string 1174 tag string 1175 hasTagOverride bool 1176 ) 1177 if b.GenOpts != nil { 1178 filter = b.GenOpts.Tags 1179 } 1180 intersected := intersectTags(pruneEmpty(b.Operation.Tags), filter) 1181 if !b.GenOpts.SkipTagPackages && len(intersected) > 0 { 1182 // override generation with: x-go-operation-tag 1183 tag, hasTagOverride = b.Operation.Extensions.GetString(xGoOperationTag) 1184 if !hasTagOverride { 1185 // TODO(fred): this part should be delegated to some new TagsFor(operation) in go-openapi/analysis 1186 tag = intersected[0] 1187 gtags := b.Doc.Spec().Tags 1188 for _, gtag := range gtags { 1189 if gtag.Name != tag { 1190 continue 1191 } 1192 // honor x-go-name in tag 1193 if name, hasGoName := gtag.Extensions.GetString(xGoName); hasGoName { 1194 tag = name 1195 break 1196 } 1197 // honor x-go-operation-tag in tag 1198 if name, hasOpName := gtag.Extensions.GetString(xGoOperationTag); hasOpName { 1199 tag = name 1200 break 1201 } 1202 } 1203 } 1204 } 1205 if tag == b.APIPackage { 1206 // confict with "operations" package is handled separately 1207 tag = renameOperationPackage(intersected, tag) 1208 } 1209 b.APIPackage = b.GenOpts.LanguageOpts.ManglePackageName(tag, b.APIPackage) // actual package name 1210 b.APIPackageAlias = deconflictTag(intersected, b.APIPackage) // deconflicted import alias 1211 return tag, intersected, len(filter) == 0 || len(filter) > 0 && len(intersected) > 0 1212 } 1213 1214 func maxInt(a, b int) int { 1215 if a > b { 1216 return a 1217 } 1218 return b 1219 } 1220 1221 // deconflictTag ensures generated packages for operations based on tags do not conflict 1222 // with other imports 1223 func deconflictTag(seenTags []string, pkg string) string { 1224 return deconflictPkg(pkg, func(pkg string) string { return renameOperationPackage(seenTags, pkg) }) 1225 } 1226 1227 // deconflictPrincipal ensures that whenever an external principal package is added, it doesn't conflict 1228 // with standard inports 1229 func deconflictPrincipal(pkg string) string { 1230 switch pkg { 1231 case "principal": 1232 return renamePrincipalPackage(pkg) 1233 default: 1234 return deconflictPkg(pkg, renamePrincipalPackage) 1235 } 1236 } 1237 1238 // deconflictPkg renames package names which conflict with standard imports 1239 func deconflictPkg(pkg string, renamer func(string) string) string { 1240 switch pkg { 1241 case "api", "httptransport", "formats": 1242 fallthrough 1243 case "errors", "runtime", "middleware", "security", "spec", "strfmt", "loads", "swag", "validate": 1244 fallthrough 1245 case "tls", "http", "fmt", "strings", "log": 1246 return renamer(pkg) 1247 } 1248 return pkg 1249 } 1250 1251 func renameOperationPackage(seenTags []string, pkg string) string { 1252 current := strings.ToLower(pkg) + "ops" 1253 if len(seenTags) == 0 { 1254 return current 1255 } 1256 for swag.ContainsStringsCI(seenTags, current) { 1257 current += "1" 1258 } 1259 return current 1260 } 1261 1262 func renamePrincipalPackage(pkg string) string { 1263 return "auth" 1264 }