9fe4437bda
* Use vendored go-swagger * vendor go-swagger * revert un wanteed change * remove un-needed GO111MODULE * Update Makefile Co-Authored-By: techknowlogick <matti@mdranta.net>
1147 lines
38 KiB
Go
1147 lines
38 KiB
Go
// Copyright 2015 go-swagger maintainers
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package generator
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/go-openapi/analysis"
|
|
"github.com/go-openapi/loads"
|
|
"github.com/go-openapi/runtime"
|
|
"github.com/go-openapi/spec"
|
|
"github.com/go-openapi/swag"
|
|
)
|
|
|
|
type respSort struct {
|
|
Code int
|
|
Response spec.Response
|
|
}
|
|
|
|
type responses []respSort
|
|
|
|
func (s responses) Len() int { return len(s) }
|
|
func (s responses) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
func (s responses) Less(i, j int) bool { return s[i].Code < s[j].Code }
|
|
|
|
// sortedResponses produces a sorted list of responses.
|
|
// TODO: this is redundant with the definition given in struct.go
|
|
func sortedResponses(input map[int]spec.Response) responses {
|
|
var res responses
|
|
for k, v := range input {
|
|
if k > 0 {
|
|
res = append(res, respSort{k, v})
|
|
}
|
|
}
|
|
sort.Sort(res)
|
|
return res
|
|
}
|
|
|
|
// GenerateServerOperation generates a parameter model, parameter validator, http handler implementations for a given operation
|
|
// It also generates an operation handler interface that uses the parameter model for handling a valid request.
|
|
// Allows for specifying a list of tags to include only certain tags for the generation
|
|
func GenerateServerOperation(operationNames []string, opts *GenOpts) error {
|
|
if opts == nil {
|
|
return errors.New("gen opts are required")
|
|
}
|
|
templates.LoadDefaults()
|
|
if opts.TemplateDir != "" {
|
|
if err := templates.LoadDir(opts.TemplateDir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := opts.CheckOpts(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Load the spec
|
|
_, specDoc, err := loadSpec(opts.Spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate and Expand. specDoc is in/out param.
|
|
specDoc, err = validateAndFlattenSpec(opts, specDoc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
analyzed := analysis.New(specDoc.Spec())
|
|
|
|
ops := gatherOperations(analyzed, operationNames)
|
|
if len(ops) == 0 {
|
|
return errors.New("no operations were selected")
|
|
}
|
|
|
|
for operationName, opRef := range ops {
|
|
method, path, operation := opRef.Method, opRef.Path, opRef.Op
|
|
defaultScheme := opts.DefaultScheme
|
|
if defaultScheme == "" {
|
|
defaultScheme = sHTTP
|
|
}
|
|
defaultProduces := opts.DefaultProduces
|
|
if defaultProduces == "" {
|
|
defaultProduces = runtime.JSONMime
|
|
}
|
|
defaultConsumes := opts.DefaultConsumes
|
|
if defaultConsumes == "" {
|
|
defaultConsumes = runtime.JSONMime
|
|
}
|
|
|
|
serverPackage := opts.LanguageOpts.ManglePackagePath(opts.ServerPackage, "server")
|
|
generator := operationGenerator{
|
|
Name: operationName,
|
|
Method: method,
|
|
Path: path,
|
|
BasePath: specDoc.BasePath(),
|
|
APIPackage: opts.LanguageOpts.ManglePackagePath(opts.APIPackage, "api"),
|
|
ModelsPackage: opts.LanguageOpts.ManglePackagePath(opts.ModelPackage, "definitions"),
|
|
ClientPackage: opts.LanguageOpts.ManglePackagePath(opts.ClientPackage, "client"),
|
|
ServerPackage: serverPackage,
|
|
Operation: *operation,
|
|
SecurityRequirements: analyzed.SecurityRequirementsFor(operation),
|
|
SecurityDefinitions: analyzed.SecurityDefinitionsFor(operation),
|
|
Principal: opts.Principal,
|
|
Target: filepath.Join(opts.Target, filepath.FromSlash(serverPackage)),
|
|
Base: opts.Target,
|
|
Tags: opts.Tags,
|
|
IncludeHandler: opts.IncludeHandler,
|
|
IncludeParameters: opts.IncludeParameters,
|
|
IncludeResponses: opts.IncludeResponses,
|
|
IncludeValidator: true, // we no more support the CLI option to disable validation
|
|
DumpData: opts.DumpData,
|
|
DefaultScheme: defaultScheme,
|
|
DefaultProduces: defaultProduces,
|
|
DefaultConsumes: defaultConsumes,
|
|
Doc: specDoc,
|
|
Analyzed: analyzed,
|
|
GenOpts: opts,
|
|
}
|
|
if err := generator.Generate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type operationGenerator struct {
|
|
Authorized bool
|
|
IncludeHandler bool
|
|
IncludeParameters bool
|
|
IncludeResponses bool
|
|
IncludeValidator bool
|
|
DumpData bool
|
|
|
|
Principal string
|
|
Target string
|
|
Base string
|
|
Name string
|
|
Method string
|
|
Path string
|
|
BasePath string
|
|
APIPackage string
|
|
ModelsPackage string
|
|
ServerPackage string
|
|
ClientPackage string
|
|
Operation spec.Operation
|
|
SecurityRequirements [][]analysis.SecurityRequirement
|
|
SecurityDefinitions map[string]spec.SecurityScheme
|
|
Tags []string
|
|
DefaultScheme string
|
|
DefaultProduces string
|
|
DefaultConsumes string
|
|
Doc *loads.Document
|
|
Analyzed *analysis.Spec
|
|
GenOpts *GenOpts
|
|
}
|
|
|
|
func intersectTags(left, right []string) (filtered []string) {
|
|
if len(right) == 0 {
|
|
filtered = left
|
|
return
|
|
}
|
|
for _, l := range left {
|
|
if containsString(right, l) {
|
|
filtered = append(filtered, l)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (o *operationGenerator) Generate() error {
|
|
// Build a list of codegen operations based on the tags,
|
|
// the tag decides the actual package for an operation
|
|
// the user specified package serves as root for generating the directory structure
|
|
var operations GenOperations
|
|
authed := len(o.SecurityRequirements) > 0
|
|
|
|
var bldr codeGenOpBuilder
|
|
bldr.Name = o.Name
|
|
bldr.Method = o.Method
|
|
bldr.Path = o.Path
|
|
bldr.BasePath = o.BasePath
|
|
bldr.ModelsPackage = o.ModelsPackage
|
|
bldr.Principal = o.Principal
|
|
bldr.Target = o.Target
|
|
bldr.Operation = o.Operation
|
|
bldr.Authed = authed
|
|
bldr.Security = o.SecurityRequirements
|
|
bldr.SecurityDefinitions = o.SecurityDefinitions
|
|
bldr.Doc = o.Doc
|
|
bldr.Analyzed = o.Analyzed
|
|
bldr.DefaultScheme = o.DefaultScheme
|
|
bldr.DefaultProduces = o.DefaultProduces
|
|
bldr.RootAPIPackage = o.GenOpts.LanguageOpts.ManglePackageName(o.ServerPackage, "server")
|
|
bldr.GenOpts = o.GenOpts
|
|
bldr.DefaultConsumes = o.DefaultConsumes
|
|
bldr.IncludeValidator = o.IncludeValidator
|
|
|
|
bldr.DefaultImports = []string{o.GenOpts.ExistingModels}
|
|
if o.GenOpts.ExistingModels == "" {
|
|
bldr.DefaultImports = []string{
|
|
path.Join(
|
|
filepath.ToSlash(o.GenOpts.LanguageOpts.baseImport(o.Base)),
|
|
o.GenOpts.LanguageOpts.ManglePackagePath(o.ModelsPackage, "")),
|
|
}
|
|
}
|
|
|
|
bldr.APIPackage = o.APIPackage
|
|
st := o.Tags
|
|
if o.GenOpts != nil {
|
|
st = o.GenOpts.Tags
|
|
}
|
|
intersected := intersectTags(o.Operation.Tags, st)
|
|
if len(intersected) > 0 {
|
|
tag := intersected[0]
|
|
bldr.APIPackage = o.GenOpts.LanguageOpts.ManglePackagePath(tag, o.APIPackage)
|
|
}
|
|
op, err := bldr.MakeOperation()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
op.Tags = intersected
|
|
operations = append(operations, op)
|
|
sort.Sort(operations)
|
|
|
|
for _, op := range operations {
|
|
if o.GenOpts.DumpData {
|
|
bb, _ := json.MarshalIndent(swag.ToDynamicJSON(op), "", " ")
|
|
fmt.Fprintln(os.Stdout, string(bb))
|
|
continue
|
|
}
|
|
if err := o.GenOpts.renderOperation(&op); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type codeGenOpBuilder struct {
|
|
Authed bool
|
|
IncludeValidator bool
|
|
|
|
Name string
|
|
Method string
|
|
Path string
|
|
BasePath string
|
|
APIPackage string
|
|
RootAPIPackage string
|
|
ModelsPackage string
|
|
Principal string
|
|
Target string
|
|
Operation spec.Operation
|
|
Doc *loads.Document
|
|
Analyzed *analysis.Spec
|
|
DefaultImports []string
|
|
Imports map[string]string
|
|
DefaultScheme string
|
|
DefaultProduces string
|
|
DefaultConsumes string
|
|
Security [][]analysis.SecurityRequirement
|
|
SecurityDefinitions map[string]spec.SecurityScheme
|
|
ExtraSchemas map[string]GenSchema
|
|
GenOpts *GenOpts
|
|
}
|
|
|
|
// renameTimeout renames the variable in use by client template to avoid conflicting
|
|
// with param names.
|
|
func renameTimeout(seenIds map[string][]string, current string) string {
|
|
var next string
|
|
switch strings.ToLower(current) {
|
|
case "timeout":
|
|
next = "requestTimeout"
|
|
case "requesttimeout":
|
|
next = "httpRequestTimeout"
|
|
case "httptrequesttimeout":
|
|
next = "swaggerTimeout"
|
|
case "swaggertimeout":
|
|
next = "operationTimeout"
|
|
case "operationtimeout":
|
|
next = "opTimeout"
|
|
case "optimeout":
|
|
next = "operTimeout"
|
|
}
|
|
if _, ok := seenIds[next]; ok {
|
|
return renameTimeout(seenIds, next)
|
|
}
|
|
return next
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) MakeOperation() (GenOperation, error) {
|
|
debugLog("[%s %s] parsing operation (id: %q)", b.Method, b.Path, b.Operation.ID)
|
|
// NOTE: we assume flatten is enabled by default (i.e. complex constructs are resolved from the models package),
|
|
// but do not assume the spec is necessarily fully flattened (i.e. all schemas moved to definitions).
|
|
//
|
|
// Fully flattened means that all complex constructs are present as
|
|
// definitions and models produced accordingly in ModelsPackage,
|
|
// whereas minimal flatten simply ensures that there are no weird $ref's in the spec.
|
|
//
|
|
// When some complex anonymous constructs are specified, extra schemas are produced in the operations package.
|
|
//
|
|
// In all cases, resetting definitions to the _original_ (untransformed) spec is not an option:
|
|
// we take from there the spec possibly already transformed by the GenDefinitions stage.
|
|
resolver := newTypeResolver(b.GenOpts.LanguageOpts.ManglePackageName(b.ModelsPackage, "models"), b.Doc)
|
|
receiver := "o"
|
|
|
|
operation := b.Operation
|
|
var params, qp, pp, hp, fp GenParameters
|
|
var hasQueryParams, hasPathParams, hasHeaderParams, hasFormParams, hasFileParams, hasFormValueParams, hasBodyParams bool
|
|
paramsForOperation := b.Analyzed.ParamsFor(b.Method, b.Path)
|
|
timeoutName := "timeout"
|
|
|
|
idMapping := map[string]map[string]string{
|
|
"query": make(map[string]string, len(paramsForOperation)),
|
|
"path": make(map[string]string, len(paramsForOperation)),
|
|
"formData": make(map[string]string, len(paramsForOperation)),
|
|
"header": make(map[string]string, len(paramsForOperation)),
|
|
"body": make(map[string]string, len(paramsForOperation)),
|
|
}
|
|
|
|
seenIds := make(map[string][]string, len(paramsForOperation))
|
|
for id, p := range paramsForOperation {
|
|
if _, ok := seenIds[p.Name]; ok {
|
|
idMapping[p.In][p.Name] = swag.ToGoName(id)
|
|
} else {
|
|
idMapping[p.In][p.Name] = swag.ToGoName(p.Name)
|
|
}
|
|
seenIds[p.Name] = append(seenIds[p.Name], p.In)
|
|
if strings.EqualFold(p.Name, timeoutName) {
|
|
timeoutName = renameTimeout(seenIds, timeoutName)
|
|
}
|
|
}
|
|
|
|
for _, p := range paramsForOperation {
|
|
cp, err := b.MakeParameter(receiver, resolver, p, idMapping)
|
|
|
|
if err != nil {
|
|
return GenOperation{}, err
|
|
}
|
|
if cp.IsQueryParam() {
|
|
hasQueryParams = true
|
|
qp = append(qp, cp)
|
|
}
|
|
if cp.IsFormParam() {
|
|
if p.Type == file {
|
|
hasFileParams = true
|
|
}
|
|
if p.Type != file {
|
|
hasFormValueParams = true
|
|
}
|
|
hasFormParams = true
|
|
fp = append(fp, cp)
|
|
}
|
|
if cp.IsPathParam() {
|
|
hasPathParams = true
|
|
pp = append(pp, cp)
|
|
}
|
|
if cp.IsHeaderParam() {
|
|
hasHeaderParams = true
|
|
hp = append(hp, cp)
|
|
}
|
|
if cp.IsBodyParam() {
|
|
hasBodyParams = true
|
|
}
|
|
params = append(params, cp)
|
|
}
|
|
sort.Sort(params)
|
|
sort.Sort(qp)
|
|
sort.Sort(pp)
|
|
sort.Sort(hp)
|
|
sort.Sort(fp)
|
|
|
|
var srs responses
|
|
if operation.Responses != nil {
|
|
srs = sortedResponses(operation.Responses.StatusCodeResponses)
|
|
}
|
|
responses := make([]GenResponse, 0, len(srs))
|
|
var defaultResponse *GenResponse
|
|
var successResponses []GenResponse
|
|
if operation.Responses != nil {
|
|
for _, v := range srs {
|
|
name, ok := v.Response.Extensions.GetString(xGoName)
|
|
if !ok {
|
|
// look for name of well-known codes
|
|
name = runtime.Statuses[v.Code]
|
|
if name == "" {
|
|
// non-standard codes deserve some name
|
|
name = fmt.Sprintf("Status %d", v.Code)
|
|
}
|
|
}
|
|
name = swag.ToJSONName(b.Name + " " + name)
|
|
isSuccess := v.Code/100 == 2
|
|
gr, err := b.MakeResponse(receiver, name, isSuccess, resolver, v.Code, v.Response)
|
|
if err != nil {
|
|
return GenOperation{}, err
|
|
}
|
|
if isSuccess {
|
|
successResponses = append(successResponses, gr)
|
|
}
|
|
responses = append(responses, gr)
|
|
}
|
|
|
|
if operation.Responses.Default != nil {
|
|
gr, err := b.MakeResponse(receiver, b.Name+" default", false, resolver, -1, *operation.Responses.Default)
|
|
if err != nil {
|
|
return GenOperation{}, err
|
|
}
|
|
defaultResponse = &gr
|
|
}
|
|
}
|
|
// Always render a default response, even when no responses were defined
|
|
if operation.Responses == nil || (operation.Responses.Default == nil && len(srs) == 0) {
|
|
gr, err := b.MakeResponse(receiver, b.Name+" default", false, resolver, -1, spec.Response{})
|
|
if err != nil {
|
|
return GenOperation{}, err
|
|
}
|
|
defaultResponse = &gr
|
|
}
|
|
|
|
if b.Principal == "" {
|
|
b.Principal = iface
|
|
}
|
|
|
|
swsp := resolver.Doc.Spec()
|
|
var extraSchemes []string
|
|
if ess, ok := operation.Extensions.GetStringSlice(xSchemes); ok {
|
|
extraSchemes = append(extraSchemes, ess...)
|
|
}
|
|
|
|
if ess1, ok := swsp.Extensions.GetStringSlice(xSchemes); ok {
|
|
extraSchemes = concatUnique(ess1, extraSchemes)
|
|
}
|
|
sort.Strings(extraSchemes)
|
|
schemes := concatUnique(swsp.Schemes, operation.Schemes)
|
|
sort.Strings(schemes)
|
|
produces := producesOrDefault(operation.Produces, swsp.Produces, b.DefaultProduces)
|
|
sort.Strings(produces)
|
|
consumes := producesOrDefault(operation.Consumes, swsp.Consumes, b.DefaultConsumes)
|
|
sort.Strings(consumes)
|
|
|
|
var hasStreamingResponse bool
|
|
if defaultResponse != nil && defaultResponse.Schema != nil && defaultResponse.Schema.IsStream {
|
|
hasStreamingResponse = true
|
|
}
|
|
var successResponse *GenResponse
|
|
for _, sr := range successResponses {
|
|
if sr.IsSuccess {
|
|
successResponse = &sr
|
|
break
|
|
}
|
|
}
|
|
for _, sr := range successResponses {
|
|
if !hasStreamingResponse && sr.Schema != nil && sr.Schema.IsStream {
|
|
hasStreamingResponse = true
|
|
break
|
|
}
|
|
}
|
|
if !hasStreamingResponse {
|
|
for _, r := range responses {
|
|
if r.Schema != nil && r.Schema.IsStream {
|
|
hasStreamingResponse = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return GenOperation{
|
|
GenCommon: GenCommon{
|
|
Copyright: b.GenOpts.Copyright,
|
|
TargetImportPath: filepath.ToSlash(b.GenOpts.LanguageOpts.baseImport(b.GenOpts.Target)),
|
|
},
|
|
Package: b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, "api"),
|
|
RootPackage: b.RootAPIPackage,
|
|
Name: b.Name,
|
|
Method: b.Method,
|
|
Path: b.Path,
|
|
BasePath: b.BasePath,
|
|
Tags: operation.Tags,
|
|
Description: trimBOM(operation.Description),
|
|
ReceiverName: receiver,
|
|
DefaultImports: b.DefaultImports,
|
|
Imports: b.Imports,
|
|
Params: params,
|
|
Summary: trimBOM(operation.Summary),
|
|
QueryParams: qp,
|
|
PathParams: pp,
|
|
HeaderParams: hp,
|
|
FormParams: fp,
|
|
HasQueryParams: hasQueryParams,
|
|
HasPathParams: hasPathParams,
|
|
HasHeaderParams: hasHeaderParams,
|
|
HasFormParams: hasFormParams,
|
|
HasFormValueParams: hasFormValueParams,
|
|
HasFileParams: hasFileParams,
|
|
HasBodyParams: hasBodyParams,
|
|
HasStreamingResponse: hasStreamingResponse,
|
|
Authorized: b.Authed,
|
|
Security: b.makeSecurityRequirements(receiver),
|
|
SecurityDefinitions: b.makeSecuritySchemes(receiver),
|
|
Principal: b.Principal,
|
|
Responses: responses,
|
|
DefaultResponse: defaultResponse,
|
|
SuccessResponse: successResponse,
|
|
SuccessResponses: successResponses,
|
|
ExtraSchemas: gatherExtraSchemas(b.ExtraSchemas),
|
|
Schemes: schemeOrDefault(schemes, b.DefaultScheme),
|
|
ProducesMediaTypes: produces,
|
|
ConsumesMediaTypes: consumes,
|
|
ExtraSchemes: extraSchemes,
|
|
TimeoutName: timeoutName,
|
|
Extensions: operation.Extensions,
|
|
}, nil
|
|
}
|
|
|
|
func producesOrDefault(produces []string, fallback []string, defaultProduces string) []string {
|
|
if len(produces) > 0 {
|
|
return produces
|
|
}
|
|
if len(fallback) > 0 {
|
|
return fallback
|
|
}
|
|
return []string{defaultProduces}
|
|
}
|
|
|
|
func schemeOrDefault(schemes []string, defaultScheme string) []string {
|
|
if len(schemes) == 0 {
|
|
return []string{defaultScheme}
|
|
}
|
|
return schemes
|
|
}
|
|
|
|
func concatUnique(collections ...[]string) []string {
|
|
resultSet := make(map[string]struct{})
|
|
for _, c := range collections {
|
|
for _, i := range c {
|
|
if _, ok := resultSet[i]; !ok {
|
|
resultSet[i] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
var result []string
|
|
for k := range resultSet {
|
|
result = append(result, k)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) MakeResponse(receiver, name string, isSuccess bool, resolver *typeResolver, code int, resp spec.Response) (GenResponse, error) {
|
|
debugLog("[%s %s] making id %q", b.Method, b.Path, b.Operation.ID)
|
|
|
|
// assume minimal flattening has been carried on, so there is not $ref in response (but some may remain in response schema)
|
|
|
|
res := GenResponse{
|
|
Package: b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, "api"),
|
|
ModelsPackage: b.ModelsPackage,
|
|
ReceiverName: receiver,
|
|
Name: name,
|
|
Description: trimBOM(resp.Description),
|
|
DefaultImports: b.DefaultImports,
|
|
Imports: b.Imports,
|
|
IsSuccess: isSuccess,
|
|
Code: code,
|
|
Method: b.Method,
|
|
Path: b.Path,
|
|
Extensions: resp.Extensions,
|
|
}
|
|
|
|
// prepare response headers
|
|
for hName, header := range resp.Headers {
|
|
hdr, err := b.MakeHeader(receiver, hName, header)
|
|
if err != nil {
|
|
return GenResponse{}, err
|
|
}
|
|
res.Headers = append(res.Headers, hdr)
|
|
}
|
|
sort.Sort(res.Headers)
|
|
|
|
if resp.Schema != nil {
|
|
// resolve schema model
|
|
schema, ers := b.buildOperationSchema(fmt.Sprintf("%q", name), name+"Body", swag.ToGoName(name+"Body"), receiver, "i", resp.Schema, resolver)
|
|
if ers != nil {
|
|
return GenResponse{}, ers
|
|
}
|
|
res.Schema = &schema
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) MakeHeader(receiver, name string, hdr spec.Header) (GenHeader, error) {
|
|
tpe := typeForHeader(hdr) //simpleResolvedType(hdr.Type, hdr.Format, hdr.Items)
|
|
|
|
id := swag.ToGoName(name)
|
|
res := GenHeader{
|
|
sharedValidations: sharedValidationsFromSimple(hdr.CommonValidations, true), // NOTE: Required is not defined by the Swagger schema for header. Set arbitrarily to true for convenience in templates.
|
|
resolvedType: tpe,
|
|
Package: b.GenOpts.LanguageOpts.ManglePackageName(b.APIPackage, "api"),
|
|
ReceiverName: receiver,
|
|
ID: id,
|
|
Name: name,
|
|
Path: fmt.Sprintf("%q", name),
|
|
ValueExpression: fmt.Sprintf("%s.%s", receiver, id),
|
|
Description: trimBOM(hdr.Description),
|
|
Default: hdr.Default,
|
|
HasDefault: hdr.Default != nil,
|
|
Converter: stringConverters[tpe.GoType],
|
|
Formatter: stringFormatters[tpe.GoType],
|
|
ZeroValue: tpe.Zero(),
|
|
CollectionFormat: hdr.CollectionFormat,
|
|
IndexVar: "i",
|
|
}
|
|
res.HasValidations, res.HasSliceValidations = b.HasValidations(hdr.CommonValidations, res.resolvedType)
|
|
|
|
hasChildValidations := false
|
|
if hdr.Items != nil {
|
|
pi, err := b.MakeHeaderItem(receiver, name+" "+res.IndexVar, res.IndexVar+"i", "fmt.Sprintf(\"%s.%v\", \"header\", "+res.IndexVar+")", res.Name+"I", hdr.Items, nil)
|
|
if err != nil {
|
|
return GenHeader{}, err
|
|
}
|
|
res.Child = &pi
|
|
hasChildValidations = pi.HasValidations
|
|
}
|
|
// we feed the GenHeader structure the same way as we do for
|
|
// GenParameter, even though there is currently no actual validation
|
|
// for response headers.
|
|
res.HasValidations = res.HasValidations || hasChildValidations
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) MakeHeaderItem(receiver, paramName, indexVar, path, valueExpression string, items, parent *spec.Items) (GenItems, error) {
|
|
var res GenItems
|
|
res.resolvedType = simpleResolvedType(items.Type, items.Format, items.Items)
|
|
res.sharedValidations = sharedValidationsFromSimple(items.CommonValidations, false)
|
|
res.Name = paramName
|
|
res.Path = path
|
|
res.Location = "header"
|
|
res.ValueExpression = swag.ToVarName(valueExpression)
|
|
res.CollectionFormat = items.CollectionFormat
|
|
res.Converter = stringConverters[res.GoType]
|
|
res.Formatter = stringFormatters[res.GoType]
|
|
res.IndexVar = indexVar
|
|
res.HasValidations, res.HasSliceValidations = b.HasValidations(items.CommonValidations, res.resolvedType)
|
|
|
|
if items.Items != nil {
|
|
// Recursively follows nested arrays
|
|
// IMPORTANT! transmitting a ValueExpression consistent with the parent's one
|
|
hi, err := b.MakeHeaderItem(receiver, paramName+" "+indexVar, indexVar+"i", "fmt.Sprintf(\"%s.%v\", \"header\", "+indexVar+")", res.ValueExpression+"I", items.Items, items)
|
|
if err != nil {
|
|
return GenItems{}, err
|
|
}
|
|
res.Child = &hi
|
|
hi.Parent = &res
|
|
// Propagates HasValidations flag to outer Items definition (currently not in use: done to remain consistent with parameters)
|
|
res.HasValidations = res.HasValidations || hi.HasValidations
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// HasValidations resolves the validation status for simple schema objects
|
|
func (b *codeGenOpBuilder) HasValidations(sh spec.CommonValidations, rt resolvedType) (hasValidations bool, hasSliceValidations bool) {
|
|
hasNumberValidation := sh.Maximum != nil || sh.Minimum != nil || sh.MultipleOf != nil
|
|
hasStringValidation := sh.MaxLength != nil || sh.MinLength != nil || sh.Pattern != ""
|
|
hasSliceValidations = sh.MaxItems != nil || sh.MinItems != nil || sh.UniqueItems || len(sh.Enum) > 0
|
|
hasValidations = (hasNumberValidation || hasStringValidation || hasSliceValidations || rt.IsCustomFormatter) && !rt.IsStream && !rt.IsInterface
|
|
return
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) MakeParameterItem(receiver, paramName, indexVar, path, valueExpression, location string, resolver *typeResolver, items, parent *spec.Items) (GenItems, error) {
|
|
debugLog("making parameter item recv=%s param=%s index=%s valueExpr=%s path=%s location=%s", receiver, paramName, indexVar, valueExpression, path, location)
|
|
var res GenItems
|
|
res.resolvedType = simpleResolvedType(items.Type, items.Format, items.Items)
|
|
res.sharedValidations = sharedValidationsFromSimple(items.CommonValidations, false)
|
|
res.Name = paramName
|
|
res.Path = path
|
|
res.Location = location
|
|
res.ValueExpression = swag.ToVarName(valueExpression)
|
|
res.CollectionFormat = items.CollectionFormat
|
|
res.Converter = stringConverters[res.GoType]
|
|
res.Formatter = stringFormatters[res.GoType]
|
|
res.IndexVar = indexVar
|
|
|
|
res.HasValidations, res.HasSliceValidations = b.HasValidations(items.CommonValidations, res.resolvedType)
|
|
|
|
if items.Items != nil {
|
|
// Recursively follows nested arrays
|
|
// IMPORTANT! transmitting a ValueExpression consistent with the parent's one
|
|
pi, err := b.MakeParameterItem(receiver, paramName+" "+indexVar, indexVar+"i", "fmt.Sprintf(\"%s.%v\", "+path+", "+indexVar+")", res.ValueExpression+"I", location, resolver, items.Items, items)
|
|
if err != nil {
|
|
return GenItems{}, err
|
|
}
|
|
res.Child = &pi
|
|
pi.Parent = &res
|
|
// Propagates HasValidations flag to outer Items definition
|
|
res.HasValidations = res.HasValidations || pi.HasValidations
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) MakeParameter(receiver string, resolver *typeResolver, param spec.Parameter, idMapping map[string]map[string]string) (GenParameter, error) {
|
|
debugLog("[%s %s] making parameter %q", b.Method, b.Path, param.Name)
|
|
|
|
// assume minimal flattening has been carried on, so there is not $ref in response (but some may remain in response schema)
|
|
|
|
var child *GenItems
|
|
id := swag.ToGoName(param.Name)
|
|
if len(idMapping) > 0 {
|
|
id = idMapping[param.In][param.Name]
|
|
}
|
|
|
|
res := GenParameter{
|
|
ID: id,
|
|
Name: param.Name,
|
|
ModelsPackage: b.ModelsPackage,
|
|
Path: fmt.Sprintf("%q", param.Name),
|
|
ValueExpression: fmt.Sprintf("%s.%s", receiver, id),
|
|
IndexVar: "i",
|
|
Default: param.Default,
|
|
HasDefault: param.Default != nil,
|
|
Description: trimBOM(param.Description),
|
|
ReceiverName: receiver,
|
|
CollectionFormat: param.CollectionFormat,
|
|
Child: child,
|
|
Location: param.In,
|
|
AllowEmptyValue: (param.In == "query" || param.In == "formData") && param.AllowEmptyValue,
|
|
Extensions: param.Extensions,
|
|
}
|
|
|
|
if param.In == "body" {
|
|
// Process parameters declared in body (i.e. have a Schema)
|
|
res.Required = param.Required
|
|
if err := b.MakeBodyParameter(&res, resolver, param.Schema); err != nil {
|
|
return GenParameter{}, err
|
|
}
|
|
} else {
|
|
// Process parameters declared in other inputs: path, query, header (SimpleSchema)
|
|
res.resolvedType = simpleResolvedType(param.Type, param.Format, param.Items)
|
|
res.sharedValidations = sharedValidationsFromSimple(param.CommonValidations, param.Required)
|
|
|
|
res.ZeroValue = res.resolvedType.Zero()
|
|
|
|
hasChildValidations := false
|
|
if param.Items != nil {
|
|
// Follow Items definition for array parameters
|
|
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)
|
|
if err != nil {
|
|
return GenParameter{}, err
|
|
}
|
|
res.Child = &pi
|
|
// Propagates HasValidations from from child array
|
|
hasChildValidations = pi.HasValidations
|
|
}
|
|
res.IsNullable = !param.Required && !param.AllowEmptyValue
|
|
res.HasValidations, res.HasSliceValidations = b.HasValidations(param.CommonValidations, res.resolvedType)
|
|
res.HasValidations = res.HasValidations || hasChildValidations
|
|
}
|
|
|
|
// Select codegen strategy for body param validation
|
|
res.Converter = stringConverters[res.GoType]
|
|
res.Formatter = stringFormatters[res.GoType]
|
|
b.setBodyParamValidation(&res)
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// MakeBodyParameter constructs a body parameter schema
|
|
func (b *codeGenOpBuilder) MakeBodyParameter(res *GenParameter, resolver *typeResolver, sch *spec.Schema) error {
|
|
// resolve schema model
|
|
schema, ers := b.buildOperationSchema(res.Path, b.Operation.ID+"ParamsBody", swag.ToGoName(b.Operation.ID+" Body"), res.ReceiverName, res.IndexVar, sch, resolver)
|
|
if ers != nil {
|
|
return ers
|
|
}
|
|
res.Schema = &schema
|
|
res.Schema.Required = res.Required // Required in body is managed independently from validations
|
|
|
|
// build Child items for nested slices and maps
|
|
var items *GenItems
|
|
res.KeyVar = "k"
|
|
res.Schema.KeyVar = "k"
|
|
switch {
|
|
case schema.IsMap && !schema.IsInterface:
|
|
items = b.MakeBodyParameterItemsAndMaps(res, res.Schema.AdditionalProperties)
|
|
case schema.IsArray:
|
|
items = b.MakeBodyParameterItemsAndMaps(res, res.Schema.Items)
|
|
default:
|
|
items = new(GenItems)
|
|
}
|
|
|
|
// templates assume at least one .Child != nil
|
|
res.Child = items
|
|
schema.HasValidations = schema.HasValidations || items.HasValidations
|
|
|
|
res.resolvedType = schema.resolvedType
|
|
|
|
// simple and schema views share the same validations
|
|
res.sharedValidations = schema.sharedValidations
|
|
res.ZeroValue = schema.Zero()
|
|
return nil
|
|
}
|
|
|
|
// MakeBodyParameterItemsAndMaps clones the .Items schema structure (resp. .AdditionalProperties) as a .GenItems structure
|
|
// for compatibility with simple param templates.
|
|
//
|
|
// Constructed children assume simple structures: any complex object is assumed to be resolved by a model or extra schema definition
|
|
func (b *codeGenOpBuilder) MakeBodyParameterItemsAndMaps(res *GenParameter, it *GenSchema) *GenItems {
|
|
items := new(GenItems)
|
|
if it != nil {
|
|
var prev *GenItems
|
|
next := items
|
|
if res.Schema.IsArray {
|
|
next.Path = "fmt.Sprintf(\"%s.%v\", " + res.Path + ", " + res.IndexVar + ")"
|
|
} else if res.Schema.IsMap {
|
|
next.Path = "fmt.Sprintf(\"%s.%v\", " + res.Path + ", " + res.KeyVar + ")"
|
|
}
|
|
next.Name = res.Name + " " + res.Schema.IndexVar
|
|
next.IndexVar = res.Schema.IndexVar + "i"
|
|
next.KeyVar = res.Schema.KeyVar + "k"
|
|
next.ValueExpression = swag.ToVarName(res.Name + "I")
|
|
next.Location = "body"
|
|
for it != nil {
|
|
next.resolvedType = it.resolvedType
|
|
next.sharedValidations = it.sharedValidations
|
|
next.Formatter = stringFormatters[it.SwaggerFormat]
|
|
next.Converter = stringConverters[res.GoType]
|
|
next.Parent = prev
|
|
_, next.IsCustomFormatter = customFormatters[it.GoType]
|
|
next.IsCustomFormatter = next.IsCustomFormatter && !it.IsStream
|
|
|
|
// special instruction to avoid using CollectionFormat for body params
|
|
next.SkipParse = true
|
|
|
|
if prev != nil {
|
|
if prev.IsArray {
|
|
next.Path = "fmt.Sprintf(\"%s.%v\", " + prev.Path + ", " + prev.IndexVar + ")"
|
|
} else if prev.IsMap {
|
|
next.Path = "fmt.Sprintf(\"%s.%v\", " + prev.Path + ", " + prev.KeyVar + ")"
|
|
}
|
|
next.Name = prev.Name + prev.IndexVar
|
|
next.IndexVar = prev.IndexVar + "i"
|
|
next.KeyVar = prev.KeyVar + "k"
|
|
next.ValueExpression = swag.ToVarName(prev.ValueExpression + "I")
|
|
prev.Child = next
|
|
}
|
|
|
|
// found a complex or aliased thing
|
|
// hide details from the aliased type and stop recursing
|
|
if next.IsAliased || next.IsComplexObject {
|
|
next.IsArray = false
|
|
next.IsMap = false
|
|
next.IsCustomFormatter = false
|
|
next.IsComplexObject = true
|
|
next.IsAliased = true
|
|
break
|
|
}
|
|
if next.IsInterface || next.IsStream {
|
|
next.HasValidations = false
|
|
}
|
|
prev = next
|
|
next = new(GenItems)
|
|
|
|
switch {
|
|
case it.Items != nil:
|
|
it = it.Items
|
|
case it.AdditionalProperties != nil:
|
|
it = it.AdditionalProperties
|
|
default:
|
|
it = nil
|
|
}
|
|
}
|
|
// propagate HasValidations
|
|
var propag func(child *GenItems) bool
|
|
propag = func(child *GenItems) bool {
|
|
if child == nil {
|
|
return false
|
|
}
|
|
child.HasValidations = child.HasValidations || propag(child.Child)
|
|
return child.HasValidations
|
|
}
|
|
items.HasValidations = propag(items)
|
|
|
|
// resolve nullability conflicts when declaring body as a map of array of an anonymous complex object
|
|
// (e.g. refer to an extra schema type, which is nullable, but not rendered as a pointer in arrays or maps)
|
|
// Rule: outer type rules (with IsMapNullOverride), inner types are fixed
|
|
var fixNullable func(child *GenItems) string
|
|
fixNullable = func(child *GenItems) string {
|
|
if !child.IsArray && !child.IsMap {
|
|
if child.IsComplexObject {
|
|
return child.GoType
|
|
}
|
|
return ""
|
|
}
|
|
if innerType := fixNullable(child.Child); innerType != "" {
|
|
if child.IsMapNullOverride && child.IsArray {
|
|
child.GoType = "[]" + innerType
|
|
return child.GoType
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
fixNullable(items)
|
|
}
|
|
return items
|
|
}
|
|
|
|
func (b *codeGenOpBuilder) setBodyParamValidation(p *GenParameter) {
|
|
// Determine validation strategy for body param.
|
|
//
|
|
// Here are the distinct strategies:
|
|
// - the body parameter is a model object => delegates
|
|
// - the body parameter is an array of model objects => carry on slice validations, then iterate and delegate
|
|
// - the body parameter is a map of model objects => iterate and delegate
|
|
// - the body parameter is an array of simple objects (including maps)
|
|
// - the body parameter is a map of simple objects (including arrays)
|
|
if p.IsBodyParam() {
|
|
var hasSimpleBodyParams, hasSimpleBodyItems, hasSimpleBodyMap, hasModelBodyParams, hasModelBodyItems, hasModelBodyMap bool
|
|
s := p.Schema
|
|
if s != nil {
|
|
doNot := s.IsInterface || s.IsStream
|
|
// composition of primitive fields must be properly identified: hack this through
|
|
_, isPrimitive := primitives[s.GoType]
|
|
_, isFormatter := customFormatters[s.GoType]
|
|
isComposedPrimitive := s.IsPrimitive && !(isPrimitive || isFormatter)
|
|
|
|
hasSimpleBodyParams = !s.IsComplexObject && !s.IsAliased && !isComposedPrimitive && !doNot
|
|
hasModelBodyParams = (s.IsComplexObject || s.IsAliased || isComposedPrimitive) && !doNot
|
|
|
|
if s.IsArray && s.Items != nil {
|
|
it := s.Items
|
|
doNot = it.IsInterface || it.IsStream
|
|
hasSimpleBodyItems = !it.IsComplexObject && !(it.IsAliased || doNot)
|
|
hasModelBodyItems = (it.IsComplexObject || it.IsAliased) && !doNot
|
|
}
|
|
if s.IsMap && s.AdditionalProperties != nil {
|
|
it := s.AdditionalProperties
|
|
hasSimpleBodyMap = !it.IsComplexObject && !(it.IsAliased || doNot)
|
|
hasModelBodyMap = !hasSimpleBodyMap && !doNot
|
|
}
|
|
}
|
|
// set validation strategy for body param
|
|
p.HasSimpleBodyParams = hasSimpleBodyParams
|
|
p.HasSimpleBodyItems = hasSimpleBodyItems
|
|
p.HasModelBodyParams = hasModelBodyParams
|
|
p.HasModelBodyItems = hasModelBodyItems
|
|
p.HasModelBodyMap = hasModelBodyMap
|
|
p.HasSimpleBodyMap = hasSimpleBodyMap
|
|
}
|
|
|
|
}
|
|
|
|
// makeSecuritySchemes produces a sorted list of security schemes for this operation
|
|
func (b *codeGenOpBuilder) makeSecuritySchemes(receiver string) GenSecuritySchemes {
|
|
return gatherSecuritySchemes(b.SecurityDefinitions, b.Name, b.Principal, receiver)
|
|
}
|
|
|
|
// makeSecurityRequirements produces a sorted list of security requirements for this operation.
|
|
// As for current, these requirements are not used by codegen (sec. requirement is determined at runtime).
|
|
// We keep the order of the slice from the original spec, but sort the inner slice which comes from a map,
|
|
// as well as the map of scopes.
|
|
func (b *codeGenOpBuilder) makeSecurityRequirements(receiver string) []GenSecurityRequirements {
|
|
if b.Security == nil {
|
|
// nil (default requirement) is different than [] (no requirement)
|
|
return nil
|
|
}
|
|
|
|
securityRequirements := make([]GenSecurityRequirements, 0, len(b.Security))
|
|
for _, req := range b.Security {
|
|
jointReq := make(GenSecurityRequirements, 0, len(req))
|
|
for _, j := range req {
|
|
scopes := j.Scopes
|
|
sort.Strings(scopes)
|
|
jointReq = append(jointReq, GenSecurityRequirement{
|
|
Name: j.Name,
|
|
Scopes: scopes,
|
|
})
|
|
}
|
|
// sort joint requirements (come from a map in spec)
|
|
sort.Sort(jointReq)
|
|
securityRequirements = append(securityRequirements, jointReq)
|
|
}
|
|
return securityRequirements
|
|
}
|
|
|
|
// cloneSchema returns a deep copy of a schema
|
|
func (b *codeGenOpBuilder) cloneSchema(schema *spec.Schema) *spec.Schema {
|
|
savedSchema := &spec.Schema{}
|
|
schemaRep, _ := json.Marshal(schema)
|
|
_ = json.Unmarshal(schemaRep, savedSchema)
|
|
return savedSchema
|
|
}
|
|
|
|
// saveResolveContext keeps a copy of known definitions and schema to properly roll back on a makeGenSchema() call
|
|
// This uses a deep clone the spec document to construct a type resolver which knows about definitions when the making of this operation started,
|
|
// and only these definitions. We are not interested in the "original spec", but in the already transformed spec.
|
|
func (b *codeGenOpBuilder) saveResolveContext(resolver *typeResolver, schema *spec.Schema) (*typeResolver, *spec.Schema) {
|
|
rslv := newTypeResolver(b.GenOpts.LanguageOpts.ManglePackageName(resolver.ModelsPackage, "models"), b.Doc.Pristine())
|
|
|
|
return rslv, b.cloneSchema(schema)
|
|
}
|
|
|
|
// liftExtraSchemas constructs the schema for an anonymous construct with some ExtraSchemas.
|
|
//
|
|
// When some ExtraSchemas are produced from something else than a definition,
|
|
// this indicates we are not running in fully flattened mode and we need to render
|
|
// these ExtraSchemas in the operation's package.
|
|
// We need to rebuild the schema with a new type resolver to reflect this change in the
|
|
// models package.
|
|
func (b *codeGenOpBuilder) liftExtraSchemas(resolver, br *typeResolver, bs *spec.Schema, sc *schemaGenContext) (schema *GenSchema, err error) {
|
|
// restore resolving state before previous call to makeGenSchema()
|
|
rslv := br
|
|
sc.Schema = *bs
|
|
|
|
pg := sc.shallowClone()
|
|
pkg := b.GenOpts.LanguageOpts.ManglePackageName(resolver.ModelsPackage, "models")
|
|
pg.TypeResolver = newTypeResolver("", rslv.Doc).withKeepDefinitionsPackage(pkg)
|
|
pg.ExtraSchemas = make(map[string]GenSchema, len(sc.ExtraSchemas))
|
|
|
|
if err = pg.makeGenSchema(); err != nil {
|
|
return
|
|
}
|
|
// lift nested extra schemas (inlined types)
|
|
if b.ExtraSchemas == nil {
|
|
b.ExtraSchemas = make(map[string]GenSchema, len(pg.ExtraSchemas))
|
|
}
|
|
for _, v := range pg.ExtraSchemas {
|
|
vv := v
|
|
if !v.IsStream {
|
|
b.ExtraSchemas[vv.Name] = vv
|
|
}
|
|
}
|
|
schema = &pg.GenSchema
|
|
return
|
|
}
|
|
|
|
// buildOperationSchema constructs a schema for an operation (for body params or responses).
|
|
// It determines if the schema is readily available from the models package,
|
|
// or if a schema has to be generated in the operations package (i.e. is anonymous).
|
|
// Whenever an anonymous schema needs some extra schemas, we also determine if these extras are
|
|
// available from models or must be generated alongside the schema in the operations package.
|
|
//
|
|
// Duplicate extra schemas are pruned later on, when operations grouping in packages (e.g. from tags) takes place.
|
|
func (b *codeGenOpBuilder) buildOperationSchema(schemaPath, containerName, schemaName, receiverName, indexVar string, sch *spec.Schema, resolver *typeResolver) (GenSchema, error) {
|
|
var schema GenSchema
|
|
|
|
if sch == nil {
|
|
sch = &spec.Schema{}
|
|
}
|
|
rslv := resolver
|
|
sc := schemaGenContext{
|
|
Path: schemaPath,
|
|
Name: containerName,
|
|
Receiver: receiverName,
|
|
ValueExpr: receiverName,
|
|
IndexVar: indexVar,
|
|
Schema: *sch,
|
|
Required: false,
|
|
TypeResolver: rslv,
|
|
Named: false,
|
|
IncludeModel: true,
|
|
IncludeValidator: true,
|
|
StrictAdditionalProperties: b.GenOpts.StrictAdditionalProperties,
|
|
ExtraSchemas: make(map[string]GenSchema),
|
|
}
|
|
|
|
var (
|
|
br *typeResolver
|
|
bs *spec.Schema
|
|
)
|
|
// these backups are not needed when sch has name.
|
|
if sch.Ref.String() == "" {
|
|
br, bs = b.saveResolveContext(rslv, sch)
|
|
}
|
|
|
|
if err := sc.makeGenSchema(); err != nil {
|
|
return GenSchema{}, err
|
|
}
|
|
for alias, pkg := range findImports(&sc.GenSchema) {
|
|
b.Imports[alias] = pkg
|
|
}
|
|
|
|
if sch.Ref.String() == "" && len(sc.ExtraSchemas) > 0 {
|
|
newSchema, err := b.liftExtraSchemas(resolver, br, bs, &sc)
|
|
if err != nil {
|
|
return GenSchema{}, err
|
|
}
|
|
if newSchema != nil {
|
|
schema = *newSchema
|
|
}
|
|
} else {
|
|
schema = sc.GenSchema
|
|
}
|
|
|
|
if schema.IsAnonymous {
|
|
// a generated name for anonymous schema
|
|
// TODO: support x-go-name
|
|
hasProperties := len(schema.Properties) > 0
|
|
isAllOf := len(schema.AllOf) > 0
|
|
isInterface := schema.IsInterface
|
|
hasValidations := schema.HasValidations
|
|
|
|
// for complex anonymous objects, produce an extra schema
|
|
if hasProperties || isAllOf {
|
|
if b.ExtraSchemas == nil {
|
|
b.ExtraSchemas = make(map[string]GenSchema)
|
|
}
|
|
schema.Name = schemaName
|
|
schema.GoType = schemaName
|
|
schema.IsAnonymous = false
|
|
b.ExtraSchemas[schemaName] = schema
|
|
|
|
// constructs new schema to refer to the newly created type
|
|
schema = GenSchema{}
|
|
schema.IsAnonymous = false
|
|
schema.IsComplexObject = true
|
|
schema.SwaggerType = schemaName
|
|
schema.HasValidations = hasValidations
|
|
schema.GoType = schemaName
|
|
} else if isInterface {
|
|
schema = GenSchema{}
|
|
schema.IsAnonymous = false
|
|
schema.IsComplexObject = false
|
|
schema.IsInterface = true
|
|
schema.HasValidations = false
|
|
schema.GoType = iface
|
|
}
|
|
}
|
|
return schema, nil
|
|
}
|