657 lines
19 KiB
Go
Vendored
657 lines
19 KiB
Go
Vendored
package generator
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/go-openapi/spec"
|
|
)
|
|
|
|
// GenCommon contains common properties needed across
|
|
// definitions, app and operations
|
|
// TargetImportPath may be used by templates to import other (possibly
|
|
// generated) packages in the generation path (e.g. relative to GOPATH).
|
|
// TargetImportPath is NOT used by standard templates.
|
|
type GenCommon struct {
|
|
Copyright string
|
|
TargetImportPath string
|
|
}
|
|
|
|
// GenDefinition contains all the properties to generate a
|
|
// definition from a swagger spec
|
|
type GenDefinition struct {
|
|
GenCommon
|
|
GenSchema
|
|
Package string
|
|
Imports map[string]string
|
|
DefaultImports map[string]string
|
|
ExtraSchemas GenSchemaList
|
|
DependsOn []string
|
|
External bool
|
|
}
|
|
|
|
// GenDefinitions represents a list of operations to generate
|
|
// this implements a sort by operation id
|
|
type GenDefinitions []GenDefinition
|
|
|
|
func (g GenDefinitions) Len() int { return len(g) }
|
|
func (g GenDefinitions) Less(i, j int) bool { return g[i].Name < g[j].Name }
|
|
func (g GenDefinitions) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
|
|
// GenSchemaList is a list of schemas for generation.
|
|
//
|
|
// It can be sorted by name to get a stable struct layout for
|
|
// version control and such
|
|
type GenSchemaList []GenSchema
|
|
|
|
// GenSchema contains all the information needed to generate the code
|
|
// for a schema
|
|
type GenSchema struct {
|
|
resolvedType
|
|
sharedValidations
|
|
Example string
|
|
OriginalName string
|
|
Name string
|
|
Suffix string
|
|
Path string
|
|
ValueExpression string
|
|
IndexVar string
|
|
KeyVar string
|
|
Title string
|
|
Description string
|
|
Location string
|
|
ReceiverName string
|
|
Items *GenSchema
|
|
AllowsAdditionalItems bool
|
|
HasAdditionalItems bool
|
|
AdditionalItems *GenSchema
|
|
Object *GenSchema
|
|
XMLName string
|
|
CustomTag string
|
|
Properties GenSchemaList
|
|
AllOf GenSchemaList
|
|
HasAdditionalProperties bool
|
|
IsAdditionalProperties bool
|
|
AdditionalProperties *GenSchema
|
|
StrictAdditionalProperties bool
|
|
ReadOnly bool
|
|
IsVirtual bool
|
|
IsBaseType bool
|
|
HasBaseType bool
|
|
IsSubType bool
|
|
IsExported bool
|
|
DiscriminatorField string
|
|
DiscriminatorValue string
|
|
Discriminates map[string]string
|
|
Parents []string
|
|
IncludeValidator bool
|
|
IncludeModel bool
|
|
Default interface{}
|
|
WantsMarshalBinary bool // do we generate MarshalBinary interface?
|
|
StructTags []string
|
|
ExtraImports map[string]string // non-standard imports detected when using external types
|
|
}
|
|
|
|
func (g GenSchemaList) Len() int { return len(g) }
|
|
func (g GenSchemaList) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenSchemaList) Less(i, j int) bool {
|
|
a, okA := g[i].Extensions[xOrder].(float64)
|
|
b, okB := g[j].Extensions[xOrder].(float64)
|
|
|
|
// If both properties have x-order defined, then the one with lower x-order is smaller
|
|
if okA && okB {
|
|
return a < b
|
|
}
|
|
|
|
// If only the first property has x-order defined, then it is smaller
|
|
if okA {
|
|
return true
|
|
}
|
|
|
|
// If only the second property has x-order defined, then it is smaller
|
|
if okB {
|
|
return false
|
|
}
|
|
|
|
// If neither property has x-order defined, then the one with lower lexicographic name is smaller
|
|
return g[i].Name < g[j].Name
|
|
}
|
|
|
|
type sharedValidations struct {
|
|
HasValidations bool
|
|
Required bool
|
|
|
|
// String validations
|
|
MaxLength *int64
|
|
MinLength *int64
|
|
Pattern string
|
|
|
|
// Number validations
|
|
MultipleOf *float64
|
|
Minimum *float64
|
|
Maximum *float64
|
|
ExclusiveMinimum bool
|
|
ExclusiveMaximum bool
|
|
|
|
Enum []interface{}
|
|
ItemsEnum []interface{}
|
|
|
|
// Slice validations
|
|
MinItems *int64
|
|
MaxItems *int64
|
|
UniqueItems bool
|
|
HasSliceValidations bool
|
|
|
|
// Not used yet (perhaps intended for maxProperties, minProperties validations?)
|
|
NeedsSize bool
|
|
|
|
// NOTE: "patternProperties" and "dependencies" not supported by Swagger 2.0
|
|
}
|
|
|
|
// GenResponse represents a response object for code generation
|
|
type GenResponse struct {
|
|
Package string
|
|
ModelsPackage string
|
|
ReceiverName string
|
|
Name string
|
|
Description string
|
|
|
|
IsSuccess bool
|
|
|
|
Code int
|
|
Method string
|
|
Path string
|
|
Headers GenHeaders
|
|
Schema *GenSchema
|
|
AllowsForStreaming bool
|
|
|
|
Imports map[string]string
|
|
DefaultImports map[string]string
|
|
|
|
Extensions map[string]interface{}
|
|
|
|
StrictResponders bool
|
|
OperationName string
|
|
}
|
|
|
|
// GenHeader represents a header on a response for code generation
|
|
type GenHeader struct {
|
|
resolvedType
|
|
sharedValidations
|
|
|
|
Package string
|
|
ReceiverName string
|
|
IndexVar string
|
|
|
|
ID string
|
|
Name string
|
|
Path string
|
|
ValueExpression string
|
|
|
|
Title string
|
|
Description string
|
|
Default interface{}
|
|
HasDefault bool
|
|
|
|
CollectionFormat string
|
|
|
|
Child *GenItems
|
|
Parent *GenItems
|
|
|
|
Converter string
|
|
Formatter string
|
|
|
|
ZeroValue string
|
|
}
|
|
|
|
// ItemsDepth returns a string "items.items..." with as many items as the level of nesting of the array.
|
|
// For a header objects it always returns "".
|
|
func (g *GenHeader) ItemsDepth() string {
|
|
// NOTE: this is currently used by templates to generate explicit comments in nested structures
|
|
return ""
|
|
}
|
|
|
|
// GenHeaders is a sorted collection of headers for codegen
|
|
type GenHeaders []GenHeader
|
|
|
|
func (g GenHeaders) Len() int { return len(g) }
|
|
func (g GenHeaders) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenHeaders) Less(i, j int) bool { return g[i].Name < g[j].Name }
|
|
|
|
// HasSomeDefaults returns true is at least one header has a default value set
|
|
func (g GenHeaders) HasSomeDefaults() bool {
|
|
// NOTE: this is currently used by templates to avoid empty constructs
|
|
for _, header := range g {
|
|
if header.HasDefault {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GenParameter is used to represent
|
|
// a parameter or a header for code generation.
|
|
type GenParameter struct {
|
|
resolvedType
|
|
sharedValidations
|
|
|
|
ID string
|
|
Name string
|
|
ModelsPackage string
|
|
Path string
|
|
ValueExpression string
|
|
IndexVar string
|
|
KeyVar string
|
|
ReceiverName string
|
|
Location string
|
|
Title string
|
|
Description string
|
|
Converter string
|
|
Formatter string
|
|
|
|
Schema *GenSchema
|
|
|
|
CollectionFormat string
|
|
|
|
Child *GenItems
|
|
Parent *GenItems
|
|
|
|
/// Unused
|
|
//BodyParam *GenParameter
|
|
|
|
Default interface{}
|
|
HasDefault bool
|
|
ZeroValue string
|
|
AllowEmptyValue bool
|
|
|
|
// validation strategy for Body params, which may mix model and simple constructs.
|
|
// Distinguish the following cases:
|
|
// - HasSimpleBodyParams: body is an inline simple type
|
|
// - HasModelBodyParams: body is a model objectd
|
|
// - HasSimpleBodyItems: body is an inline array of simple type
|
|
// - HasModelBodyItems: body is an array of model objects
|
|
// - HasSimpleBodyMap: body is a map of simple objects (possibly arrays)
|
|
// - HasModelBodyMap: body is a map of model objects
|
|
HasSimpleBodyParams bool
|
|
HasModelBodyParams bool
|
|
HasSimpleBodyItems bool
|
|
HasModelBodyItems bool
|
|
HasSimpleBodyMap bool
|
|
HasModelBodyMap bool
|
|
|
|
Extensions map[string]interface{}
|
|
}
|
|
|
|
// IsQueryParam returns true when this parameter is a query param
|
|
func (g *GenParameter) IsQueryParam() bool {
|
|
return g.Location == "query"
|
|
}
|
|
|
|
// IsPathParam returns true when this parameter is a path param
|
|
func (g *GenParameter) IsPathParam() bool {
|
|
return g.Location == "path"
|
|
}
|
|
|
|
// IsFormParam returns true when this parameter is a form param
|
|
func (g *GenParameter) IsFormParam() bool {
|
|
return g.Location == "formData"
|
|
}
|
|
|
|
// IsHeaderParam returns true when this parameter is a header param
|
|
func (g *GenParameter) IsHeaderParam() bool {
|
|
return g.Location == "header"
|
|
}
|
|
|
|
// IsBodyParam returns true when this parameter is a body param
|
|
func (g *GenParameter) IsBodyParam() bool {
|
|
return g.Location == "body"
|
|
}
|
|
|
|
// IsFileParam returns true when this parameter is a file param
|
|
func (g *GenParameter) IsFileParam() bool {
|
|
return g.SwaggerType == "file"
|
|
}
|
|
|
|
// ItemsDepth returns a string "items.items..." with as many items as the level of nesting of the array.
|
|
// For a parameter object, it always returns "".
|
|
func (g *GenParameter) ItemsDepth() string {
|
|
// NOTE: this is currently used by templates to generate explicit comments in nested structures
|
|
return ""
|
|
}
|
|
|
|
// GenParameters represents a sorted parameter collection
|
|
type GenParameters []GenParameter
|
|
|
|
func (g GenParameters) Len() int { return len(g) }
|
|
func (g GenParameters) Less(i, j int) bool { return g[i].Name < g[j].Name }
|
|
func (g GenParameters) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
|
|
// HasSomeDefaults returns true is at least one parameter has a default value set
|
|
func (g GenParameters) HasSomeDefaults() bool {
|
|
// NOTE: this is currently used by templates to avoid empty constructs
|
|
for _, param := range g {
|
|
if param.HasDefault {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GenItems represents the collection items for a collection parameter
|
|
type GenItems struct {
|
|
sharedValidations
|
|
resolvedType
|
|
|
|
Name string
|
|
Path string
|
|
ValueExpression string
|
|
CollectionFormat string
|
|
Child *GenItems
|
|
Parent *GenItems
|
|
Converter string
|
|
Formatter string
|
|
|
|
Location string
|
|
IndexVar string
|
|
KeyVar string
|
|
|
|
// instructs generator to skip the splitting and parsing from CollectionFormat
|
|
SkipParse bool
|
|
// instructs generator that some nested structure needs an higher level loop index
|
|
NeedsIndex bool
|
|
}
|
|
|
|
// ItemsDepth returns a string "items.items..." with as many items as the level of nesting of the array.
|
|
func (g *GenItems) ItemsDepth() string {
|
|
// NOTE: this is currently used by templates to generate explicit comments in nested structures
|
|
current := g
|
|
i := 1
|
|
for current.Parent != nil {
|
|
i++
|
|
current = current.Parent
|
|
}
|
|
return strings.Repeat("items.", i)
|
|
}
|
|
|
|
// GenOperationGroup represents a named (tagged) group of operations
|
|
type GenOperationGroup struct {
|
|
GenCommon
|
|
Name string
|
|
Operations GenOperations
|
|
|
|
Summary string
|
|
Description string
|
|
Imports map[string]string
|
|
DefaultImports map[string]string
|
|
RootPackage string
|
|
GenOpts *GenOpts
|
|
PackageAlias string
|
|
}
|
|
|
|
// GenOperationGroups is a sorted collection of operation groups
|
|
type GenOperationGroups []GenOperationGroup
|
|
|
|
func (g GenOperationGroups) Len() int { return len(g) }
|
|
func (g GenOperationGroups) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenOperationGroups) Less(i, j int) bool { return g[i].Name < g[j].Name }
|
|
|
|
// GenStatusCodeResponses a container for status code responses
|
|
type GenStatusCodeResponses []GenResponse
|
|
|
|
func (g GenStatusCodeResponses) Len() int { return len(g) }
|
|
func (g GenStatusCodeResponses) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenStatusCodeResponses) Less(i, j int) bool { return g[i].Code < g[j].Code }
|
|
|
|
// MarshalJSON marshals these responses to json
|
|
//
|
|
// This is used by DumpData.
|
|
func (g GenStatusCodeResponses) MarshalJSON() ([]byte, error) {
|
|
if g == nil {
|
|
return nil, nil
|
|
}
|
|
responses := make(GenStatusCodeResponses, len(g))
|
|
copy(responses, g)
|
|
// order marshalled output
|
|
sort.Sort(responses)
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteRune('{')
|
|
for i, v := range responses {
|
|
rb, err := json.Marshal(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if i > 0 {
|
|
buf.WriteRune(',')
|
|
}
|
|
buf.WriteString(fmt.Sprintf("%q:", strconv.Itoa(v.Code)))
|
|
buf.Write(rb)
|
|
}
|
|
buf.WriteRune('}')
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
// UnmarshalJSON unmarshals this GenStatusCodeResponses from json
|
|
func (g *GenStatusCodeResponses) UnmarshalJSON(data []byte) error {
|
|
var dd map[string]GenResponse
|
|
if err := json.Unmarshal(data, &dd); err != nil {
|
|
return err
|
|
}
|
|
var gg GenStatusCodeResponses
|
|
for _, v := range dd {
|
|
gg = append(gg, v)
|
|
}
|
|
sort.Sort(gg)
|
|
*g = gg
|
|
return nil
|
|
}
|
|
|
|
// GenOperation represents an operation for code generation
|
|
type GenOperation struct {
|
|
GenCommon
|
|
Package string
|
|
ReceiverName string
|
|
Name string
|
|
Summary string
|
|
Description string
|
|
Method string
|
|
Path string
|
|
BasePath string
|
|
Tags []string
|
|
UseTags bool
|
|
RootPackage string
|
|
|
|
Imports map[string]string
|
|
DefaultImports map[string]string
|
|
ExtraSchemas GenSchemaList
|
|
PackageAlias string
|
|
|
|
Authorized bool
|
|
Security []GenSecurityRequirements
|
|
SecurityDefinitions GenSecuritySchemes
|
|
Principal string
|
|
|
|
SuccessResponse *GenResponse
|
|
SuccessResponses []GenResponse
|
|
Responses GenStatusCodeResponses
|
|
DefaultResponse *GenResponse
|
|
|
|
Params GenParameters
|
|
QueryParams GenParameters
|
|
PathParams GenParameters
|
|
HeaderParams GenParameters
|
|
FormParams GenParameters
|
|
HasQueryParams bool
|
|
HasPathParams bool
|
|
HasHeaderParams bool
|
|
HasFormParams bool
|
|
HasFormValueParams bool
|
|
HasFileParams bool
|
|
HasBodyParams bool
|
|
HasStreamingResponse bool
|
|
|
|
Schemes []string
|
|
ExtraSchemes []string
|
|
ProducesMediaTypes []string
|
|
ConsumesMediaTypes []string
|
|
TimeoutName string
|
|
|
|
Extensions map[string]interface{}
|
|
|
|
StrictResponders bool
|
|
}
|
|
|
|
// GenOperations represents a list of operations to generate
|
|
// this implements a sort by operation id
|
|
type GenOperations []GenOperation
|
|
|
|
func (g GenOperations) Len() int { return len(g) }
|
|
func (g GenOperations) Less(i, j int) bool { return g[i].Name < g[j].Name }
|
|
func (g GenOperations) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
|
|
// GenApp represents all the meta data needed to generate an application
|
|
// from a swagger spec
|
|
type GenApp struct {
|
|
GenCommon
|
|
APIPackage string
|
|
Package string
|
|
ReceiverName string
|
|
Name string
|
|
Principal string
|
|
DefaultConsumes string
|
|
DefaultProduces string
|
|
Host string
|
|
BasePath string
|
|
Info *spec.Info
|
|
ExternalDocs *spec.ExternalDocumentation
|
|
Imports map[string]string
|
|
DefaultImports map[string]string
|
|
Schemes []string
|
|
ExtraSchemes []string
|
|
Consumes GenSerGroups
|
|
Produces GenSerGroups
|
|
SecurityDefinitions GenSecuritySchemes
|
|
Models []GenDefinition
|
|
Operations GenOperations
|
|
OperationGroups GenOperationGroups
|
|
SwaggerJSON string
|
|
// Embedded specs: this is important for when the generated server adds routes.
|
|
// NOTE: there is a distinct advantage to having this in runtime rather than generated code.
|
|
// We are not ever going to generate the router.
|
|
// If embedding spec is an issue (e.g. memory usage), this can be excluded with the --exclude-spec
|
|
// generation option. Alternative methods to serve spec (e.g. from disk, ...) may be implemented by
|
|
// adding a middleware to the generated API.
|
|
FlatSwaggerJSON string
|
|
ExcludeSpec bool
|
|
GenOpts *GenOpts
|
|
}
|
|
|
|
// UseGoStructFlags returns true when no strategy is specified or it is set to "go-flags"
|
|
func (g *GenApp) UseGoStructFlags() bool {
|
|
if g.GenOpts == nil {
|
|
return true
|
|
}
|
|
return g.GenOpts.FlagStrategy == "" || g.GenOpts.FlagStrategy == "go-flags"
|
|
}
|
|
|
|
// UsePFlags returns true when the flag strategy is set to pflag
|
|
func (g *GenApp) UsePFlags() bool {
|
|
return g.GenOpts != nil && strings.HasPrefix(g.GenOpts.FlagStrategy, "pflag")
|
|
}
|
|
|
|
// UseFlags returns true when the flag strategy is set to flag
|
|
func (g *GenApp) UseFlags() bool {
|
|
return g.GenOpts != nil && strings.HasPrefix(g.GenOpts.FlagStrategy, "flag")
|
|
}
|
|
|
|
// UseIntermediateMode for https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
|
func (g *GenApp) UseIntermediateMode() bool {
|
|
return g.GenOpts != nil && g.GenOpts.CompatibilityMode == "intermediate"
|
|
}
|
|
|
|
// UseModernMode for https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
|
func (g *GenApp) UseModernMode() bool {
|
|
return g.GenOpts == nil || g.GenOpts.CompatibilityMode == "" || g.GenOpts.CompatibilityMode == "modern"
|
|
}
|
|
|
|
// GenSerGroups sorted representation of serializer groups
|
|
type GenSerGroups []GenSerGroup
|
|
|
|
func (g GenSerGroups) Len() int { return len(g) }
|
|
func (g GenSerGroups) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenSerGroups) Less(i, j int) bool { return g[i].Name < g[j].Name }
|
|
|
|
// GenSerGroup represents a group of serializers: this links a serializer to a list of
|
|
// prioritized media types (mime).
|
|
type GenSerGroup struct {
|
|
GenSerializer
|
|
|
|
// All media types for this serializer. The redundant representation allows for easier use in templates
|
|
AllSerializers GenSerializers
|
|
}
|
|
|
|
// GenSerializers sorted representation of serializers
|
|
type GenSerializers []GenSerializer
|
|
|
|
func (g GenSerializers) Len() int { return len(g) }
|
|
func (g GenSerializers) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenSerializers) Less(i, j int) bool { return g[i].MediaType < g[j].MediaType }
|
|
|
|
// GenSerializer represents a single serializer for a particular media type
|
|
type GenSerializer struct {
|
|
AppName string // Application name
|
|
ReceiverName string
|
|
Name string // Name of the Producer/Consumer (e.g. json, yaml, txt, bin)
|
|
MediaType string // mime
|
|
Implementation string // func implementing the Producer/Consumer
|
|
Parameters []string // parameters supported by this serializer
|
|
}
|
|
|
|
// GenSecurityScheme represents a security scheme for code generation
|
|
type GenSecurityScheme struct {
|
|
AppName string
|
|
ID string
|
|
Name string
|
|
ReceiverName string
|
|
IsBasicAuth bool
|
|
IsAPIKeyAuth bool
|
|
IsOAuth2 bool
|
|
Scopes []string
|
|
Source string
|
|
Principal string
|
|
// from spec.SecurityScheme
|
|
Description string
|
|
Type string
|
|
In string
|
|
Flow string
|
|
AuthorizationURL string
|
|
TokenURL string
|
|
Extensions map[string]interface{}
|
|
}
|
|
|
|
// GenSecuritySchemes sorted representation of serializers
|
|
type GenSecuritySchemes []GenSecurityScheme
|
|
|
|
func (g GenSecuritySchemes) Len() int { return len(g) }
|
|
func (g GenSecuritySchemes) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenSecuritySchemes) Less(i, j int) bool { return g[i].ID < g[j].ID }
|
|
|
|
// GenSecurityRequirement represents a security requirement for an operation
|
|
type GenSecurityRequirement struct {
|
|
Name string
|
|
Scopes []string
|
|
}
|
|
|
|
// GenSecurityRequirements represents a compounded security requirement specification.
|
|
// In a []GenSecurityRequirements complete requirements specification,
|
|
// outer elements are interpreted as optional requirements (OR), and
|
|
// inner elements are interpreted as jointly required (AND).
|
|
type GenSecurityRequirements []GenSecurityRequirement
|
|
|
|
func (g GenSecurityRequirements) Len() int { return len(g) }
|
|
func (g GenSecurityRequirements) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
|
|
func (g GenSecurityRequirements) Less(i, j int) bool { return g[i].Name < g[j].Name }
|