github.com/6543-forks/go-swagger@v0.26.0/generator/operation.go (about)

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