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