github.com/AngusLu/go-swagger@v0.28.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 _, 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  
   210  type 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
   240  func 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.
   275  func 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  
   303  func (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  
   516  func 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  
   526  func schemeOrDefault(schemes []string, defaultScheme string) []string {
   527  	if len(schemes) == 0 {
   528  		return []string{defaultScheme}
   529  	}
   530  	return schemes
   531  }
   532  
   533  func (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  
   582  func (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  
   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, &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
   662  func (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  
   668  func (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  
   707  func (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
   784  func (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
   822  func (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  
   925  func (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
   971  func (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.
   979  func (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
  1004  func (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.
  1014  func (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.
  1030  func (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).
  1039  		withKeepDefinitionsPackage(pkg).
  1040  		withDefinitionPackage(b.APIPackageAlias) // all new extra schemas are going to be in api pkg
  1041  	pg.ExtraSchemas = make(map[string]GenSchema, len(sc.ExtraSchemas))
  1042  	pg.UseContainerInName = true
  1043  
  1044  	// rebuild schema within local package
  1045  	if err = pg.makeGenSchema(); err != nil {
  1046  		return
  1047  	}
  1048  
  1049  	// lift nested extra schemas (inlined types)
  1050  	if b.ExtraSchemas == nil {
  1051  		b.ExtraSchemas = make(map[string]GenSchema, len(pg.ExtraSchemas))
  1052  	}
  1053  	for _, v := range pg.ExtraSchemas {
  1054  		vv := v
  1055  		if !v.IsStream {
  1056  			b.ExtraSchemas[vv.Name] = vv
  1057  		}
  1058  	}
  1059  	schema = &pg.GenSchema
  1060  	return
  1061  }
  1062  
  1063  // buildOperationSchema constructs a schema for an operation (for body params or responses).
  1064  // It determines if the schema is readily available from the models package,
  1065  // or if a schema has to be generated in the operations package (i.e. is anonymous).
  1066  // Whenever an anonymous schema needs some extra schemas, we also determine if these extras are
  1067  // available from models or must be generated alongside the schema in the operations package.
  1068  //
  1069  // Duplicate extra schemas are pruned later on, when operations grouping in packages (e.g. from tags) takes place.
  1070  func (b *codeGenOpBuilder) buildOperationSchema(schemaPath, containerName, schemaName, receiverName, indexVar string, sch *spec.Schema, resolver *typeResolver) (GenSchema, error) {
  1071  	var schema GenSchema
  1072  
  1073  	if sch == nil {
  1074  		sch = &spec.Schema{}
  1075  	}
  1076  	shallowClonedResolver := *resolver
  1077  	shallowClonedResolver.ModelsFullPkg = b.DefaultImports[b.ModelsPackage]
  1078  	rslv := &shallowClonedResolver
  1079  
  1080  	sc := schemaGenContext{
  1081  		Path:                       schemaPath,
  1082  		Name:                       containerName,
  1083  		Receiver:                   receiverName,
  1084  		ValueExpr:                  receiverName,
  1085  		IndexVar:                   indexVar,
  1086  		Schema:                     *sch,
  1087  		Required:                   false,
  1088  		TypeResolver:               rslv,
  1089  		Named:                      false,
  1090  		IncludeModel:               true,
  1091  		IncludeValidator:           b.GenOpts.IncludeValidator,
  1092  		StrictAdditionalProperties: b.GenOpts.StrictAdditionalProperties,
  1093  		ExtraSchemas:               make(map[string]GenSchema),
  1094  		StructTags:                 b.GenOpts.StructTags,
  1095  	}
  1096  
  1097  	var (
  1098  		br *typeResolver
  1099  		bs *spec.Schema
  1100  	)
  1101  
  1102  	if sch.Ref.String() == "" {
  1103  		// backup the type resolver context
  1104  		// (not needed when the schema has a name)
  1105  		br, bs = b.saveResolveContext(rslv, sch)
  1106  	}
  1107  
  1108  	if err := sc.makeGenSchema(); err != nil {
  1109  		return GenSchema{}, err
  1110  	}
  1111  	for alias, pkg := range findImports(&sc.GenSchema) {
  1112  		b.Imports[alias] = pkg
  1113  	}
  1114  
  1115  	if sch.Ref.String() == "" && len(sc.ExtraSchemas) > 0 {
  1116  		newSchema, err := b.liftExtraSchemas(resolver, br, bs, &sc)
  1117  		if err != nil {
  1118  			return GenSchema{}, err
  1119  		}
  1120  		if newSchema != nil {
  1121  			schema = *newSchema
  1122  		}
  1123  	} else {
  1124  		schema = sc.GenSchema
  1125  	}
  1126  
  1127  	// new schemas will be in api pkg
  1128  	schemaPkg := b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, "")
  1129  	schema.Pkg = schemaPkg
  1130  
  1131  	if schema.IsAnonymous {
  1132  		// a generated name for anonymous schema
  1133  		// TODO: support x-go-name
  1134  		hasProperties := len(schema.Properties) > 0
  1135  		isAllOf := len(schema.AllOf) > 0
  1136  		isInterface := schema.IsInterface
  1137  		hasValidations := schema.HasValidations
  1138  
  1139  		// for complex anonymous objects, produce an extra schema
  1140  		if hasProperties || isAllOf {
  1141  			if b.ExtraSchemas == nil {
  1142  				b.ExtraSchemas = make(map[string]GenSchema)
  1143  			}
  1144  			schema.Name = schemaName
  1145  			schema.GoType = schemaName
  1146  			schema.IsAnonymous = false
  1147  			b.ExtraSchemas[schemaName] = schema
  1148  
  1149  			// constructs new schema to refer to the newly created type
  1150  			schema = GenSchema{}
  1151  			schema.IsAnonymous = false
  1152  			schema.IsComplexObject = true
  1153  			schema.SwaggerType = schemaName
  1154  			schema.HasValidations = hasValidations
  1155  			schema.GoType = schemaName
  1156  			schema.Pkg = schemaPkg
  1157  		} else if isInterface {
  1158  			schema = GenSchema{}
  1159  			schema.IsAnonymous = false
  1160  			schema.IsComplexObject = false
  1161  			schema.IsInterface = true
  1162  			schema.HasValidations = false
  1163  			schema.GoType = iface
  1164  		}
  1165  	}
  1166  
  1167  	return schema, nil
  1168  }
  1169  
  1170  func intersectTags(left, right []string) []string {
  1171  	// dedupe
  1172  	uniqueTags := make(map[string]struct{}, maxInt(len(left), len(right)))
  1173  	for _, l := range left {
  1174  		if len(right) == 0 || swag.ContainsStrings(right, l) {
  1175  			uniqueTags[l] = struct{}{}
  1176  		}
  1177  	}
  1178  	filtered := make([]string, 0, len(uniqueTags))
  1179  	// stable output across generations, preserving original order
  1180  	for _, k := range left {
  1181  		if _, ok := uniqueTags[k]; !ok {
  1182  			continue
  1183  		}
  1184  		filtered = append(filtered, k)
  1185  		delete(uniqueTags, k)
  1186  	}
  1187  	return filtered
  1188  }
  1189  
  1190  // analyze tags for an operation
  1191  func (b *codeGenOpBuilder) analyzeTags() (string, []string, bool) {
  1192  	var (
  1193  		filter         []string
  1194  		tag            string
  1195  		hasTagOverride bool
  1196  	)
  1197  	if b.GenOpts != nil {
  1198  		filter = b.GenOpts.Tags
  1199  	}
  1200  	intersected := intersectTags(pruneEmpty(b.Operation.Tags), filter)
  1201  	if !b.GenOpts.SkipTagPackages && len(intersected) > 0 {
  1202  		// override generation with: x-go-operation-tag
  1203  		tag, hasTagOverride = b.Operation.Extensions.GetString(xGoOperationTag)
  1204  		if !hasTagOverride {
  1205  			// TODO(fred): this part should be delegated to some new TagsFor(operation) in go-openapi/analysis
  1206  			tag = intersected[0]
  1207  			gtags := b.Doc.Spec().Tags
  1208  			for _, gtag := range gtags {
  1209  				if gtag.Name != tag {
  1210  					continue
  1211  				}
  1212  				//  honor x-go-name in tag
  1213  				if name, hasGoName := gtag.Extensions.GetString(xGoName); hasGoName {
  1214  					tag = name
  1215  					break
  1216  				}
  1217  				//  honor x-go-operation-tag in tag
  1218  				if name, hasOpName := gtag.Extensions.GetString(xGoOperationTag); hasOpName {
  1219  					tag = name
  1220  					break
  1221  				}
  1222  			}
  1223  		}
  1224  	}
  1225  	if tag == b.APIPackage {
  1226  		// conflict with "operations" package is handled separately
  1227  		tag = renameOperationPackage(intersected, tag)
  1228  	}
  1229  	b.APIPackage = b.GenOpts.LanguageOpts.ManglePackageName(tag, b.APIPackage) // actual package name
  1230  	b.APIPackageAlias = deconflictTag(intersected, b.APIPackage)               // deconflicted import alias
  1231  	return tag, intersected, len(filter) == 0 || len(filter) > 0 && len(intersected) > 0
  1232  }
  1233  
  1234  func maxInt(a, b int) int {
  1235  	if a > b {
  1236  		return a
  1237  	}
  1238  	return b
  1239  }
  1240  
  1241  // deconflictTag ensures generated packages for operations based on tags do not conflict
  1242  // with other imports
  1243  func deconflictTag(seenTags []string, pkg string) string {
  1244  	return deconflictPkg(pkg, func(pkg string) string { return renameOperationPackage(seenTags, pkg) })
  1245  }
  1246  
  1247  // deconflictPrincipal ensures that whenever an external principal package is added, it doesn't conflict
  1248  // with standard inports
  1249  func deconflictPrincipal(pkg string) string {
  1250  	switch pkg {
  1251  	case "principal":
  1252  		return renamePrincipalPackage(pkg)
  1253  	default:
  1254  		return deconflictPkg(pkg, renamePrincipalPackage)
  1255  	}
  1256  }
  1257  
  1258  // deconflictPkg renames package names which conflict with standard imports
  1259  func deconflictPkg(pkg string, renamer func(string) string) string {
  1260  	switch pkg {
  1261  	// package conflict with variables
  1262  	case "api", "httptransport", "formats", "server":
  1263  		fallthrough
  1264  	// package conflict with go-openapi imports
  1265  	case "errors", "runtime", "middleware", "security", "spec", "strfmt", "loads", "swag", "validate":
  1266  		fallthrough
  1267  	// package conflict with stdlib/other lib imports
  1268  	case "tls", "http", "fmt", "strings", "log", "flags", "pflag", "json", "time":
  1269  		return renamer(pkg)
  1270  	}
  1271  	return pkg
  1272  }
  1273  
  1274  func renameOperationPackage(seenTags []string, pkg string) string {
  1275  	current := strings.ToLower(pkg) + "ops"
  1276  	if len(seenTags) == 0 {
  1277  		return current
  1278  	}
  1279  	for swag.ContainsStringsCI(seenTags, current) {
  1280  		current += "1"
  1281  	}
  1282  	return current
  1283  }
  1284  
  1285  func renamePrincipalPackage(pkg string) string {
  1286  	// favors readability over perfect deconfliction
  1287  	return "auth"
  1288  }
  1289  
  1290  func renameServerPackage(pkg string) string {
  1291  	// favors readability over perfect deconfliction
  1292  	return "swagger" + pkg + "srv"
  1293  }
  1294  
  1295  func renameAPIPackage(pkg string) string {
  1296  	// favors readability over perfect deconfliction
  1297  	return "swagger" + pkg
  1298  }
  1299  
  1300  func renameImplementationPackage(pkg string) string {
  1301  	// favors readability over perfect deconfliction
  1302  	return "swagger" + pkg + "impl"
  1303  }