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