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