[bugfix] Fix Swagger spec and add test script (#2698)

* Add Swagger spec test script

* Fix Swagger spec errors not related to statuses with polls

* Add API tests that post a status with a poll

* Fix creating a status with a poll from form params

* Fix Swagger spec errors related to statuses with polls (this is the last error)

* Fix Swagger spec warnings not related to unused definitions

* Suppress a duplicate list update params definition that was somehow causing wrong param names

* Add Swagger test to CI

- updates Drone config
- vendorizes go-swagger
- fixes a file extension issue that caused the test script to generate JSON instead of YAML with the vendorized version

* Put `Sample: ` on its own line everywhere

* Remove unused id param from emojiCategoriesGet

* Add 5 more pairs of profile fields to account update API Swagger

* Remove Swagger prefix from dummy fields

It makes the generated code look weird

* Manually annotate params for statusCreate operation

* Fix all remaining Swagger spec warnings

- Change some models into operation parameters
- Ignore models that already correspond to manually documented operation parameters but can't be trivially changed (those with file fields)

* Documented that creating a status with scheduled_at isn't implemented yet

* sign drone.yml

* Fix filter API Swagger errors

* fixup! Fix filter API Swagger errors

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
This commit is contained in:
Vyr Cossont 2024-03-06 09:05:45 -08:00 committed by GitHub
commit fc3741365c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
672 changed files with 135624 additions and 713 deletions

View file

@ -0,0 +1 @@
generated/

View file

@ -0,0 +1,40 @@
package generator
import (
"embed"
"io/fs"
)
//go:embed templates
var _bindata embed.FS
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0)
_ = fs.WalkDir(_bindata, "templates", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
names = append(names, path)
return nil
})
return names
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
return _bindata.ReadFile(name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}

View file

@ -0,0 +1,120 @@
// 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 (
"errors"
"github.com/go-openapi/swag"
)
// GenerateClient generates a client library for a swagger spec document.
func GenerateClient(name string, modelNames, operationIDs []string, opts *GenOpts) error {
if err := opts.CheckOpts(); err != nil {
return err
}
if err := opts.setTemplates(); err != nil {
return err
}
specDoc, analyzed, err := opts.analyzeSpec()
if err != nil {
return err
}
models, err := gatherModels(specDoc, modelNames)
if err != nil {
return err
}
operations := gatherOperations(analyzed, operationIDs)
if len(operations) == 0 {
return errors.New("no operations were selected")
}
generator := appGenerator{
Name: appNameOrDefault(specDoc, name, defaultClientName),
SpecDoc: specDoc,
Analyzed: analyzed,
Models: models,
Operations: operations,
Target: opts.Target,
DumpData: opts.DumpData,
Package: opts.LanguageOpts.ManglePackageName(opts.ClientPackage, defaultClientTarget),
APIPackage: opts.LanguageOpts.ManglePackagePath(opts.APIPackage, defaultOperationsTarget),
ModelsPackage: opts.LanguageOpts.ManglePackagePath(opts.ModelPackage, defaultModelsTarget),
ServerPackage: opts.LanguageOpts.ManglePackagePath(opts.ServerPackage, defaultServerTarget),
ClientPackage: opts.LanguageOpts.ManglePackagePath(opts.ClientPackage, defaultClientTarget),
OperationsPackage: opts.LanguageOpts.ManglePackagePath(opts.ClientPackage, defaultClientTarget),
Principal: opts.PrincipalAlias(),
DefaultScheme: opts.DefaultScheme,
DefaultProduces: opts.DefaultProduces,
DefaultConsumes: opts.DefaultConsumes,
GenOpts: opts,
}
generator.Receiver = "o"
return (&clientGenerator{generator}).Generate()
}
type clientGenerator struct {
appGenerator
}
func (c *clientGenerator) Generate() error {
app, err := c.makeCodegenApp()
if err != nil {
return err
}
if c.DumpData {
return dumpData(swag.ToDynamicJSON(app))
}
if c.GenOpts.IncludeModel {
for _, m := range app.Models {
if m.IsStream {
continue
}
mod := m
if err := c.GenOpts.renderDefinition(&mod); err != nil {
return err
}
}
}
if c.GenOpts.IncludeHandler {
for _, g := range app.OperationGroups {
opg := g
for _, o := range opg.Operations {
op := o
if err := c.GenOpts.renderOperation(&op); err != nil {
return err
}
}
if err := c.GenOpts.renderOperationGroup(&opg); err != nil {
return err
}
}
}
if c.GenOpts.IncludeSupport {
if err := c.GenOpts.renderApplication(&app); err != nil {
return err
}
}
return nil
}

View file

@ -0,0 +1,61 @@
package generator
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/viper"
)
// LanguageDefinition in the configuration file.
type LanguageDefinition struct {
Layout SectionOpts `mapstructure:"layout"`
}
// ConfigureOpts for generation
func (d *LanguageDefinition) ConfigureOpts(opts *GenOpts) error {
opts.Sections = d.Layout
if opts.LanguageOpts == nil {
opts.LanguageOpts = GoLangOpts()
}
return nil
}
// LanguageConfig structure that is obtained from parsing a config file
type LanguageConfig map[string]LanguageDefinition
// ReadConfig at the specified path, when no path is specified it will look into
// the current directory and load a .swagger.{yml,json,hcl,toml,properties} file
// Returns a viper config or an error
func ReadConfig(fpath string) (*viper.Viper, error) {
v := viper.New()
if fpath != "" {
if !fileExists(fpath, "") {
return nil, fmt.Errorf("can't find file for %q", fpath)
}
file, err := os.Open(fpath)
if err != nil {
return nil, err
}
defer func() { _ = file.Close() }()
ext := filepath.Ext(fpath)
if len(ext) > 0 {
ext = ext[1:]
}
v.SetConfigType(ext)
if err := v.ReadConfig(file); err != nil {
return nil, err
}
return v, nil
}
v.SetConfigName(".swagger")
v.AddConfigPath(".")
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(viper.UnsupportedConfigError); !ok && v.ConfigFileUsed() != "" {
return nil, err
}
}
return v, nil
}

View file

@ -0,0 +1,64 @@
// 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"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
// Debug when the env var DEBUG or SWAGGER_DEBUG is not empty
// the generators will be very noisy about what they are doing
Debug = os.Getenv("DEBUG") != "" || os.Getenv("SWAGGER_DEBUG") != ""
// generatorLogger is a debug logger for this package
generatorLogger *log.Logger
)
func debugOptions() {
generatorLogger = log.New(os.Stdout, "generator:", log.LstdFlags)
}
// debugLog wraps log.Printf with a debug-specific logger
func debugLog(frmt string, args ...interface{}) {
if Debug {
_, file, pos, _ := runtime.Caller(1)
generatorLogger.Printf("%s:%d: %s", filepath.Base(file), pos,
fmt.Sprintf(frmt, args...))
}
}
// debugLogAsJSON unmarshals its last arg as pretty JSON
func debugLogAsJSON(frmt string, args ...interface{}) {
if Debug {
var dfrmt string
_, file, pos, _ := runtime.Caller(1)
dargs := make([]interface{}, 0, len(args)+2)
dargs = append(dargs, filepath.Base(file), pos)
if len(args) > 0 {
dfrmt = "%s:%d: " + frmt + "\n%s"
bbb, _ := json.MarshalIndent(args[len(args)-1], "", " ")
dargs = append(dargs, args[0:len(args)-1]...)
dargs = append(dargs, string(bbb))
} else {
dfrmt = "%s:%d: " + frmt
}
generatorLogger.Printf(dfrmt, dargs...)
}
}

View file

@ -0,0 +1,75 @@
package generator
import (
"github.com/go-openapi/analysis"
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
type discInfo struct {
Discriminators map[string]discor
Discriminated map[string]discee
}
type discor struct {
FieldName string `json:"fieldName"`
GoType string `json:"goType"`
JSONName string `json:"jsonName"`
Children []discee `json:"children"`
}
type discee struct {
FieldName string `json:"fieldName"`
FieldValue string `json:"fieldValue"`
GoType string `json:"goType"`
JSONName string `json:"jsonName"`
Ref spec.Ref `json:"ref"`
ParentRef spec.Ref `json:"parentRef"`
}
func discriminatorInfo(doc *analysis.Spec) *discInfo {
baseTypes := make(map[string]discor)
for _, sch := range doc.AllDefinitions() {
if sch.Schema.Discriminator != "" {
tpe, _ := sch.Schema.Extensions.GetString(xGoName)
if tpe == "" {
tpe = swag.ToGoName(sch.Name)
}
baseTypes[sch.Ref.String()] = discor{
FieldName: sch.Schema.Discriminator,
GoType: tpe,
JSONName: sch.Name,
}
}
}
subTypes := make(map[string]discee)
for _, sch := range doc.SchemasWithAllOf() {
for _, ao := range sch.Schema.AllOf {
if ao.Ref.String() != "" {
if bt, ok := baseTypes[ao.Ref.String()]; ok {
name, _ := sch.Schema.Extensions.GetString(xClass)
if name == "" {
name = sch.Name
}
tpe, _ := sch.Schema.Extensions.GetString(xGoName)
if tpe == "" {
tpe = swag.ToGoName(sch.Name)
}
dce := discee{
FieldName: bt.FieldName,
FieldValue: name,
Ref: sch.Ref,
ParentRef: ao.Ref,
JSONName: sch.Name,
GoType: tpe,
}
subTypes[sch.Ref.String()] = dce
bt.Children = append(bt.Children, dce)
baseTypes[ao.Ref.String()] = bt
}
}
}
}
return &discInfo{Discriminators: baseTypes, Discriminated: subTypes}
}

View file

@ -0,0 +1,78 @@
// 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 provides the code generation library for go-swagger.
# Generating data types
The general idea is that you should rarely see interface{} in the generated code.
You get a complete representation of a swagger document in somewhat idiomatic go.
To do so, there is a set of mapping patterns that are applied,
to map a Swagger specification to go types:
definition of primitive => type alias/name
definition of array => type alias/name
definition of map => type alias/name
definition of object
with properties => struct
definition of $ref => type alias/name
object with only
additional properties => map[string]T
object with additional
properties and properties => custom serializer
schema with schema array
in items => tuple (struct with properties, custom serializer)
schema with all of => struct
* allOf schema with $ref => embedded value
* allOf schema with properties => properties are included in struct
* adding an allOf schema with just "x-isnullable": true or
"x-nullable": true turns the schema into a pointer when
there are only other extension properties provided
NOTE: anyOf and oneOf JSON-schema constructs are not supported by Swagger 2.0
A property on a definition is a pointer when any one of the following conditions is met:
it is an object schema (struct)
it has x-nullable or x-isnullable as vendor extension
it is a primitive where the zero value is valid but would fail validation
otherwise strings minLength > 0 or required results in non-pointer
numbers min > 0, max < 0 and min < max
JSONSchema and by extension Swagger allow for items that have a fixed size array,
with the schema describing the items at each index. This can be combined with additional items
to form some kind of tuple with varargs.
To map this to go it creates a struct that has fixed names and a custom json serializer.
NOTE: the additionalItems keyword is not supported by Swagger 2.0. However, the generator and validator parts
in go-swagger do.
# Documenting the generated code
The code that is generated also gets the doc comments that are used by the scanner
to generate a spec from go code. So that after generation you should be able to reverse
generate a spec from the code that was generated by your spec.
It should be equivalent to the original spec but might miss some default values and examples.
*/
package generator

View file

@ -0,0 +1,226 @@
// 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
// TODO: we may probably find a way to register most of this dynamically from strfmt
// map of function calls to be generated to get the zero value of a given type
var zeroes = map[string]string{
"bool": "false",
"float32": "0",
"float64": "0",
"int": "0",
"int8": "0",
"int16": "0",
"int32": "0",
"int64": "0",
"string": "\"\"",
"uint": "0",
"uint8": "0",
"uint16": "0",
"uint32": "0",
"uint64": "0",
// Extended formats (23 formats corresponding to the Default registry
// provided by go-openapi/strfmt)
"strfmt.Base64": "strfmt.Base64([]byte(nil))",
"strfmt.CreditCard": "strfmt.CreditCard(\"\")",
"strfmt.Date": "strfmt.Date{}",
"strfmt.DateTime": "strfmt.DateTime{}",
"strfmt.Duration": "strfmt.Duration(0)",
"strfmt.Email": "strfmt.Email(\"\")",
"strfmt.HexColor": "strfmt.HexColor(\"#000000\")",
"strfmt.Hostname": "strfmt.Hostname(\"\")",
"strfmt.IPv4": "strfmt.IPv4(\"\")",
"strfmt.IPv6": "strfmt.IPv6(\"\")",
"strfmt.ISBN": "strfmt.ISBN(\"\")",
"strfmt.ISBN10": "strfmt.ISBN10(\"\")",
"strfmt.ISBN13": "strfmt.ISBN13(\"\")",
"strfmt.MAC": "strfmt.MAC(\"\")",
"strfmt.ObjectId": "strfmt.ObjectId{}",
"strfmt.Password": "strfmt.Password(\"\")",
"strfmt.RGBColor": "strfmt.RGBColor(\"rgb(0,0,0)\")",
"strfmt.SSN": "strfmt.SSN(\"\")",
"strfmt.URI": "strfmt.URI(\"\")",
"strfmt.UUID": "strfmt.UUID(\"\")",
"strfmt.UUID3": "strfmt.UUID3(\"\")",
"strfmt.UUID4": "strfmt.UUID4(\"\")",
"strfmt.UUID5": "strfmt.UUID5(\"\")",
// "file": "runtime.File",
}
// conversion functions from string representation to a numerical or boolean
// primitive type
var stringConverters = map[string]string{
"bool": "swag.ConvertBool",
"float32": "swag.ConvertFloat32",
"float64": "swag.ConvertFloat64",
"int8": "swag.ConvertInt8",
"int16": "swag.ConvertInt16",
"int32": "swag.ConvertInt32",
"int64": "swag.ConvertInt64",
"uint8": "swag.ConvertUint8",
"uint16": "swag.ConvertUint16",
"uint32": "swag.ConvertUint32",
"uint64": "swag.ConvertUint64",
}
// formatting (string representation) functions from a native representation
// of a numerical or boolean primitive type
var stringFormatters = map[string]string{
"bool": "swag.FormatBool",
"float32": "swag.FormatFloat32",
"float64": "swag.FormatFloat64",
"int8": "swag.FormatInt8",
"int16": "swag.FormatInt16",
"int32": "swag.FormatInt32",
"int64": "swag.FormatInt64",
"uint8": "swag.FormatUint8",
"uint16": "swag.FormatUint16",
"uint32": "swag.FormatUint32",
"uint64": "swag.FormatUint64",
}
// typeMapping contains a mapping of type name to go type
var typeMapping = map[string]string{
// Standard formats with native, straightforward, mapping
"string": "string",
"boolean": "bool",
"integer": "int64",
"number": "float64",
// For file producers
"file": "runtime.File",
}
// formatMapping contains a type-specific version of mapping of format to go type
var formatMapping = map[string]map[string]string{
"number": {
"double": "float64",
"float": "float32",
"int": "int64",
"int8": "int8",
"int16": "int16",
"int32": "int32",
"int64": "int64",
"uint": "uint64",
"uint8": "uint8",
"uint16": "uint16",
"uint32": "uint32",
"uint64": "uint64",
},
"integer": {
"int": "int64",
"int8": "int8",
"int16": "int16",
"int32": "int32",
"int64": "int64",
"uint": "uint64",
"uint8": "uint8",
"uint16": "uint16",
"uint32": "uint32",
"uint64": "uint64",
},
"string": {
"char": "rune",
// Extended format registry from go-openapi/strfmt.
// Currently, 23 such formats are supported (default strftm registry),
// plus the following aliases:
// - "datetime" alias for the more official "date-time"
// - "objectid" and "ObjectId" aliases for "bsonobjectid"
"binary": "io.ReadCloser",
"byte": "strfmt.Base64",
"creditcard": "strfmt.CreditCard",
"date": "strfmt.Date",
"date-time": "strfmt.DateTime",
"datetime": "strfmt.DateTime",
"duration": "strfmt.Duration",
"email": "strfmt.Email",
"hexcolor": "strfmt.HexColor",
"hostname": "strfmt.Hostname",
"ipv4": "strfmt.IPv4",
"ipv6": "strfmt.IPv6",
"isbn": "strfmt.ISBN",
"isbn10": "strfmt.ISBN10",
"isbn13": "strfmt.ISBN13",
"mac": "strfmt.MAC",
"bsonobjectid": "strfmt.ObjectId",
"objectid": "strfmt.ObjectId",
"ObjectId": "strfmt.ObjectId", // NOTE: does it work with uppercase?
"password": "strfmt.Password",
"rgbcolor": "strfmt.RGBColor",
"ssn": "strfmt.SSN",
"uri": "strfmt.URI",
"uuid": "strfmt.UUID",
"uuid3": "strfmt.UUID3",
"uuid4": "strfmt.UUID4",
"uuid5": "strfmt.UUID5",
// For file producers
"file": "runtime.File",
},
}
// go primitive types
var primitives = map[string]struct{}{
"bool": {},
"byte": {},
"[]byte": {},
"complex64": {},
"complex128": {},
"float32": {},
"float64": {},
"int": {},
"int8": {},
"int16": {},
"int32": {},
"int64": {},
"rune": {},
"string": {},
"uint": {},
"uint8": {},
"uint16": {},
"uint32": {},
"uint64": {},
}
// Formats with a custom formatter.
// Currently, 23 such formats are supported
var customFormatters = map[string]struct{}{
"strfmt.Base64": {},
"strfmt.CreditCard": {},
"strfmt.Date": {},
"strfmt.DateTime": {},
"strfmt.Duration": {},
"strfmt.Email": {},
"strfmt.HexColor": {},
"strfmt.Hostname": {},
"strfmt.IPv4": {},
"strfmt.IPv6": {},
"strfmt.ISBN": {},
"strfmt.ISBN10": {},
"strfmt.ISBN13": {},
"strfmt.MAC": {},
"strfmt.ObjectId": {},
"strfmt.Password": {},
"strfmt.RGBColor": {},
"strfmt.SSN": {},
"strfmt.URI": {},
"strfmt.UUID": {},
"strfmt.UUID3": {},
"strfmt.UUID4": {},
"strfmt.UUID5": {},
// the following interfaces do not generate validations
"io.ReadCloser": {}, // for "format": "binary" (server side)
"io.Writer": {}, // for "format": "binary" (client side)
// NOTE: runtime.File is not a customFormatter
}

View file

@ -0,0 +1,50 @@
//go:build !windows
// +build !windows
package generator
import (
"log"
"plugin"
"text/template"
)
type GenOpts struct {
GenOptsCommon
TemplatePlugin string
}
func (g *GenOpts) setTemplates() error {
if g.TemplatePlugin != "" {
if err := g.templates.LoadPlugin(g.TemplatePlugin); err != nil {
return err
}
}
return g.GenOptsCommon.setTemplates()
}
// LoadPlugin will load the named plugin and inject its functions into the funcMap
//
// The plugin must implement a function matching the signature:
// `func AddFuncs(f template.FuncMap)`
// which can add any number of functions to the template repository funcMap.
// Any existing sprig or go-swagger templates with the same name will be overridden.
func (t *Repository) LoadPlugin(pluginPath string) error {
log.Printf("Attempting to load template plugin: %s", pluginPath)
p, err := plugin.Open(pluginPath)
if err != nil {
return err
}
f, err := p.Lookup("AddFuncs")
if err != nil {
return err
}
f.(func(template.FuncMap))(t.funcs)
return nil
}

View file

@ -0,0 +1,12 @@
//go:build windows
// +build windows
package generator
type GenOpts struct {
GenOptsCommon
}
func (g *GenOpts) setTemplates() error {
return g.GenOptsCommon.setTemplates()
}

View file

@ -0,0 +1,440 @@
package generator
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
"regexp"
goruntime "runtime"
"sort"
"strings"
"github.com/go-openapi/swag"
"golang.org/x/tools/imports"
)
var (
// DefaultLanguageFunc defines the default generation language
DefaultLanguageFunc func() *LanguageOpts
moduleRe *regexp.Regexp
)
func initLanguage() {
DefaultLanguageFunc = GoLangOpts
moduleRe = regexp.MustCompile(`module[ \t]+([^\s]+)`)
}
// LanguageOpts to describe a language to the code generator
type LanguageOpts struct {
ReservedWords []string
BaseImportFunc func(string) string `json:"-"`
ImportsFunc func(map[string]string) string `json:"-"`
ArrayInitializerFunc func(interface{}) (string, error) `json:"-"`
reservedWordsSet map[string]struct{}
initialized bool
formatFunc func(string, []byte) ([]byte, error)
fileNameFunc func(string) string // language specific source file naming rules
dirNameFunc func(string) string // language specific directory naming rules
}
// Init the language option
func (l *LanguageOpts) Init() {
if l.initialized {
return
}
l.initialized = true
l.reservedWordsSet = make(map[string]struct{})
for _, rw := range l.ReservedWords {
l.reservedWordsSet[rw] = struct{}{}
}
}
// MangleName makes sure a reserved word gets a safe name
func (l *LanguageOpts) MangleName(name, suffix string) string {
if _, ok := l.reservedWordsSet[swag.ToFileName(name)]; !ok {
return name
}
return strings.Join([]string{name, suffix}, "_")
}
// MangleVarName makes sure a reserved word gets a safe name
func (l *LanguageOpts) MangleVarName(name string) string {
nm := swag.ToVarName(name)
if _, ok := l.reservedWordsSet[nm]; !ok {
return nm
}
return nm + "Var"
}
// MangleFileName makes sure a file name gets a safe name
func (l *LanguageOpts) MangleFileName(name string) string {
if l.fileNameFunc != nil {
return l.fileNameFunc(name)
}
return swag.ToFileName(name)
}
// ManglePackageName makes sure a package gets a safe name.
// In case of a file system path (e.g. name contains "/" or "\" on Windows), this return only the last element.
func (l *LanguageOpts) ManglePackageName(name, suffix string) string {
if name == "" {
return suffix
}
if l.dirNameFunc != nil {
name = l.dirNameFunc(name)
}
pth := filepath.ToSlash(filepath.Clean(name)) // preserve path
pkg := importAlias(pth) // drop path
return l.MangleName(swag.ToFileName(prefixForName(pkg)+pkg), suffix)
}
// ManglePackagePath makes sure a full package path gets a safe name.
// Only the last part of the path is altered.
func (l *LanguageOpts) ManglePackagePath(name string, suffix string) string {
if name == "" {
return suffix
}
target := filepath.ToSlash(filepath.Clean(name)) // preserve path
parts := strings.Split(target, "/")
parts[len(parts)-1] = l.ManglePackageName(parts[len(parts)-1], suffix)
return strings.Join(parts, "/")
}
// FormatContent formats a file with a language specific formatter
func (l *LanguageOpts) FormatContent(name string, content []byte) ([]byte, error) {
if l.formatFunc != nil {
return l.formatFunc(name, content)
}
return content, nil
}
// imports generate the code to import some external packages, possibly aliased
func (l *LanguageOpts) imports(imports map[string]string) string {
if l.ImportsFunc != nil {
return l.ImportsFunc(imports)
}
return ""
}
// arrayInitializer builds a litteral array
func (l *LanguageOpts) arrayInitializer(data interface{}) (string, error) {
if l.ArrayInitializerFunc != nil {
return l.ArrayInitializerFunc(data)
}
return "", nil
}
// baseImport figures out the base path to generate import statements
func (l *LanguageOpts) baseImport(tgt string) string {
if l.BaseImportFunc != nil {
return l.BaseImportFunc(tgt)
}
debugLog("base import func is nil")
return ""
}
// GoLangOpts for rendering items as golang code
func GoLangOpts() *LanguageOpts {
var goOtherReservedSuffixes = map[string]bool{
// see:
// https://golang.org/src/go/build/syslist.go
// https://golang.org/doc/install/source#environment
// goos
"aix": true,
"android": true,
"darwin": true,
"dragonfly": true,
"freebsd": true,
"hurd": true,
"illumos": true,
"js": true,
"linux": true,
"nacl": true,
"netbsd": true,
"openbsd": true,
"plan9": true,
"solaris": true,
"windows": true,
"zos": true,
// arch
"386": true,
"amd64": true,
"amd64p32": true,
"arm": true,
"armbe": true,
"arm64": true,
"arm64be": true,
"mips": true,
"mipsle": true,
"mips64": true,
"mips64le": true,
"mips64p32": true,
"mips64p32le": true,
"ppc": true,
"ppc64": true,
"ppc64le": true,
"riscv": true,
"riscv64": true,
"s390": true,
"s390x": true,
"sparc": true,
"sparc64": true,
"wasm": true,
// other reserved suffixes
"test": true,
}
opts := new(LanguageOpts)
opts.ReservedWords = []string{
"break", "default", "func", "interface", "select",
"case", "defer", "go", "map", "struct",
"chan", "else", "goto", "package", "switch",
"const", "fallthrough", "if", "range", "type",
"continue", "for", "import", "return", "var",
}
opts.formatFunc = func(ffn string, content []byte) ([]byte, error) {
opts := new(imports.Options)
opts.TabIndent = true
opts.TabWidth = 2
opts.Fragment = true
opts.Comments = true
return imports.Process(ffn, content, opts)
}
opts.fileNameFunc = func(name string) string {
// whenever a generated file name ends with a suffix
// that is meaningful to go build, adds a "swagger"
// suffix
parts := strings.Split(swag.ToFileName(name), "_")
if goOtherReservedSuffixes[parts[len(parts)-1]] {
// file name ending with a reserved arch or os name
// are appended an innocuous suffix "swagger"
parts = append(parts, "swagger")
}
return strings.Join(parts, "_")
}
opts.dirNameFunc = func(name string) string {
// whenever a generated directory name is a special
// golang directory, append an innocuous suffix
switch name {
case "vendor", "internal":
return strings.Join([]string{name, "swagger"}, "_")
}
return name
}
opts.ImportsFunc = func(imports map[string]string) string {
if len(imports) == 0 {
return ""
}
result := make([]string, 0, len(imports))
for k, v := range imports {
_, name := path.Split(v)
if name != k {
result = append(result, fmt.Sprintf("\t%s %q", k, v))
} else {
result = append(result, fmt.Sprintf("\t%q", v))
}
}
sort.Strings(result)
return strings.Join(result, "\n")
}
opts.ArrayInitializerFunc = func(data interface{}) (string, error) {
// ArrayInitializer constructs a Go literal initializer from interface{} literals.
// e.g. []interface{}{"a", "b"} is transformed in {"a","b",}
// e.g. map[string]interface{}{ "a": "x", "b": "y"} is transformed in {"a":"x","b":"y",}.
//
// NOTE: this is currently used to construct simple slice intializers for default values.
// This allows for nicer slice initializers for slices of primitive types and avoid systematic use for json.Unmarshal().
b, err := json.Marshal(data)
if err != nil {
return "", err
}
return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(string(b), "}", ",}"), "[", "{"), "]", ",}"), "{,}", "{}"), nil
}
opts.BaseImportFunc = func(tgt string) string {
tgt = filepath.Clean(tgt)
// On Windows, filepath.Abs("") behaves differently than on Unix.
// Windows: yields an error, since Abs() does not know the volume.
// UNIX: returns current working directory
if tgt == "" {
tgt = "."
}
tgtAbsPath, err := filepath.Abs(tgt)
if err != nil {
log.Fatalf("could not evaluate base import path with target \"%s\": %v", tgt, err)
}
var tgtAbsPathExtended string
tgtAbsPathExtended, err = filepath.EvalSymlinks(tgtAbsPath)
if err != nil {
log.Fatalf("could not evaluate base import path with target \"%s\" (with symlink resolution): %v", tgtAbsPath, err)
}
gopath := os.Getenv("GOPATH")
if gopath == "" {
homeDir, herr := os.UserHomeDir()
if herr != nil {
log.Fatalln(herr)
}
gopath = filepath.Join(homeDir, "go")
}
var pth string
for _, gp := range filepath.SplitList(gopath) {
if _, derr := os.Stat(filepath.Join(gp, "src")); os.IsNotExist(derr) {
continue
}
// EvalSymLinks also calls the Clean
gopathExtended, er := filepath.EvalSymlinks(gp)
if er != nil {
panic(er)
}
gopathExtended = filepath.Join(gopathExtended, "src")
gp = filepath.Join(gp, "src")
// At this stage we have expanded and unexpanded target path. GOPATH is fully expanded.
// Expanded means symlink free.
// We compare both types of targetpath<s> with gopath.
// If any one of them coincides with gopath , it is imperative that
// target path lies inside gopath. How?
// - Case 1: Irrespective of symlinks paths coincide. Both non-expanded paths.
// - Case 2: Symlink in target path points to location inside GOPATH. (Expanded Target Path)
// - Case 3: Symlink in target path points to directory outside GOPATH (Unexpanded target path)
// Case 1: - Do nothing case. If non-expanded paths match just generate base import path as if
// there are no symlinks.
// Case 2: - Symlink in target path points to location inside GOPATH. (Expanded Target Path)
// First if will fail. Second if will succeed.
// Case 3: - Symlink in target path points to directory outside GOPATH (Unexpanded target path)
// First if will succeed and break.
// compares non expanded path for both
if ok, relativepath := checkPrefixAndFetchRelativePath(tgtAbsPath, gp); ok {
pth = relativepath
break
}
// Compares non-expanded target path
if ok, relativepath := checkPrefixAndFetchRelativePath(tgtAbsPath, gopathExtended); ok {
pth = relativepath
break
}
// Compares expanded target path.
if ok, relativepath := checkPrefixAndFetchRelativePath(tgtAbsPathExtended, gopathExtended); ok {
pth = relativepath
break
}
}
mod, goModuleAbsPath, err := tryResolveModule(tgtAbsPath)
switch {
case err != nil:
log.Fatalf("Failed to resolve module using go.mod file: %s", err)
case mod != "":
relTgt := relPathToRelGoPath(goModuleAbsPath, tgtAbsPath)
if !strings.HasSuffix(mod, relTgt) {
return filepath.ToSlash(mod + relTgt)
}
return filepath.ToSlash(mod)
}
if pth == "" {
log.Fatalln("target must reside inside a location in the $GOPATH/src or be a module")
}
return filepath.ToSlash(pth)
}
opts.Init()
return opts
}
// resolveGoModFile walks up the directory tree starting from 'dir' until it
// finds a go.mod file. If go.mod is found it will return the related file
// object. If no go.mod file is found it will return an error.
func resolveGoModFile(dir string) (*os.File, string, error) {
goModPath := filepath.Join(dir, "go.mod")
f, err := os.Open(goModPath)
if err != nil {
if os.IsNotExist(err) && dir != filepath.Dir(dir) {
return resolveGoModFile(filepath.Dir(dir))
}
return nil, "", err
}
return f, dir, nil
}
// relPathToRelGoPath takes a relative os path and returns the relative go
// package path. For unix nothing will change but for windows \ will be
// converted to /.
func relPathToRelGoPath(modAbsPath, absPath string) string {
if absPath == "." {
return ""
}
path := strings.TrimPrefix(absPath, modAbsPath)
pathItems := strings.Split(path, string(filepath.Separator))
return strings.Join(pathItems, "/")
}
func tryResolveModule(baseTargetPath string) (string, string, error) {
f, goModAbsPath, err := resolveGoModFile(baseTargetPath)
switch {
case os.IsNotExist(err):
return "", "", nil
case err != nil:
return "", "", err
}
src, err := io.ReadAll(f)
if err != nil {
return "", "", err
}
match := moduleRe.FindSubmatch(src)
if len(match) != 2 {
return "", "", nil
}
return string(match[1]), goModAbsPath, nil
}
// 1. Checks if the child path and parent path coincide.
// 2. If they do return child path relative to parent path.
// 3. Everything else return false
func checkPrefixAndFetchRelativePath(childpath string, parentpath string) (bool, string) {
// Windows (local) file systems - NTFS, as well as FAT and variants
// are case insensitive.
cp, pp := childpath, parentpath
if goruntime.GOOS == "windows" {
cp = strings.ToLower(cp)
pp = strings.ToLower(pp)
}
if strings.HasPrefix(cp, pp) {
pth, err := filepath.Rel(parentpath, childpath)
if err != nil {
log.Fatalln(err)
}
return true, pth
}
return false, ""
}

View file

@ -0,0 +1,191 @@
package generator
import (
"regexp"
"sort"
"strings"
"github.com/go-openapi/runtime"
"github.com/go-openapi/swag"
)
const jsonSerializer = "json"
var mediaTypeNames = map[*regexp.Regexp]string{
regexp.MustCompile("application/.*json"): jsonSerializer,
regexp.MustCompile("application/.*yaml"): "yaml",
regexp.MustCompile("application/.*protobuf"): "protobuf",
regexp.MustCompile("application/.*capnproto"): "capnproto",
regexp.MustCompile("application/.*thrift"): "thrift",
regexp.MustCompile("(?:application|text)/.*xml"): "xml",
regexp.MustCompile("text/.*markdown"): "markdown",
regexp.MustCompile("text/.*html"): "html",
regexp.MustCompile("text/.*csv"): "csv",
regexp.MustCompile("text/.*tsv"): "tsv",
regexp.MustCompile("text/.*javascript"): "js",
regexp.MustCompile("text/.*css"): "css",
regexp.MustCompile("text/.*plain"): "txt",
regexp.MustCompile("application/.*octet-stream"): "bin",
regexp.MustCompile("application/.*tar"): "tar",
regexp.MustCompile("application/.*gzip"): "gzip",
regexp.MustCompile("application/.*gz"): "gzip",
regexp.MustCompile("application/.*raw-stream"): "bin",
regexp.MustCompile("application/x-www-form-urlencoded"): "urlform",
regexp.MustCompile("application/javascript"): "txt",
regexp.MustCompile("multipart/form-data"): "multipartform",
regexp.MustCompile("image/.*"): "bin",
regexp.MustCompile("audio/.*"): "bin",
regexp.MustCompile("application/pdf"): "bin",
}
var knownProducers = map[string]string{
jsonSerializer: "runtime.JSONProducer()",
"yaml": "yamlpc.YAMLProducer()",
"xml": "runtime.XMLProducer()",
"txt": "runtime.TextProducer()",
"bin": "runtime.ByteStreamProducer()",
"urlform": "runtime.DiscardProducer",
"multipartform": "runtime.DiscardProducer",
}
var knownConsumers = map[string]string{
jsonSerializer: "runtime.JSONConsumer()",
"yaml": "yamlpc.YAMLConsumer()",
"xml": "runtime.XMLConsumer()",
"txt": "runtime.TextConsumer()",
"bin": "runtime.ByteStreamConsumer()",
"urlform": "runtime.DiscardConsumer",
"multipartform": "runtime.DiscardConsumer",
}
func wellKnownMime(tn string) (string, bool) {
for k, v := range mediaTypeNames {
if k.MatchString(tn) {
return v, true
}
}
return "", false
}
func mediaMime(orig string) string {
return strings.SplitN(orig, ";", 2)[0]
}
func mediaParameters(orig string) string {
parts := strings.SplitN(orig, ";", 2)
if len(parts) < 2 {
return ""
}
return parts[1]
}
func (a *appGenerator) makeSerializers(mediaTypes []string, known func(string) (string, bool)) (GenSerGroups, bool) {
supportsJSON := false
uniqueSerializers := make(map[string]*GenSerializer, len(mediaTypes))
uniqueSerializerGroups := make(map[string]*GenSerGroup, len(mediaTypes))
// build all required serializers
for _, media := range mediaTypes {
key := mediaMime(media)
nm, ok := wellKnownMime(key)
if !ok {
// keep this serializer named, even though its implementation is empty (cf. #1557)
nm = key
}
name := swag.ToJSONName(nm)
impl, _ := known(name)
ser, ok := uniqueSerializers[key]
if !ok {
ser = &GenSerializer{
AppName: a.Name,
ReceiverName: a.Receiver,
Name: name,
MediaType: key,
Implementation: impl,
Parameters: []string{},
}
uniqueSerializers[key] = ser
}
// provide all known parameters (currently unused by codegen templates)
if params := strings.TrimSpace(mediaParameters(media)); params != "" {
found := false
for _, p := range ser.Parameters {
if params == p {
found = true
break
}
}
if !found {
ser.Parameters = append(ser.Parameters, params)
}
}
uniqueSerializerGroups[name] = &GenSerGroup{
GenSerializer: GenSerializer{
AppName: a.Name,
ReceiverName: a.Receiver,
Name: name,
Implementation: impl,
},
}
}
if len(uniqueSerializers) == 0 {
impl, _ := known(jsonSerializer)
uniqueSerializers[runtime.JSONMime] = &GenSerializer{
AppName: a.Name,
ReceiverName: a.Receiver,
Name: jsonSerializer,
MediaType: runtime.JSONMime,
Implementation: impl,
Parameters: []string{},
}
uniqueSerializerGroups[jsonSerializer] = &GenSerGroup{
GenSerializer: GenSerializer{
AppName: a.Name,
ReceiverName: a.Receiver,
Name: jsonSerializer,
Implementation: impl,
},
}
supportsJSON = true
}
// group serializers by consumer/producer to serve several mime media types
serializerGroups := make(GenSerGroups, 0, len(uniqueSerializers))
for _, group := range uniqueSerializerGroups {
if group.Name == jsonSerializer {
supportsJSON = true
}
serializers := make(GenSerializers, 0, len(uniqueSerializers))
for _, ser := range uniqueSerializers {
if group.Name == ser.Name {
sort.Strings(ser.Parameters)
serializers = append(serializers, *ser)
}
}
sort.Sort(serializers)
group.AllSerializers = serializers // provides the full list of mime media types for this serializer group
serializerGroups = append(serializerGroups, *group)
}
sort.Sort(serializerGroups)
return serializerGroups, supportsJSON
}
func (a *appGenerator) makeConsumes() (GenSerGroups, bool) {
// builds a codegen struct from all consumes in the spec
return a.makeSerializers(a.Analyzed.RequiredConsumes(), func(media string) (string, bool) {
c, ok := knownConsumers[media]
return c, ok
})
}
func (a *appGenerator) makeProduces() (GenSerGroups, bool) {
// builds a codegen struct from all produces in the spec
return a.makeSerializers(a.Analyzed.RequiredProduces(), func(media string) (string, bool) {
p, ok := knownProducers[media]
return p, ok
})
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,273 @@
package generator
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"github.com/go-openapi/analysis"
swaggererrors "github.com/go-openapi/errors"
"github.com/go-openapi/loads"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
yamlv2 "gopkg.in/yaml.v2"
)
func (g *GenOpts) validateAndFlattenSpec() (*loads.Document, error) {
// Load spec document
specDoc, err := loads.Spec(g.Spec)
if err != nil {
return nil, err
}
// If accepts definitions only, add dummy swagger header to pass validation
if g.AcceptDefinitionsOnly {
specDoc, err = applyDefaultSwagger(specDoc)
if err != nil {
return nil, err
}
}
// Validate if needed
if g.ValidateSpec {
log.Printf("validating spec %v", g.Spec)
validationErrors := validate.Spec(specDoc, strfmt.Default)
if validationErrors != nil {
str := fmt.Sprintf("The swagger spec at %q is invalid against swagger specification %s. see errors :\n",
g.Spec, specDoc.Version())
for _, desc := range validationErrors.(*swaggererrors.CompositeError).Errors {
str += fmt.Sprintf("- %s\n", desc)
}
return nil, errors.New(str)
}
// TODO(fredbi): due to uncontrolled $ref state in spec, we need to reload the spec atm, or flatten won't
// work properly (validate expansion alters the $ref cache in go-openapi/spec)
specDoc, _ = loads.Spec(g.Spec)
}
// Flatten spec
//
// Some preprocessing is required before codegen
//
// This ensures at least that $ref's in the spec document are canonical,
// i.e all $ref are local to this file and point to some uniquely named definition.
//
// Default option is to ensure minimal flattening of $ref, bundling remote $refs and relocating arbitrary JSON
// pointers as definitions.
// This preprocessing may introduce duplicate names (e.g. remote $ref with same name). In this case, a definition
// suffixed with "OAIGen" is produced.
//
// Full flattening option farther transforms the spec by moving every complex object (e.g. with some properties)
// as a standalone definition.
//
// Eventually, an "expand spec" option is available. It is essentially useful for testing purposes.
//
// NOTE(fredbi): spec expansion may produce some unsupported constructs and is not yet protected against the
// following cases:
// - polymorphic types generation may fail with expansion (expand destructs the reuse intent of the $ref in allOf)
// - name duplicates may occur and result in compilation failures
//
// The right place to fix these shortcomings is go-openapi/analysis.
g.FlattenOpts.BasePath = specDoc.SpecFilePath()
g.FlattenOpts.Spec = analysis.New(specDoc.Spec())
g.printFlattenOpts()
if err = analysis.Flatten(*g.FlattenOpts); err != nil {
return nil, err
}
// yields the preprocessed spec document
return specDoc, nil
}
func (g *GenOpts) analyzeSpec() (*loads.Document, *analysis.Spec, error) {
// load, validate and flatten
specDoc, err := g.validateAndFlattenSpec()
if err != nil {
return nil, nil, err
}
// spec preprocessing option
if g.PropertiesSpecOrder {
g.Spec = WithAutoXOrder(g.Spec)
specDoc, err = loads.Spec(g.Spec)
if err != nil {
return nil, nil, err
}
}
// analyze the spec
analyzed := analysis.New(specDoc.Spec())
return specDoc, analyzed, nil
}
func (g *GenOpts) printFlattenOpts() {
var preprocessingOption string
switch {
case g.FlattenOpts.Expand:
preprocessingOption = "expand"
case g.FlattenOpts.Minimal:
preprocessingOption = "minimal flattening"
default:
preprocessingOption = "full flattening"
}
log.Printf("preprocessing spec with option: %s", preprocessingOption)
}
// findSwaggerSpec fetches a default swagger spec if none is provided
func findSwaggerSpec(nm string) (string, error) {
specs := []string{"swagger.json", "swagger.yml", "swagger.yaml"}
if nm != "" {
specs = []string{nm}
}
var name string
for _, nn := range specs {
f, err := os.Stat(nn)
if err != nil {
if os.IsNotExist(err) {
continue
}
return "", err
}
if f.IsDir() {
return "", fmt.Errorf("%s is a directory", nn)
}
name = nn
break
}
if name == "" {
return "", errors.New("couldn't find a swagger spec")
}
return name, nil
}
// WithAutoXOrder amends the spec to specify property order as they appear
// in the spec (supports yaml documents only).
func WithAutoXOrder(specPath string) string {
lookFor := func(ele interface{}, key string) (yamlv2.MapSlice, bool) {
if slice, ok := ele.(yamlv2.MapSlice); ok {
for _, v := range slice {
if v.Key == key {
if slice, ok := v.Value.(yamlv2.MapSlice); ok {
return slice, ok
}
}
}
}
return nil, false
}
var addXOrder func(interface{})
addXOrder = func(element interface{}) {
if props, ok := lookFor(element, "properties"); ok {
for i, prop := range props {
if pSlice, ok := prop.Value.(yamlv2.MapSlice); ok {
isObject := false
xOrderIndex := -1 // find if x-order already exists
for i, v := range pSlice {
if v.Key == "type" && v.Value == object {
isObject = true
}
if v.Key == xOrder {
xOrderIndex = i
break
}
}
if xOrderIndex > -1 { // override existing x-order
pSlice[xOrderIndex] = yamlv2.MapItem{Key: xOrder, Value: i}
} else { // append new x-order
pSlice = append(pSlice, yamlv2.MapItem{Key: xOrder, Value: i})
}
prop.Value = pSlice
props[i] = prop
if isObject {
addXOrder(pSlice)
}
}
}
}
}
data, err := swag.LoadFromFileOrHTTP(specPath)
if err != nil {
panic(err)
}
yamlDoc, err := BytesToYAMLv2Doc(data)
if err != nil {
panic(err)
}
if defs, ok := lookFor(yamlDoc, "definitions"); ok {
for _, def := range defs {
addXOrder(def.Value)
}
}
addXOrder(yamlDoc)
out, err := yamlv2.Marshal(yamlDoc)
if err != nil {
panic(err)
}
tmpDir, err := os.MkdirTemp("", "go-swagger-")
if err != nil {
panic(err)
}
tmpFile := filepath.Join(tmpDir, filepath.Base(specPath))
if err := os.WriteFile(tmpFile, out, 0600); err != nil {
panic(err)
}
return tmpFile
}
// BytesToYAMLDoc converts a byte slice into a YAML document
func BytesToYAMLv2Doc(data []byte) (interface{}, error) {
var canary map[interface{}]interface{} // validate this is an object and not a different type
if err := yamlv2.Unmarshal(data, &canary); err != nil {
return nil, err
}
var document yamlv2.MapSlice // preserve order that is present in the document
if err := yamlv2.Unmarshal(data, &document); err != nil {
return nil, err
}
return document, nil
}
func applyDefaultSwagger(doc *loads.Document) (*loads.Document, error) {
// bake a minimal swagger spec to pass validation
swspec := doc.Spec()
if swspec.Swagger == "" {
swspec.Swagger = "2.0"
}
if swspec.Info == nil {
info := new(spec.Info)
info.Version = "0.0.0"
info.Title = "minimal"
swspec.Info = info
}
if swspec.Paths == nil {
swspec.Paths = &spec.Paths{}
}
// rewrite the document with the new addition
jazon, err := json.Marshal(swspec)
if err != nil {
return nil, err
}
return loads.Analyzed(jazon, swspec.Swagger)
}

View file

@ -0,0 +1,803 @@
package generator
import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
"github.com/go-openapi/analysis"
"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
ExternalDocs *spec.ExternalDocumentation
}
func (g GenSchema) renderMarshalTag() string {
if g.HasBaseType {
return "-"
}
var result strings.Builder
result.WriteString(g.OriginalName)
if !g.Required && g.IsEmptyOmitted {
result.WriteString(",omitempty")
}
if g.IsJSONString {
result.WriteString(",string")
}
return result.String()
}
// PrintTags takes care of rendering tags for a struct field
func (g GenSchema) PrintTags() string {
tags := make(map[string]string, 3)
orderedTags := make([]string, 0, 3)
tags["json"] = g.renderMarshalTag()
orderedTags = append(orderedTags, "json")
if len(g.XMLName) > 0 {
if !g.Required && g.IsEmptyOmitted {
tags["xml"] = g.XMLName + ",omitempty"
} else {
tags["xml"] = g.XMLName
}
orderedTags = append(orderedTags, "xml")
}
// Add extra struct tags, only if the tag hasn't already been set, i.e. example.
// Extra struct tags have the same value has the `json` tag.
for _, tag := range g.StructTags {
if _, exists := tags[tag]; exists {
// dedupe
continue
}
switch {
case tag == "example" && len(g.Example) > 0:
// only add example tag if it's contained in the struct tags
tags["example"] = g.Example // json representation of the example object
case tag == "description" && len(g.Description) > 0:
tags["description"] = g.Description
default:
tags[tag] = tags["json"]
}
orderedTags = append(orderedTags, tag)
}
// Assemble the tags in key value pairs with the value properly quoted.
kvPairs := make([]string, 0, len(orderedTags)+1)
for _, key := range orderedTags {
kvPairs = append(kvPairs, fmt.Sprintf("%s:%s", key, strconv.Quote(tags[key])))
}
if len(g.CustomTag) > 0 {
kvPairs = append(kvPairs, g.CustomTag)
}
// Join the key value pairs by a space.
completeTag := strings.Join(kvPairs, " ")
// If the values contain a backtick, we cannot render the tag using backticks because Go does not support
// escaping backticks in raw string literals.
valuesHaveBacktick := false
for _, value := range tags {
if !strconv.CanBackquote(value) {
valuesHaveBacktick = true
break
}
}
if !valuesHaveBacktick {
return fmt.Sprintf("`%s`", completeTag)
}
// We have to escape the tag again to put it in a literal with double quotes as the tag format uses double quotes.
return strconv.Quote(completeTag)
}
// UnderlyingType tells the go type or the aliased go type
func (g GenSchema) UnderlyingType() string {
if g.IsAliased {
return g.AliasedType
}
return g.GoType
}
// ToString returns a string conversion expression for the schema
func (g GenSchema) ToString() string {
return g.resolvedType.ToString(g.ValueExpression)
}
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 {
spec.SchemaValidations
HasValidations bool
HasContextValidations bool
Required bool
HasSliceValidations bool
ItemsEnum []interface{}
// 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
Examples GenResponseExamples
}
// GenResponseExamples is a sortable collection []GenResponseExample
type GenResponseExamples []GenResponseExample
func (g GenResponseExamples) Len() int { return len(g) }
func (g GenResponseExamples) Swap(i, j int) { g[i], g[j] = g[j], g[i] }
func (g GenResponseExamples) Less(i, j int) bool { return g[i].MediaType < g[j].MediaType }
// GenResponseExample captures an example provided for a response for some mime type
type GenResponseExample struct {
MediaType string
Example interface{}
}
// 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 (h *GenHeader) ItemsDepth() string {
// NOTE: this is currently used by templates to generate explicit comments in nested structures
return ""
}
// ToString returns a string conversion expression for the header
func (h GenHeader) ToString() string {
return h.resolvedType.ToString(h.ValueExpression)
}
// 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 ""
}
// UnderlyingType tells the go type or the aliased go type
func (g GenParameter) UnderlyingType() string {
return g.GoType
}
// ToString returns a string conversion expression for the parameter
func (g GenParameter) ToString() string {
return g.resolvedType.ToString(g.ValueExpression)
}
// 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)
}
// UnderlyingType tells the go type or the aliased go type
func (g GenItems) UnderlyingType() string {
return g.GoType
}
// ToString returns a string conversion expression for the item
func (g GenItems) ToString() string {
return g.resolvedType.ToString(g.ValueExpression)
}
// 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 // resolved security requirements for the operation
SecurityDefinitions GenSecuritySchemes
SecurityRequirements []analysis.SecurityRequirement // original security requirements as per the spec (for doc)
Principal string
PrincipalIsNullable bool
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
SchemeOverrides []string // original scheme overrides for operation, as per spec (for doc)
ExtraSchemeOverrides []string // original extra scheme overrides for operation, as per spec (for doc)
ProducesMediaTypes []string
ConsumesMediaTypes []string
TimeoutName string
Extensions map[string]interface{}
StrictResponders bool
ExternalDocs *spec.ExternalDocumentation
Produces []string // original produces for operation (for doc)
Consumes []string // original consumes for operation (for doc)
}
// 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
ServerPackageAlias string
ImplementationPackageAlias string
APIPackageAlias string
Package string
ReceiverName string
Name string
Principal string
PrincipalIsNullable bool
DefaultConsumes string
DefaultProduces string
Host string
BasePath string
Info *spec.Info
ExternalDocs *spec.ExternalDocumentation
Tags []spec.Tag
Imports map[string]string
DefaultImports map[string]string
Schemes []string
ExtraSchemes []string
Consumes GenSerGroups
Produces GenSerGroups
SecurityDefinitions GenSecuritySchemes
SecurityRequirements []analysis.SecurityRequirement // original security requirements as per the spec (for doc)
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
PrincipalIsNullable bool
// from spec.SecurityScheme
Description string
Type string
In string
Flow string
AuthorizationURL string
TokenURL string
Extensions map[string]interface{}
ScopesDesc []GenSecurityScope
}
// 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
}
// GenSecurityScope represents a scope descriptor for an OAuth2 security scheme
type GenSecurityScope struct {
Name string
Description 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 }

View file

@ -0,0 +1,546 @@
// 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 (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"path"
"path/filepath"
"sort"
"github.com/go-openapi/analysis"
"github.com/go-openapi/loads"
"github.com/go-openapi/spec"
"github.com/go-openapi/swag"
)
// GenerateServer generates a server application
func GenerateServer(name string, modelNames, operationIDs []string, opts *GenOpts) error {
generator, err := newAppGenerator(name, modelNames, operationIDs, opts)
if err != nil {
return err
}
return generator.Generate()
}
// GenerateSupport generates the supporting files for an API
func GenerateSupport(name string, modelNames, operationIDs []string, opts *GenOpts) error {
generator, err := newAppGenerator(name, modelNames, operationIDs, opts)
if err != nil {
return err
}
return generator.GenerateSupport(nil)
}
// GenerateMarkdown documentation for a swagger specification
func GenerateMarkdown(output string, modelNames, operationIDs []string, opts *GenOpts) error {
if output == "." || output == "" {
output = "markdown.md"
}
if err := opts.EnsureDefaults(); err != nil {
return err
}
MarkdownSectionOpts(opts, output)
generator, err := newAppGenerator("", modelNames, operationIDs, opts)
if err != nil {
return err
}
return generator.GenerateMarkdown()
}
func newAppGenerator(name string, modelNames, operationIDs []string, opts *GenOpts) (*appGenerator, error) {
if err := opts.CheckOpts(); err != nil {
return nil, err
}
if err := opts.setTemplates(); err != nil {
return nil, err
}
specDoc, analyzed, err := opts.analyzeSpec()
if err != nil {
return nil, err
}
models, err := gatherModels(specDoc, modelNames)
if err != nil {
return nil, err
}
operations := gatherOperations(analyzed, operationIDs)
if len(operations) == 0 && !opts.IgnoreOperations {
return nil, errors.New("no operations were selected")
}
opts.Name = appNameOrDefault(specDoc, name, defaultServerName)
if opts.IncludeMain && opts.MainPackage == "" {
// default target for the generated main
opts.MainPackage = swag.ToCommandName(mainNameOrDefault(specDoc, name, defaultServerName) + "-server")
}
apiPackage := opts.LanguageOpts.ManglePackagePath(opts.APIPackage, defaultOperationsTarget)
return &appGenerator{
Name: opts.Name,
Receiver: "o",
SpecDoc: specDoc,
Analyzed: analyzed,
Models: models,
Operations: operations,
Target: opts.Target,
DumpData: opts.DumpData,
Package: opts.LanguageOpts.ManglePackageName(apiPackage, defaultOperationsTarget),
APIPackage: apiPackage,
ModelsPackage: opts.LanguageOpts.ManglePackagePath(opts.ModelPackage, defaultModelsTarget),
ServerPackage: opts.LanguageOpts.ManglePackagePath(opts.ServerPackage, defaultServerTarget),
ClientPackage: opts.LanguageOpts.ManglePackagePath(opts.ClientPackage, defaultClientTarget),
OperationsPackage: filepath.Join(opts.LanguageOpts.ManglePackagePath(opts.ServerPackage, defaultServerTarget), apiPackage),
Principal: opts.PrincipalAlias(),
DefaultScheme: opts.DefaultScheme,
DefaultProduces: opts.DefaultProduces,
DefaultConsumes: opts.DefaultConsumes,
GenOpts: opts,
}, nil
}
type appGenerator struct {
Name string
Receiver string
SpecDoc *loads.Document
Analyzed *analysis.Spec
Package string
APIPackage string
ModelsPackage string
ServerPackage string
ClientPackage string
OperationsPackage string
MainPackage string
Principal string
Models map[string]spec.Schema
Operations map[string]opRef
Target string
DumpData bool
DefaultScheme string
DefaultProduces string
DefaultConsumes string
GenOpts *GenOpts
}
func (a *appGenerator) Generate() error {
app, err := a.makeCodegenApp()
if err != nil {
return err
}
if a.DumpData {
return dumpData(app)
}
// NOTE: relative to previous implem with chan.
// IPC removed concurrent execution because of the FuncMap that is being shared
// templates are now lazy loaded so there is concurrent map access I can't guard
if a.GenOpts.IncludeModel {
log.Printf("rendering %d models", len(app.Models))
for _, md := range app.Models {
mod := md
mod.IncludeModel = true
mod.IncludeValidator = a.GenOpts.IncludeValidator
if err := a.GenOpts.renderDefinition(&mod); err != nil {
return err
}
}
}
if a.GenOpts.IncludeHandler {
log.Printf("rendering %d operation groups (tags)", app.OperationGroups.Len())
for _, g := range app.OperationGroups {
opg := g
log.Printf("rendering %d operations for %s", opg.Operations.Len(), opg.Name)
for _, p := range opg.Operations {
op := p
if err := a.GenOpts.renderOperation(&op); err != nil {
return err
}
}
// optional OperationGroups templates generation
if err := a.GenOpts.renderOperationGroup(&opg); err != nil {
return fmt.Errorf("error while rendering operation group: %v", err)
}
}
}
if a.GenOpts.IncludeSupport {
log.Printf("rendering support")
if err := a.GenerateSupport(&app); err != nil {
return err
}
}
return nil
}
func (a *appGenerator) GenerateSupport(ap *GenApp) error {
app := ap
if ap == nil {
// allows for calling GenerateSupport standalone
ca, err := a.makeCodegenApp()
if err != nil {
return err
}
app = &ca
}
baseImport := a.GenOpts.LanguageOpts.baseImport(a.Target)
serverPath := path.Join(baseImport,
a.GenOpts.LanguageOpts.ManglePackagePath(a.ServerPackage, defaultServerTarget))
pkgAlias := deconflictPkg(importAlias(serverPath), renameServerPackage)
app.DefaultImports[pkgAlias] = serverPath
app.ServerPackageAlias = pkgAlias
// add client import for cli generation
clientPath := path.Join(baseImport,
a.GenOpts.LanguageOpts.ManglePackagePath(a.ClientPackage, defaultClientTarget))
clientPkgAlias := importAlias(clientPath)
app.DefaultImports[clientPkgAlias] = clientPath
return a.GenOpts.renderApplication(app)
}
func (a *appGenerator) GenerateMarkdown() error {
app, err := a.makeCodegenApp()
if err != nil {
return err
}
return a.GenOpts.renderApplication(&app)
}
func (a *appGenerator) makeSecuritySchemes() GenSecuritySchemes {
requiredSecuritySchemes := make(map[string]spec.SecurityScheme, len(a.Analyzed.RequiredSecuritySchemes()))
for _, scheme := range a.Analyzed.RequiredSecuritySchemes() {
if req, ok := a.SpecDoc.Spec().SecurityDefinitions[scheme]; ok && req != nil {
requiredSecuritySchemes[scheme] = *req
}
}
return gatherSecuritySchemes(requiredSecuritySchemes, a.Name, a.Principal, a.Receiver, a.GenOpts.PrincipalIsNullable())
}
func (a *appGenerator) makeCodegenApp() (GenApp, error) {
log.Println("building a plan for generation")
sw := a.SpecDoc.Spec()
receiver := a.Receiver
consumes, _ := a.makeConsumes()
produces, _ := a.makeProduces()
security := a.makeSecuritySchemes()
log.Println("generation target", a.Target)
baseImport := a.GenOpts.LanguageOpts.baseImport(a.Target)
defaultImports := a.GenOpts.defaultImports()
imports := make(map[string]string, 50)
alias := deconflictPkg(a.GenOpts.LanguageOpts.ManglePackageName(a.OperationsPackage, defaultOperationsTarget), renameAPIPackage)
imports[alias] = path.Join(
baseImport,
a.GenOpts.LanguageOpts.ManglePackagePath(a.OperationsPackage, defaultOperationsTarget))
implAlias := ""
if a.GenOpts.ImplementationPackage != "" {
implAlias = deconflictPkg(a.GenOpts.LanguageOpts.ManglePackageName(a.GenOpts.ImplementationPackage, defaultImplementationTarget), renameImplementationPackage)
imports[implAlias] = a.GenOpts.ImplementationPackage
}
log.Printf("planning definitions (found: %d)", len(a.Models))
genModels := make(GenDefinitions, 0, len(a.Models))
for mn, m := range a.Models {
model, err := makeGenDefinition(
mn,
a.ModelsPackage,
m,
a.SpecDoc,
a.GenOpts,
)
if err != nil {
return GenApp{}, fmt.Errorf("error in model %s while planning definitions: %v", mn, err)
}
if model != nil {
if !model.External {
genModels = append(genModels, *model)
}
// Copy model imports to operation imports
// TODO(fredbi): mangle model pkg aliases
for alias, pkg := range model.Imports {
target := a.GenOpts.LanguageOpts.ManglePackageName(alias, "")
imports[target] = pkg
}
}
}
sort.Sort(genModels)
log.Printf("planning operations (found: %d)", len(a.Operations))
genOps := make(GenOperations, 0, len(a.Operations))
for operationName, opp := range a.Operations {
o := opp.Op
o.ID = operationName
bldr := codeGenOpBuilder{
ModelsPackage: a.ModelsPackage,
Principal: a.GenOpts.PrincipalAlias(),
Target: a.Target,
DefaultImports: defaultImports,
Imports: imports,
DefaultScheme: a.DefaultScheme,
Doc: a.SpecDoc,
Analyzed: a.Analyzed,
BasePath: a.SpecDoc.BasePath(),
GenOpts: a.GenOpts,
Name: operationName,
Operation: *o,
Method: opp.Method,
Path: opp.Path,
IncludeValidator: a.GenOpts.IncludeValidator,
APIPackage: a.APIPackage, // defaults to main operations package
DefaultProduces: a.DefaultProduces,
DefaultConsumes: a.DefaultConsumes,
}
tag, tags, ok := bldr.analyzeTags()
if !ok {
continue // operation filtered according to CLI params
}
bldr.Authed = len(a.Analyzed.SecurityRequirementsFor(o)) > 0
bldr.Security = a.Analyzed.SecurityRequirementsFor(o)
bldr.SecurityDefinitions = a.Analyzed.SecurityDefinitionsFor(o)
bldr.RootAPIPackage = a.GenOpts.LanguageOpts.ManglePackageName(a.ServerPackage, defaultServerTarget)
st := o.Tags
if a.GenOpts != nil {
st = a.GenOpts.Tags
}
intersected := intersectTags(o.Tags, st)
if len(st) > 0 && len(intersected) == 0 {
continue
}
op, err := bldr.MakeOperation()
if err != nil {
return GenApp{}, err
}
op.ReceiverName = receiver
op.Tags = tags // ordered tags for this operation, possibly filtered by CLI params
genOps = append(genOps, op)
if !a.GenOpts.SkipTagPackages && tag != "" {
importPath := filepath.ToSlash(
path.Join(
baseImport,
a.GenOpts.LanguageOpts.ManglePackagePath(a.OperationsPackage, defaultOperationsTarget),
a.GenOpts.LanguageOpts.ManglePackageName(bldr.APIPackage, defaultOperationsTarget),
))
defaultImports[bldr.APIPackageAlias] = importPath
}
}
sort.Sort(genOps)
opsGroupedByPackage := make(map[string]GenOperations, len(genOps))
for _, operation := range genOps {
opsGroupedByPackage[operation.PackageAlias] = append(opsGroupedByPackage[operation.PackageAlias], operation)
}
log.Printf("grouping operations into packages (packages: %d)", len(opsGroupedByPackage))
opGroups := make(GenOperationGroups, 0, len(opsGroupedByPackage))
for k, v := range opsGroupedByPackage {
log.Printf("operations for package packages %q (found: %d)", k, len(v))
sort.Sort(v)
// trim duplicate extra schemas within the same package
vv := make(GenOperations, 0, len(v))
seenExtraSchema := make(map[string]bool)
for _, op := range v {
uniqueExtraSchemas := make(GenSchemaList, 0, len(op.ExtraSchemas))
for _, xs := range op.ExtraSchemas {
if _, alreadyThere := seenExtraSchema[xs.Name]; !alreadyThere {
seenExtraSchema[xs.Name] = true
uniqueExtraSchemas = append(uniqueExtraSchemas, xs)
}
}
op.ExtraSchemas = uniqueExtraSchemas
vv = append(vv, op)
}
var pkg string
if len(vv) > 0 {
pkg = vv[0].Package
} else {
pkg = k
}
opGroup := GenOperationGroup{
GenCommon: GenCommon{
Copyright: a.GenOpts.Copyright,
TargetImportPath: baseImport,
},
Name: pkg,
PackageAlias: k,
Operations: vv,
DefaultImports: defaultImports,
Imports: imports,
RootPackage: a.APIPackage,
GenOpts: a.GenOpts,
}
opGroups = append(opGroups, opGroup)
}
sort.Sort(opGroups)
log.Println("planning meta data and facades")
var collectedSchemes, extraSchemes []string
for _, op := range genOps {
collectedSchemes = concatUnique(collectedSchemes, op.Schemes)
extraSchemes = concatUnique(extraSchemes, op.ExtraSchemes)
}
sort.Strings(collectedSchemes)
sort.Strings(extraSchemes)
host := "localhost"
if sw.Host != "" {
host = sw.Host
}
basePath := "/"
if sw.BasePath != "" {
basePath = sw.BasePath
}
jsonb, _ := json.MarshalIndent(a.SpecDoc.OrigSpec(), "", " ")
flatjsonb, _ := json.MarshalIndent(a.SpecDoc.Spec(), "", " ")
return GenApp{
GenCommon: GenCommon{
Copyright: a.GenOpts.Copyright,
TargetImportPath: baseImport,
},
APIPackage: a.GenOpts.LanguageOpts.ManglePackageName(a.ServerPackage, defaultServerTarget),
APIPackageAlias: alias,
ImplementationPackageAlias: implAlias,
Package: a.Package,
ReceiverName: receiver,
Name: a.Name,
Host: host,
BasePath: basePath,
Schemes: schemeOrDefault(collectedSchemes, a.DefaultScheme),
ExtraSchemes: extraSchemes,
ExternalDocs: trimExternalDoc(sw.ExternalDocs),
Tags: trimTags(sw.Tags),
Info: trimInfo(sw.Info),
Consumes: consumes,
Produces: produces,
DefaultConsumes: a.DefaultConsumes,
DefaultProduces: a.DefaultProduces,
DefaultImports: defaultImports,
Imports: imports,
SecurityDefinitions: security,
SecurityRequirements: securityRequirements(a.SpecDoc.Spec().Security), // top level securityRequirements
Models: genModels,
Operations: genOps,
OperationGroups: opGroups,
Principal: a.GenOpts.PrincipalAlias(),
SwaggerJSON: generateReadableSpec(jsonb),
FlatSwaggerJSON: generateReadableSpec(flatjsonb),
ExcludeSpec: a.GenOpts.ExcludeSpec,
GenOpts: a.GenOpts,
PrincipalIsNullable: a.GenOpts.PrincipalIsNullable(),
}, nil
}
// generateReadableSpec makes swagger json spec as a string instead of bytes
// the only character that needs to be escaped is '`' symbol, since it cannot be escaped in the GO string
// that is quoted as `string data`. The function doesn't care about the beginning or the ending of the
// string it escapes since all data that needs to be escaped is always in the middle of the swagger spec.
func generateReadableSpec(spec []byte) string {
buf := &bytes.Buffer{}
for _, b := range string(spec) {
if b == '`' {
buf.WriteString("`+\"`\"+`")
} else {
buf.WriteRune(b)
}
}
return buf.String()
}
func trimExternalDoc(in *spec.ExternalDocumentation) *spec.ExternalDocumentation {
if in == nil {
return nil
}
return &spec.ExternalDocumentation{
URL: in.URL,
Description: trimBOM(in.Description),
}
}
func trimInfo(in *spec.Info) *spec.Info {
if in == nil {
return nil
}
return &spec.Info{
InfoProps: spec.InfoProps{
Contact: in.Contact,
Title: trimBOM(in.Title),
Description: trimBOM(in.Description),
TermsOfService: trimBOM(in.TermsOfService),
License: in.License,
Version: in.Version,
},
VendorExtensible: in.VendorExtensible,
}
}
func trimTags(in []spec.Tag) []spec.Tag {
if in == nil {
return nil
}
tags := make([]spec.Tag, 0, len(in))
for _, tag := range in {
tags = append(tags, spec.Tag{
TagProps: spec.TagProps{
Name: tag.Name,
Description: trimBOM(tag.Description),
ExternalDocs: trimExternalDoc(tag.ExternalDocs),
},
})
}
return tags
}

View file

@ -0,0 +1,855 @@
package generator
import (
"bytes"
"encoding/json"
"fmt"
"math"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
"text/template"
"text/template/parse"
"unicode"
"log"
"github.com/Masterminds/sprig/v3"
"github.com/go-openapi/inflect"
"github.com/go-openapi/runtime"
"github.com/go-openapi/swag"
"github.com/kr/pretty"
)
var (
assets map[string][]byte
protectedTemplates map[string]bool
// FuncMapFunc yields a map with all functions for templates
FuncMapFunc func(*LanguageOpts) template.FuncMap
templates *Repository
docFormat map[string]string
)
func initTemplateRepo() {
FuncMapFunc = DefaultFuncMap
// this makes the ToGoName func behave with the special
// prefixing rule above
swag.GoNamePrefixFunc = prefixForName
assets = defaultAssets()
protectedTemplates = defaultProtectedTemplates()
templates = NewRepository(FuncMapFunc(DefaultLanguageFunc()))
docFormat = map[string]string{
"binary": "binary (byte stream)",
"byte": "byte (base64 string)",
}
}
// DefaultFuncMap yields a map with default functions for use in the templates.
// These are available in every template
func DefaultFuncMap(lang *LanguageOpts) template.FuncMap {
f := sprig.TxtFuncMap()
extra := template.FuncMap{
"pascalize": pascalize,
"camelize": swag.ToJSONName,
"varname": lang.MangleVarName,
"humanize": swag.ToHumanNameLower,
"snakize": lang.MangleFileName,
"toPackagePath": func(name string) string {
return filepath.FromSlash(lang.ManglePackagePath(name, ""))
},
"toPackage": func(name string) string {
return lang.ManglePackagePath(name, "")
},
"toPackageName": func(name string) string {
return lang.ManglePackageName(name, "")
},
"dasherize": swag.ToCommandName,
"pluralizeFirstWord": pluralizeFirstWord,
"json": asJSON,
"prettyjson": asPrettyJSON,
"hasInsecure": func(arg []string) bool {
return swag.ContainsStringsCI(arg, "http") || swag.ContainsStringsCI(arg, "ws")
},
"hasSecure": func(arg []string) bool {
return swag.ContainsStringsCI(arg, "https") || swag.ContainsStringsCI(arg, "wss")
},
"dropPackage": dropPackage,
"containsPkgStr": containsPkgStr,
"contains": swag.ContainsStrings,
"padSurround": padSurround,
"joinFilePath": filepath.Join,
"joinPath": path.Join,
"comment": padComment,
"blockcomment": blockComment,
"inspect": pretty.Sprint,
"cleanPath": path.Clean,
"mediaTypeName": mediaMime,
"arrayInitializer": lang.arrayInitializer,
"hasPrefix": strings.HasPrefix,
"stringContains": strings.Contains,
"imports": lang.imports,
"dict": dict,
"isInteger": isInteger,
"escapeBackticks": func(arg string) string {
return strings.ReplaceAll(arg, "`", "`+\"`\"+`")
},
"paramDocType": func(param GenParameter) string {
return resolvedDocType(param.SwaggerType, param.SwaggerFormat, param.Child)
},
"headerDocType": func(header GenHeader) string {
return resolvedDocType(header.SwaggerType, header.SwaggerFormat, header.Child)
},
"schemaDocType": func(in interface{}) string {
switch schema := in.(type) {
case GenSchema:
return resolvedDocSchemaType(schema.SwaggerType, schema.SwaggerFormat, schema.Items)
case *GenSchema:
if schema == nil {
return ""
}
return resolvedDocSchemaType(schema.SwaggerType, schema.SwaggerFormat, schema.Items)
case GenDefinition:
return resolvedDocSchemaType(schema.SwaggerType, schema.SwaggerFormat, schema.Items)
case *GenDefinition:
if schema == nil {
return ""
}
return resolvedDocSchemaType(schema.SwaggerType, schema.SwaggerFormat, schema.Items)
default:
panic("dev error: schemaDocType should be called with GenSchema or GenDefinition")
}
},
"schemaDocMapType": func(schema GenSchema) string {
return resolvedDocElemType("object", schema.SwaggerFormat, &schema.resolvedType)
},
"docCollectionFormat": resolvedDocCollectionFormat,
"trimSpace": strings.TrimSpace,
"httpStatus": httpStatus,
"cleanupEnumVariant": cleanupEnumVariant,
"gt0": gt0,
}
for k, v := range extra {
f[k] = v
}
return f
}
func defaultAssets() map[string][]byte {
return map[string][]byte{
// schema validation templates
"validation/primitive.gotmpl": MustAsset("templates/validation/primitive.gotmpl"),
"validation/customformat.gotmpl": MustAsset("templates/validation/customformat.gotmpl"),
"validation/structfield.gotmpl": MustAsset("templates/validation/structfield.gotmpl"),
"structfield.gotmpl": MustAsset("templates/structfield.gotmpl"),
"schemavalidator.gotmpl": MustAsset("templates/schemavalidator.gotmpl"),
"schemapolymorphic.gotmpl": MustAsset("templates/schemapolymorphic.gotmpl"),
"schemaembedded.gotmpl": MustAsset("templates/schemaembedded.gotmpl"),
"validation/minimum.gotmpl": MustAsset("templates/validation/minimum.gotmpl"),
"validation/maximum.gotmpl": MustAsset("templates/validation/maximum.gotmpl"),
"validation/multipleOf.gotmpl": MustAsset("templates/validation/multipleOf.gotmpl"),
// schema serialization templates
"additionalpropertiesserializer.gotmpl": MustAsset("templates/serializers/additionalpropertiesserializer.gotmpl"),
"aliasedserializer.gotmpl": MustAsset("templates/serializers/aliasedserializer.gotmpl"),
"allofserializer.gotmpl": MustAsset("templates/serializers/allofserializer.gotmpl"),
"basetypeserializer.gotmpl": MustAsset("templates/serializers/basetypeserializer.gotmpl"),
"marshalbinaryserializer.gotmpl": MustAsset("templates/serializers/marshalbinaryserializer.gotmpl"),
"schemaserializer.gotmpl": MustAsset("templates/serializers/schemaserializer.gotmpl"),
"subtypeserializer.gotmpl": MustAsset("templates/serializers/subtypeserializer.gotmpl"),
"tupleserializer.gotmpl": MustAsset("templates/serializers/tupleserializer.gotmpl"),
// schema generation template
"docstring.gotmpl": MustAsset("templates/docstring.gotmpl"),
"schematype.gotmpl": MustAsset("templates/schematype.gotmpl"),
"schemabody.gotmpl": MustAsset("templates/schemabody.gotmpl"),
"schema.gotmpl": MustAsset("templates/schema.gotmpl"),
"model.gotmpl": MustAsset("templates/model.gotmpl"),
"header.gotmpl": MustAsset("templates/header.gotmpl"),
// simple schema generation helpers templates
"simpleschema/defaultsvar.gotmpl": MustAsset("templates/simpleschema/defaultsvar.gotmpl"),
"simpleschema/defaultsinit.gotmpl": MustAsset("templates/simpleschema/defaultsinit.gotmpl"),
"swagger_json_embed.gotmpl": MustAsset("templates/swagger_json_embed.gotmpl"),
// server templates
"server/parameter.gotmpl": MustAsset("templates/server/parameter.gotmpl"),
"server/urlbuilder.gotmpl": MustAsset("templates/server/urlbuilder.gotmpl"),
"server/responses.gotmpl": MustAsset("templates/server/responses.gotmpl"),
"server/operation.gotmpl": MustAsset("templates/server/operation.gotmpl"),
"server/builder.gotmpl": MustAsset("templates/server/builder.gotmpl"),
"server/server.gotmpl": MustAsset("templates/server/server.gotmpl"),
"server/configureapi.gotmpl": MustAsset("templates/server/configureapi.gotmpl"),
"server/autoconfigureapi.gotmpl": MustAsset("templates/server/autoconfigureapi.gotmpl"),
"server/main.gotmpl": MustAsset("templates/server/main.gotmpl"),
"server/doc.gotmpl": MustAsset("templates/server/doc.gotmpl"),
// client templates
"client/parameter.gotmpl": MustAsset("templates/client/parameter.gotmpl"),
"client/response.gotmpl": MustAsset("templates/client/response.gotmpl"),
"client/client.gotmpl": MustAsset("templates/client/client.gotmpl"),
"client/facade.gotmpl": MustAsset("templates/client/facade.gotmpl"),
"markdown/docs.gotmpl": MustAsset("templates/markdown/docs.gotmpl"),
// cli templates
"cli/cli.gotmpl": MustAsset("templates/cli/cli.gotmpl"),
"cli/main.gotmpl": MustAsset("templates/cli/main.gotmpl"),
"cli/modelcli.gotmpl": MustAsset("templates/cli/modelcli.gotmpl"),
"cli/operation.gotmpl": MustAsset("templates/cli/operation.gotmpl"),
"cli/registerflag.gotmpl": MustAsset("templates/cli/registerflag.gotmpl"),
"cli/retrieveflag.gotmpl": MustAsset("templates/cli/retrieveflag.gotmpl"),
"cli/schema.gotmpl": MustAsset("templates/cli/schema.gotmpl"),
"cli/completion.gotmpl": MustAsset("templates/cli/completion.gotmpl"),
}
}
func defaultProtectedTemplates() map[string]bool {
return map[string]bool{
"dereffedSchemaType": true,
"docstring": true,
"header": true,
"mapvalidator": true,
"model": true,
"modelvalidator": true,
"objectvalidator": true,
"primitivefieldvalidator": true,
"privstructfield": true,
"privtuplefield": true,
"propertyValidationDocString": true,
"propertyvalidator": true,
"schema": true,
"schemaBody": true,
"schemaType": true,
"schemabody": true,
"schematype": true,
"schemavalidator": true,
"serverDoc": true,
"slicevalidator": true,
"structfield": true,
"structfieldIface": true,
"subTypeBody": true,
"swaggerJsonEmbed": true,
"tuplefield": true,
"tuplefieldIface": true,
"typeSchemaType": true,
"simpleschemaDefaultsvar": true,
"simpleschemaDefaultsinit": true,
// validation helpers
"validationCustomformat": true,
"validationPrimitive": true,
"validationStructfield": true,
"withBaseTypeBody": true,
"withoutBaseTypeBody": true,
"validationMinimum": true,
"validationMaximum": true,
"validationMultipleOf": true,
// all serializers
"additionalPropertiesSerializer": true,
"tupleSerializer": true,
"schemaSerializer": true,
"hasDiscriminatedSerializer": true,
"discriminatedSerializer": true,
}
}
// AddFile adds a file to the default repository. It will create a new template based on the filename.
// It trims the .gotmpl from the end and converts the name using swag.ToJSONName. This will strip
// directory separators and Camelcase the next letter.
// e.g validation/primitive.gotmpl will become validationPrimitive
//
// If the file contains a definition for a template that is protected the whole file will not be added
func AddFile(name, data string) error {
return templates.addFile(name, data, false)
}
// NewRepository creates a new template repository with the provided functions defined
func NewRepository(funcs template.FuncMap) *Repository {
repo := Repository{
files: make(map[string]string),
templates: make(map[string]*template.Template),
funcs: funcs,
}
if repo.funcs == nil {
repo.funcs = make(template.FuncMap)
}
return &repo
}
// Repository is the repository for the generator templates
type Repository struct {
files map[string]string
templates map[string]*template.Template
funcs template.FuncMap
allowOverride bool
mux sync.Mutex
}
// ShallowClone a repository.
//
// Clones the maps of files and templates, so as to be able to use
// the cloned repo concurrently.
func (t *Repository) ShallowClone() *Repository {
clone := &Repository{
files: make(map[string]string, len(t.files)),
templates: make(map[string]*template.Template, len(t.templates)),
funcs: t.funcs,
allowOverride: t.allowOverride,
}
t.mux.Lock()
defer t.mux.Unlock()
for k, file := range t.files {
clone.files[k] = file
}
for k, tpl := range t.templates {
clone.templates[k] = tpl
}
return clone
}
// LoadDefaults will load the embedded templates
func (t *Repository) LoadDefaults() {
for name, asset := range assets {
if err := t.addFile(name, string(asset), true); err != nil {
log.Fatal(err)
}
}
}
// LoadDir will walk the specified path and add each .gotmpl file it finds to the repository
func (t *Repository) LoadDir(templatePath string) error {
err := filepath.Walk(templatePath, func(path string, info os.FileInfo, err error) error {
if strings.HasSuffix(path, ".gotmpl") {
if assetName, e := filepath.Rel(templatePath, path); e == nil {
if data, e := os.ReadFile(path); e == nil {
if ee := t.AddFile(assetName, string(data)); ee != nil {
return fmt.Errorf("could not add template: %v", ee)
}
}
// Non-readable files are skipped
}
}
if err != nil {
return err
}
// Non-template files are skipped
return nil
})
if err != nil {
return fmt.Errorf("could not complete template processing in directory \"%s\": %v", templatePath, err)
}
return nil
}
// LoadContrib loads template from contrib directory
func (t *Repository) LoadContrib(name string) error {
log.Printf("loading contrib %s", name)
const pathPrefix = "templates/contrib/"
basePath := pathPrefix + name
filesAdded := 0
for _, aname := range AssetNames() {
if !strings.HasSuffix(aname, ".gotmpl") {
continue
}
if strings.HasPrefix(aname, basePath) {
target := aname[len(basePath)+1:]
err := t.addFile(target, string(MustAsset(aname)), true)
if err != nil {
return err
}
log.Printf("added contributed template %s from %s", target, aname)
filesAdded++
}
}
if filesAdded == 0 {
return fmt.Errorf("no files added from template: %s", name)
}
return nil
}
func (t *Repository) addFile(name, data string, allowOverride bool) error {
fileName := name
name = swag.ToJSONName(strings.TrimSuffix(name, ".gotmpl"))
templ, err := template.New(name).Funcs(t.funcs).Parse(data)
if err != nil {
return fmt.Errorf("failed to load template %s: %v", name, err)
}
// check if any protected templates are defined
if !allowOverride && !t.allowOverride {
for _, template := range templ.Templates() {
if protectedTemplates[template.Name()] {
return fmt.Errorf("cannot overwrite protected template %s", template.Name())
}
}
}
// Add each defined template into the cache
for _, template := range templ.Templates() {
t.files[template.Name()] = fileName
t.templates[template.Name()] = template.Lookup(template.Name())
}
return nil
}
// MustGet a template by name, panics when fails
func (t *Repository) MustGet(name string) *template.Template {
tpl, err := t.Get(name)
if err != nil {
panic(err)
}
return tpl
}
// AddFile adds a file to the repository. It will create a new template based on the filename.
// It trims the .gotmpl from the end and converts the name using swag.ToJSONName. This will strip
// directory separators and Camelcase the next letter.
// e.g validation/primitive.gotmpl will become validationPrimitive
//
// If the file contains a definition for a template that is protected the whole file will not be added
func (t *Repository) AddFile(name, data string) error {
return t.addFile(name, data, false)
}
// SetAllowOverride allows setting allowOverride after the Repository was initialized
func (t *Repository) SetAllowOverride(value bool) {
t.allowOverride = value
}
func findDependencies(n parse.Node) []string {
var deps []string
depMap := make(map[string]bool)
if n == nil {
return deps
}
switch node := n.(type) {
case *parse.ListNode:
if node != nil && node.Nodes != nil {
for _, nn := range node.Nodes {
for _, dep := range findDependencies(nn) {
depMap[dep] = true
}
}
}
case *parse.IfNode:
for _, dep := range findDependencies(node.BranchNode.List) {
depMap[dep] = true
}
for _, dep := range findDependencies(node.BranchNode.ElseList) {
depMap[dep] = true
}
case *parse.RangeNode:
for _, dep := range findDependencies(node.BranchNode.List) {
depMap[dep] = true
}
for _, dep := range findDependencies(node.BranchNode.ElseList) {
depMap[dep] = true
}
case *parse.WithNode:
for _, dep := range findDependencies(node.BranchNode.List) {
depMap[dep] = true
}
for _, dep := range findDependencies(node.BranchNode.ElseList) {
depMap[dep] = true
}
case *parse.TemplateNode:
depMap[node.Name] = true
}
for dep := range depMap {
deps = append(deps, dep)
}
return deps
}
func (t *Repository) flattenDependencies(templ *template.Template, dependencies map[string]bool) map[string]bool {
if dependencies == nil {
dependencies = make(map[string]bool)
}
deps := findDependencies(templ.Tree.Root)
for _, d := range deps {
if _, found := dependencies[d]; !found {
dependencies[d] = true
if tt := t.templates[d]; tt != nil {
dependencies = t.flattenDependencies(tt, dependencies)
}
}
dependencies[d] = true
}
return dependencies
}
func (t *Repository) addDependencies(templ *template.Template) (*template.Template, error) {
name := templ.Name()
deps := t.flattenDependencies(templ, nil)
for dep := range deps {
if dep == "" {
continue
}
tt := templ.Lookup(dep)
// Check if we have it
if tt == nil {
tt = t.templates[dep]
// Still don't have it, return an error
if tt == nil {
return templ, fmt.Errorf("could not find template %s", dep)
}
var err error
// Add it to the parse tree
templ, err = templ.AddParseTree(dep, tt.Tree)
if err != nil {
return templ, fmt.Errorf("dependency error: %v", err)
}
}
}
return templ.Lookup(name), nil
}
// Get will return the named template from the repository, ensuring that all dependent templates are loaded.
// It will return an error if a dependent template is not defined in the repository.
func (t *Repository) Get(name string) (*template.Template, error) {
templ, found := t.templates[name]
if !found {
return templ, fmt.Errorf("template doesn't exist %s", name)
}
return t.addDependencies(templ)
}
// DumpTemplates prints out a dump of all the defined templates, where they are defined and what their dependencies are.
func (t *Repository) DumpTemplates() {
buf := bytes.NewBuffer(nil)
fmt.Fprintln(buf, "\n# Templates")
for name, templ := range t.templates {
fmt.Fprintf(buf, "## %s\n", name)
fmt.Fprintf(buf, "Defined in `%s`\n", t.files[name])
if deps := findDependencies(templ.Tree.Root); len(deps) > 0 {
fmt.Fprintf(buf, "####requires \n - %v\n\n\n", strings.Join(deps, "\n - "))
}
fmt.Fprintln(buf, "\n---")
}
log.Println(buf.String())
}
// FuncMap functions
func asJSON(data interface{}) (string, error) {
b, err := json.Marshal(data)
if err != nil {
return "", err
}
return string(b), nil
}
func asPrettyJSON(data interface{}) (string, error) {
b, err := json.MarshalIndent(data, "", " ")
if err != nil {
return "", err
}
return string(b), nil
}
func pluralizeFirstWord(arg string) string {
sentence := strings.Split(arg, " ")
if len(sentence) == 1 {
return inflect.Pluralize(arg)
}
return inflect.Pluralize(sentence[0]) + " " + strings.Join(sentence[1:], " ")
}
func dropPackage(str string) string {
parts := strings.Split(str, ".")
return parts[len(parts)-1]
}
// return true if the GoType str contains pkg. For example "model.MyType" -> true, "MyType" -> false
func containsPkgStr(str string) bool {
dropped := dropPackage(str)
return !(dropped == str)
}
func padSurround(entry, padWith string, i, ln int) string {
var res []string
if i > 0 {
for j := 0; j < i; j++ {
res = append(res, padWith)
}
}
res = append(res, entry)
tot := ln - i - 1
for j := 0; j < tot; j++ {
res = append(res, padWith)
}
return strings.Join(res, ",")
}
func padComment(str string, pads ...string) string {
// pads specifes padding to indent multi line comments.Defaults to one space
pad := " "
lines := strings.Split(str, "\n")
if len(pads) > 0 {
pad = strings.Join(pads, "")
}
return (strings.Join(lines, "\n//"+pad))
}
func blockComment(str string) string {
return strings.ReplaceAll(str, "*/", "[*]/")
}
func pascalize(arg string) string {
runes := []rune(arg)
switch len(runes) {
case 0:
return "Empty"
case 1: // handle special case when we have a single rune that is not handled by swag.ToGoName
switch runes[0] {
case '+', '-', '#', '_', '*', '/', '=': // those cases are handled differently than swag utility
return prefixForName(arg)
}
}
return swag.ToGoName(swag.ToGoName(arg)) // want to remove spaces
}
func prefixForName(arg string) string {
first := []rune(arg)[0]
if len(arg) == 0 || unicode.IsLetter(first) {
return ""
}
switch first {
case '+':
return "Plus"
case '-':
return "Minus"
case '#':
return "HashTag"
case '*':
return "Asterisk"
case '/':
return "ForwardSlash"
case '=':
return "EqualSign"
// other cases ($,@ etc..) handled by swag.ToGoName
}
return "Nr"
}
func replaceSpecialChar(in rune) string {
switch in {
case '.':
return "-Dot-"
case '+':
return "-Plus-"
case '-':
return "-Dash-"
case '#':
return "-Hashtag-"
}
return string(in)
}
func cleanupEnumVariant(in string) string {
replaced := ""
for _, char := range in {
replaced += replaceSpecialChar(char)
}
return replaced
}
func dict(values ...interface{}) (map[string]interface{}, error) {
if len(values)%2 != 0 {
return nil, fmt.Errorf("expected even number of arguments, got %d", len(values))
}
dict := make(map[string]interface{}, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].(string)
if !ok {
return nil, fmt.Errorf("expected string key, got %+v", values[i])
}
dict[key] = values[i+1]
}
return dict, nil
}
func isInteger(arg interface{}) bool {
// is integer determines if a value may be represented by an integer
switch val := arg.(type) {
case int8, int16, int32, int, int64, uint8, uint16, uint32, uint, uint64:
return true
case *int8, *int16, *int32, *int, *int64, *uint8, *uint16, *uint32, *uint, *uint64:
v := reflect.ValueOf(arg)
return !v.IsNil()
case float64:
return math.Round(val) == val
case *float64:
return val != nil && math.Round(*val) == *val
case float32:
return math.Round(float64(val)) == float64(val)
case *float32:
return val != nil && math.Round(float64(*val)) == float64(*val)
case string:
_, err := strconv.ParseInt(val, 10, 64)
return err == nil
case *string:
if val == nil {
return false
}
_, err := strconv.ParseInt(*val, 10, 64)
return err == nil
default:
return false
}
}
func resolvedDocCollectionFormat(cf string, child *GenItems) string {
if child == nil {
return cf
}
ccf := cf
if ccf == "" {
ccf = "csv"
}
rcf := resolvedDocCollectionFormat(child.CollectionFormat, child.Child)
if rcf == "" {
return ccf
}
return ccf + "|" + rcf
}
func resolvedDocType(tn, ft string, child *GenItems) string {
if tn == "array" {
if child == nil {
return "[]any"
}
return "[]" + resolvedDocType(child.SwaggerType, child.SwaggerFormat, child.Child)
}
if ft != "" {
if doc, ok := docFormat[ft]; ok {
return doc
}
return fmt.Sprintf("%s (formatted %s)", ft, tn)
}
return tn
}
func resolvedDocSchemaType(tn, ft string, child *GenSchema) string {
if tn == "array" {
if child == nil {
return "[]any"
}
return "[]" + resolvedDocSchemaType(child.SwaggerType, child.SwaggerFormat, child.Items)
}
if tn == "object" {
if child == nil || child.ElemType == nil {
return "map of any"
}
if child.IsMap {
return "map of " + resolvedDocElemType(child.SwaggerType, child.SwaggerFormat, &child.resolvedType)
}
return child.GoType
}
if ft != "" {
if doc, ok := docFormat[ft]; ok {
return doc
}
return fmt.Sprintf("%s (formatted %s)", ft, tn)
}
return tn
}
func resolvedDocElemType(tn, ft string, schema *resolvedType) string {
if schema == nil {
return ""
}
if schema.IsMap {
return "map of " + resolvedDocElemType(schema.ElemType.SwaggerType, schema.ElemType.SwaggerFormat, schema.ElemType)
}
if schema.IsArray {
return "[]" + resolvedDocElemType(schema.ElemType.SwaggerType, schema.ElemType.SwaggerFormat, schema.ElemType)
}
if ft != "" {
if doc, ok := docFormat[ft]; ok {
return doc
}
return fmt.Sprintf("%s (formatted %s)", ft, tn)
}
return tn
}
func httpStatus(code int) string {
if name, ok := runtime.Statuses[code]; ok {
return name
}
// non-standard codes deserve some name
return fmt.Sprintf("Status %d", code)
}
func gt0(in *int64) bool {
// gt0 returns true if the *int64 points to a value > 0
// NOTE: plain {{ gt .MinProperties 0 }} just refuses to work normally
// with a pointer
return in != nil && *in > 0
}

View file

@ -0,0 +1,242 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .GenOpts.CliPackage }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
{{ imports .DefaultImports }}
{{ imports .Imports }}
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/go-openapi/runtime"
"github.com/go-openapi/swag"
httptransport "github.com/go-openapi/runtime/client"
homedir "github.com/mitchellh/go-homedir"
)
// debug flag indicating that cli should output debug logs
var debug bool
// config file location
var configFile string
// dry run flag
var dryRun bool
// name of the executable
var exeName string = filepath.Base(os.Args[0])
// logDebugf writes debug log to stdout
func logDebugf(format string, v ...interface{}) {
if !debug{
return
}
log.Printf(format, v...)
}
{{/*TODO: make this a swagger cli option*/}}
// depth of recursion to construct model flags
var maxDepth int = 5
// makeClient constructs a client object
func makeClient(cmd *cobra.Command, args []string) (*client.{{ pascalize .Name }}, error) {
hostname := viper.GetString("hostname")
viper.SetDefault("base_path", client.DefaultBasePath)
basePath := viper.GetString("base_path")
scheme := viper.GetString("scheme")
r := httptransport.New(hostname, basePath, []string{scheme})
r.SetDebug(debug)
{{- /* user might define custom mediatype xxx/json and there is no registered ones to handle. */}}
// set custom producer and consumer to use the default ones
{{ range .Consumes }}
{{ range .AllSerializers }}
{{- if stringContains .MediaType "json" }}
r.Consumers["{{ .MediaType }}"] = runtime.JSONConsumer()
{{- else }}
// warning: consumes {{ .MediaType }} is not supported by go-swagger cli yet
{{- end }}
{{- end }}
{{ end }}
{{ range .Produces }}
{{- range .AllSerializers }}
{{- if stringContains .MediaType "json" }}
r.Producers["{{ .MediaType }}"] = runtime.JSONProducer()
{{- else }}
// warning: produces {{ .MediaType }} is not supported by go-swagger cli yet
{{- end }}
{{- end }}
{{ end }}
{{- if .SecurityDefinitions }}
auth, err := makeAuthInfoWriter(cmd)
if err != nil {
return nil, err
}
r.DefaultAuthentication = auth
{{ end }}
appCli := client.New(r, strfmt.Default)
logDebugf("Server url: %v://%v", scheme, hostname)
return appCli, nil
}
// MakeRootCmd returns the root cmd
func MakeRootCmd() (*cobra.Command, error) {
cobra.OnInitialize(initViperConfigs)
// Use executable name as the command name
rootCmd := &cobra.Command{
Use: exeName,
}
{{/*note: viper binded flag value must be retrieved from viper rather than cmd*/}}
// register basic flags
rootCmd.PersistentFlags().String("hostname", client.DefaultHost, "hostname of the service")
viper.BindPFlag("hostname", rootCmd.PersistentFlags().Lookup("hostname"))
rootCmd.PersistentFlags().String("scheme", client.DefaultSchemes[0], fmt.Sprintf("Choose from: %v", client.DefaultSchemes))
viper.BindPFlag("scheme", rootCmd.PersistentFlags().Lookup("scheme"))
rootCmd.PersistentFlags().String("base-path", client.DefaultBasePath, fmt.Sprintf("For example: %v", client.DefaultBasePath))
viper.BindPFlag("base_path", rootCmd.PersistentFlags().Lookup("base-path"))
// configure debug flag
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "output debug logs")
// configure config location
rootCmd.PersistentFlags().StringVar(&configFile, "config", "", "config file path")
// configure dry run flag
rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "do not send the request to server")
// register security flags
{{- if .SecurityDefinitions }}
if err := registerAuthInoWriterFlags(rootCmd); err != nil{
return nil, err
}
{{- end }}
// add all operation groups
{{- range .OperationGroups -}}
{{- $operationGroupCmdVarName := printf "operationGroup%vCmd" (pascalize .Name) }}
{{ $operationGroupCmdVarName }}, err := makeOperationGroup{{ pascalize .Name }}Cmd()
if err != nil {
return nil, err
}
rootCmd.AddCommand({{ $operationGroupCmdVarName }})
{{ end }}
// add cobra completion
rootCmd.AddCommand(makeGenCompletionCmd())
return rootCmd, nil
}
// initViperConfigs initialize viper config using config file in '$HOME/.config/<cli name>/config.<json|yaml...>'
// currently hostname, scheme and auth tokens can be specified in this config file.
func initViperConfigs() {
if configFile != "" {
// use user specified config file location
viper.SetConfigFile(configFile)
}else{
// look for default config
// Find home directory.
home, err := homedir.Dir()
cobra.CheckErr(err)
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(path.Join(home, ".config", exeName))
viper.SetConfigName("config")
}
if err := viper.ReadInConfig(); err != nil {
logDebugf("Error: loading config file: %v", err)
return
}
logDebugf("Using config file: %v", viper.ConfigFileUsed())
}
{{- if .SecurityDefinitions }}
{{- /*youyuan: rework this since spec may define multiple auth schemes.
cli needs to detect which one user passed rather than add all of them.*/}}
// registerAuthInoWriterFlags registers all flags needed to perform authentication
func registerAuthInoWriterFlags(cmd *cobra.Command) error {
{{- range .SecurityDefinitions }}
/*{{.Name}} {{.Description}}*/
{{- if .IsBasicAuth }}
cmd.PersistentFlags().String("username", "", "username for basic auth")
viper.BindPFlag("username", cmd.PersistentFlags().Lookup("username"))
cmd.PersistentFlags().String("password", "", "password for basic auth")
viper.BindPFlag("password", cmd.PersistentFlags().Lookup("password"))
{{- end }}
{{- if .IsAPIKeyAuth }}
cmd.PersistentFlags().String("{{.Name}}", "", `{{.Description}}`)
viper.BindPFlag("{{.Name}}", cmd.PersistentFlags().Lookup("{{.Name}}"))
{{- end }}
{{- if .IsOAuth2 }}
// oauth2: let user provide the token in a flag, rather than implement the logic to fetch the token.
cmd.PersistentFlags().String("oauth2-token", "", `{{.Description}}`)
viper.BindPFlag("oauth2-token", cmd.PersistentFlags().Lookup("oauth2-token"))
{{- end }}
{{- end }}
return nil
}
// makeAuthInfoWriter retrieves cmd flags and construct an auth info writer
func makeAuthInfoWriter(cmd *cobra.Command) (runtime.ClientAuthInfoWriter, error) {
auths := []runtime.ClientAuthInfoWriter{}
{{- range .SecurityDefinitions }}
/*{{.Name}} {{.Description}}*/
{{- if .IsBasicAuth }}
if viper.IsSet("username") {
usr := viper.GetString("username")
if !viper.IsSet("password"){
return nil, fmt.Errorf("Basic Auth password for user [%v] is not provided.", usr)
}
pwd := viper.GetString("password")
auths = append(auths, httptransport.BasicAuth(usr,pwd))
}
{{- end }}
{{- if .IsAPIKeyAuth }}
if viper.IsSet("{{.Name}}") {
{{ pascalize .Name }}Key := viper.GetString("{{.Name}}")
auths = append(auths, httptransport.APIKeyAuth("{{.Name}}", "{{.In}}", {{ pascalize .Name }}Key))
}
{{- end }}
{{- if .IsOAuth2 }}
if viper.IsSet("oauth2-token") {
// oauth2 workflow for generated CLI is not ideal.
// If you have suggestions on how to support it, raise an issue here: https://github.com/go-swagger/go-swagger/issues
// This will be added to header: "Authorization: Bearer {oauth2-token value}"
token := viper.GetString("oauth2-token")
auths = append(auths, httptransport.BearerToken(token))
}
{{- end }}
{{- end }}
if len(auths) == 0 {
logDebugf("Warning: No auth params detected.")
return nil, nil
}
// compose all auths together
return httptransport.Compose(auths...), nil
}
{{- end }}
{{ range .OperationGroups -}}
func makeOperationGroup{{ pascalize .Name }}Cmd() (*cobra.Command, error) {
{{- $operationGroupCmdVarName := printf "operationGroup%vCmd" (pascalize .Name) }}
{{ $operationGroupCmdVarName }} := &cobra.Command{
Use: "{{ .Name }}",
Long: `{{ .Description }}`,
}
{{ range .Operations }}
{{- $operationCmdVarName := printf "operation%vCmd" (pascalize .Name) }}
{{ $operationCmdVarName }}, err := makeOperation{{pascalize .Package}}{{ pascalize .Name }}Cmd()
if err != nil {
return nil, err
}
{{ $operationGroupCmdVarName }}.AddCommand({{ $operationCmdVarName }})
{{ end }}
return {{ $operationGroupCmdVarName }}, nil
}
{{ end }} {{/*operation group*/}}

View file

@ -0,0 +1,77 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .GenOpts.CliPackage }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import(
"github.com/spf13/cobra"
)
func makeGenCompletionCmd() *cobra.Command{
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `To load completions:
Bash:
$ source <(yourprogram completion bash)
# To load completions for each session, execute once:
# Linux:
$ yourprogram completion bash > /etc/bash_completion.d/yourprogram
# macOS:
$ yourprogram completion bash > /usr/local/etc/bash_completion.d/yourprogram
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ yourprogram completion zsh > "${fpath[1]}/_yourprogram"
# You will need to start a new shell for this setup to take effect.
fish:
$ yourprogram completion fish | source
# To load completions for each session, execute once:
$ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish
PowerShell:
PS> yourprogram completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> yourprogram completion powershell > yourprogram.ps1
# and source this file from your PowerShell profile.
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
return completionCmd
}

View file

@ -0,0 +1,28 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package main
import (
"encoding/json"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
func main() {
rootCmd,err := cli.MakeRootCmd()
if err != nil {
fmt.Println("Cmd construction error: ", err)
os.Exit(1)
}
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}

View file

@ -0,0 +1,25 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package cli
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
{{ imports .DefaultImports }}
{{ imports .Imports }}
"github.com/spf13/cobra"
)
// Schema cli for {{.GoType}}
{{ template "modelschemacli" .}}
{{ range .ExtraSchemas }}
// Extra schema cli for {{.GoType}}
{{ template "modelschemacli" .}}
{{ end }}

View file

@ -0,0 +1,230 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
{{- /*TODO: do not hardcode cli pkg*/}}
package cli
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
{{ imports .DefaultImports }}
{{ imports .Imports }}
"github.com/spf13/cobra"
"github.com/go-openapi/runtime"
"github.com/go-openapi/swag"
httptransport "github.com/go-openapi/runtime/client"
)
// makeOperation{{pascalize .Package}}{{ pascalize .Name }}Cmd returns a cmd to handle operation {{ camelize .Name }}
func makeOperation{{pascalize .Package}}{{ pascalize .Name }}Cmd() (*cobra.Command, error) {
cmd := &cobra.Command{
Use: "{{ .Name }}",
Short: `{{ escapeBackticks .Description}}`,
RunE: runOperation{{pascalize .Package}}{{ pascalize .Name }},
}
if err := registerOperation{{pascalize .Package}}{{ pascalize .Name }}ParamFlags(cmd); err != nil{
return nil, err
}
return cmd, nil
}
{{ $operationGroup := .Package }}
{{ $operation := .Name }}
{{ $operationPkgAlias := .PackageAlias }}
// runOperation{{pascalize $operationGroup }}{{ pascalize $operation }} uses cmd flags to call endpoint api
func runOperation{{pascalize $operationGroup }}{{ pascalize $operation }}(cmd *cobra.Command, args []string) error{
appCli, err := makeClient(cmd, args)
if err != nil {
return err
}
// retrieve flag values from cmd and fill params
params := {{ .PackageAlias }}.New{{ pascalize .Name}}Params()
{{- range .Params }}
if err, _ := retrieveOperation{{pascalize $operationGroup }}{{ pascalize $operation }}{{ pascalize .Name }}Flag(params, "", cmd); err != nil{
return err
}
{{- end }} {{/*Params*/}}
if dryRun {
{{/* Note: dry run is not very useful for now, but useful when validation is added in future*/}}
logDebugf("dry-run flag specified. Skip sending request.")
return nil
}
// make request and then print result
{{- /*Package string is the operation name*/}}
msgStr, err := parseOperation{{pascalize .Package}}{{ pascalize .Name }}Result(appCli.{{- pascalize .Package }}.{{ pascalize .Name }}(params {{- if .Authorized}}, nil{{ end }}{{ if .HasStreamingResponse }}, &bytes.Buffer{}{{ end }}))
if err != nil {
return err
}
if !debug{
{{/* In debug mode content should have been printed in transport layer, so do not print again*/}}
fmt.Println(msgStr)
}
return nil
}
// registerOperation{{pascalize $operationGroup }}{{ pascalize $operation }}ParamFlags registers all flags needed to fill params
func registerOperation{{pascalize $operationGroup }}{{ pascalize $operation }}ParamFlags(cmd *cobra.Command) error {
{{- range .Params }}
if err := registerOperation{{pascalize $operationGroup }}{{ pascalize $operation }}{{pascalize .Name }}ParamFlags("", cmd); err != nil{
return err
}
{{- end }}
return nil
}
{{/*register functions for each fields in this operation*/}}
{{- range .Params }}
func registerOperation{{pascalize $operationGroup }}{{ pascalize $operation }}{{pascalize .Name }}ParamFlags(cmdPrefix string, cmd *cobra.Command) error{
{{- if .IsPrimitive }}
{{ template "primitiveregistrator" . }}
{{- else if .IsArray }}
{{ template "arrayregistrator" . }}
{{- else if and .IsBodyParam .Schema (not .IsArray) (not .IsMap) (not .IsStream) }}
{{ template "modelparamstringregistrator" . }}
{{ template "modelparamregistrator" . }}
{{/* Do not mark body flag as required, since the individial flag for body field will be added separately */}}
{{- else }}
// warning: go type {{ .GoType }} is not supported by go-swagger cli yet.
{{- end }}
return nil
}
{{- end }}
{{/*functions to retreive each field of params*/}}
{{- range .Params }}
func retrieveOperation{{pascalize $operationGroup }}{{ pascalize $operation }}{{ pascalize .Name }}Flag(m *{{ $operationPkgAlias }}.{{ pascalize $operation }}Params, cmdPrefix string, cmd *cobra.Command) (error,bool){
retAdded := false
{{- $flagStr := .Name }}
{{- $flagValueVar := printf "%vValue" (camelize .Name) }}
{{- /*only set the param if user set the flag*/}}
if cmd.Flags().Changed("{{ $flagStr }}") {
{{- if .IsPrimitive }}
{{ template "primitiveretriever" . }}
{{- else if .IsArray }}
{{ template "arrayretriever" . }}
{{- else if .IsMap }}
// warning: {{ .Name }} map type {{.GoType}} is not supported by go-swagger cli yet
{{- else if and .IsBodyParam .Schema .IsComplexObject (not .IsStream) }}
{{- /*schema payload can be passed in cmd as a string and here is unmarshalled to model struct and attached in params*/}}
// Read {{ $flagStr }} string from cmd and unmarshal
{{ $flagValueVar }}Str, err := cmd.Flags().GetString("{{ $flagStr }}")
if err != nil {
return err, false
}
{{/*Note anonymous body schema is not pointer*/}}
{{ $flagValueVar }} := {{if containsPkgStr .GoType}}{{ .GoType }}{{else}}{{ .Pkg }}.{{.GoType}}{{ end }}{}
if err := json.Unmarshal([]byte({{ $flagValueVar }}Str), &{{ $flagValueVar }}); err!= nil{
return fmt.Errorf("cannot unmarshal {{ $flagStr }} string in {{.GoType}}: %v", err), false
}
m.{{ .ID }} = {{- if .IsNullable }}&{{- end }}{{ $flagValueVar }}
{{- else }}
// warning: {{.GoType}} is not supported by go-swagger cli yet
{{- end }} {{/*end go type case*/}}
}
{{- if and .IsBodyParam .Schema .IsComplexObject (not .IsArray) (not .IsMap) (not .IsStream) }}
{{- /* Add flags to capture fields in Body. If previously Body struct was constructed in unmarshalling body string,
then reuse the struct, otherwise construct an empty value struct to fill. Here body field flags overwrites
unmarshalled body string values. */}}
{{- $flagModelVar := printf "%vModel" (camelize $flagValueVar) }}
{{ $flagModelVar }} := m.{{ .ID }}
if swag.IsZero({{ $flagModelVar }}){
{{ $flagModelVar }} = {{- if .IsNullable }}&{{- end }}{{if containsPkgStr .GoType}}{{ .GoType }}{{else}}{{ .Pkg }}.{{.GoType}}{{ end }}{}
}
{{- /*Only attach the body struct in params if user passed some flag filling some body fields.*/}}
{{- /* add "&" to $flagModelVar when it is not nullable because the retrieve method always expects a pointer */}}
err, added := retrieveModel{{ pascalize (dropPackage .GoType) }}Flags(0, {{if not .IsNullable}}&{{end}}{{ $flagModelVar }}, "{{ camelize (dropPackage .GoType) }}", cmd)
if err != nil{
return err, false
}
if added {
m.{{.ID}} = {{ $flagModelVar }}
}
if dryRun && debug {
{{/* dry run we don't get trasnport debug strings, so print it here*/}}
{{- $bodyDebugVar := printf "%vDebugBytes" (camelize $flagValueVar) }}
{{ $bodyDebugVar }}, err := json.Marshal(m.{{.ID}})
if err != nil{
return err, false
}
logDebugf("{{.ID }} dry-run payload: %v", string({{ $bodyDebugVar }}))
}
retAdded = retAdded || added
{{/*body debug string will be printed in transport layer*/}}
{{- end }}
return nil, retAdded
}
{{- end }} {{/*Params*/}}
// parseOperation{{pascalize .Package}}{{ pascalize .Name }}Result parses request result and return the string content
{{- /*TODO: handle multiple success response case*/}}
func parseOperation{{pascalize .Package}}{{ pascalize .Name }}Result({{- if .SuccessResponse }}{{ range $i, $v := .SuccessResponses }} resp{{$i}} *{{$v.Package}}.{{pascalize $v.Name}},{{- end }}{{- end }} respErr error) (string, error){
if respErr != nil {
{{- /*error is of type default model. If we can cast, then print the resp.*/}}
{{ if .DefaultResponse }} {{with .DefaultResponse}}
{{ if .Schema }}
var iRespD interface{} = respErr
respD, ok := iRespD.(*{{ .Package }}.{{ pascalize .Name }})
if ok {
if !swag.IsZero(respD) && !swag.IsZero(respD.Payload) {
msgStr,err := json.Marshal(respD.Payload)
if err != nil{
return "", err
}
return string(msgStr), nil
}
}
{{ else }}
// Non schema case: warning {{.Name}} is not supported
{{ end }}
{{ end }} {{ end }}
{{- range $i, $v := .Responses }}
{{ if .Schema }}
var iResp{{$i}} interface{} = respErr
resp{{$i}}, ok := iResp{{$i}}.(*{{ .Package }}.{{ pascalize .Name }})
if ok {
if !swag.IsZero(resp{{$i}}) && !swag.IsZero(resp{{$i}}.Payload) {
msgStr,err := json.Marshal(resp{{$i}}.Payload)
if err != nil{
return "", err
}
return string(msgStr), nil
}
}
{{ else }}
// Non schema case: warning {{.Name}} is not supported
{{ end }}
{{ end }}
return "", respErr
}
{{- range $i, $v := .SuccessResponses }}
{{ if .Schema }}
{{- with .Schema}}
if !swag.IsZero(resp{{$i}}) && !swag.IsZero(resp{{$i}}.Payload) {
{{- if or .IsComplexObject .IsArray .IsMap }}
msgStr,err := json.Marshal(resp{{$i}}.Payload)
if err != nil{
return "", err
}
{{- else }}
msgStr := fmt.Sprintf("%v", resp{{$i}}.Payload)
{{- end }}
return string(msgStr), nil
}
{{- end }}
{{ else }}
// warning: non schema response {{.Name}} is not supported by go-swagger cli yet.
{{ end }}
{{ end }}
return "", nil
}
{{/*for models defined in params, generate their register and retrieve flags functions*/}}
{{- range .ExtraSchemas }}
{{ template "modelschemacli" . }}
{{- end}}

View file

@ -0,0 +1,97 @@
{{/*util functions to run or register cmd flags*/}}
{{ define "flagdescriptionvar" }}
{{- $fullDescription := (escapeBackticks .Description) }}
{{- if .Required}}
{{- $fullDescription = printf "Required. %v" $fullDescription}}
{{- end}}
{{- if .Enum }}
{{- $fullDescription = printf "Enum: %v. %v" (json .Enum) $fullDescription}}
{{- end }}
{{ camelize .Name }}Description := `{{ $fullDescription }}`
{{ end }}
{{ define "flagnamevar" }}
{{- $flagNameVar := printf "%vFlagName" (camelize .Name) }}
var {{ $flagNameVar }} string
if cmdPrefix == "" {
{{ $flagNameVar }} = "{{ .Name }}"
}else{
{{ $flagNameVar }} = fmt.Sprintf("%v.{{ .Name }}", cmdPrefix)
}
{{ end }}
{{ define "flagdefaultvar" }}
{{ $defaultVar := printf "%vFlagDefault" (camelize .Name) }}
var {{ $defaultVar}} {{ .GoType }} {{ if .Default }}= {{ printf "%#v" .Default }}{{ end }}
{{ end }}
{{/* Not used. CLI does not mark flag as required, and required will be checked by validation in future */}}
{{/* {{ define "requiredregistrator" }}
if err := cmd.MarkPersistentFlagRequired({{ camelize .Name }}FlagName); err != nil{
return err
}
{{ end }} */}}
{{ define "enumcompletion" }} {{/*only used for primitive types. completion type is always string.*/}}
{{ if .Enum }}
if err := cmd.RegisterFlagCompletionFunc({{ camelize .Name }}FlagName,
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var res []string
if err := json.Unmarshal([]byte(`{{ json .Enum }}`), &res); err != nil {
panic(err)
}
return res, cobra.ShellCompDirectiveDefault
}); err != nil{
return err
}
{{ end }}
{{ end }}
{{/* intended to be used on struct GenSchema with .IsPrimitive */}}
{{ define "primitiveregistrator" }}
{{- if or (eq .GoType "int64") (eq .GoType "int32") (eq .GoType "string") (eq .GoType "float64") (eq .GoType "float32") (eq .GoType "bool") }}
{{ template "flagdescriptionvar" . }}
{{ template "flagnamevar" . }}
{{ template "flagdefaultvar" . }}
_ = cmd.PersistentFlags().{{ pascalize .GoType }}({{ camelize .Name }}FlagName, {{ camelize .Name }}FlagDefault, {{ (camelize .Name) }}Description)
{{ template "enumcompletion" . }}
{{- else if or (eq .GoType "strfmt.DateTime") (eq .GoType "strfmt.UUID") (eq .GoType "strfmt.ObjectId") }} {{/* read as string */}}
{{ template "flagdescriptionvar" . }}
{{ template "flagnamevar" . }}
_ = cmd.PersistentFlags().String({{ camelize .Name }}FlagName, "", {{ (camelize .Name) }}Description)
{{ template "enumcompletion" . }}
{{- else }}
// warning: primitive {{.Name}} {{.GoType }} is not supported by go-swagger cli yet
{{- end }}
{{ end }}
{{ define "arrayregistrator" }}
{{- if or (eq .GoType "[]int64") (eq .GoType "[]int32") (eq .GoType "[]string") (eq .GoType "[]float64") (eq .GoType "[]float32") (eq .GoType "[]bool") }}
{{ template "flagdescriptionvar" . }}
{{ template "flagnamevar" . }}
{{ template "flagdefaultvar" . }}
_ = cmd.PersistentFlags().{{ pascalize .GoType }}Slice({{ camelize .Name }}FlagName, {{ camelize .Name }}FlagDefault, {{ (camelize .Name) }}Description)
{{ template "enumcompletion" . }}
{{- else if or (eq .GoType "[]strfmt.DateTime") (eq .GoType "[]strfmt.UUID") (eq .GoType "[]strfmt.ObjectId") }} {{/* read as string */}}
{{ template "flagdescriptionvar" . }}
{{ template "flagnamevar" . }}
_ = cmd.PersistentFlags().StringSlice({{ camelize .Name }}FlagName, []string{}, {{ (camelize .Name) }}Description)
{{- else }}
// warning: array {{.Name}} {{.GoType }} is not supported by go-swagger cli yet
{{- end }}
{{ end }}
{{/* each body parameter gets a string flag to input json raw string */}}
{{ define "modelparamstringregistrator" }}
{{ template "flagnamevar" . }}
_ = cmd.PersistentFlags().String({{ camelize .Name }}FlagName, "", "Optional json string for [{{ .Name }}]. {{ .Description }}")
{{ end }}
{{ define "modelparamregistrator" }} {{/* register a param that has a schema */}}
// add flags for body {{/*use go type as the flag prefix. There is no good way to determine the original str case in spec*/}}
if err := registerModel{{ pascalize (dropPackage .GoType) }}Flags(0, "{{ camelize (dropPackage .GoType) }}", cmd); err != nil {
return err
}
{{ end }}

View file

@ -0,0 +1,59 @@
{{/*util functions to retrieve flags*/}}
{{ define "primitiveretriever" }}
{{- $flagValueVar := printf "%vFlagValue" (camelize .Name) }}
{{- $flagNameVar := printf "%vFlagName" (camelize .Name )}}
{{- if or (eq .GoType "int64") (eq .GoType "int32") (eq .GoType "string") (eq .GoType "float64") (eq .GoType "float32") (eq .GoType "bool") }}
{{ template "flagnamevar" . }}
{{ $flagValueVar }}, err := cmd.Flags().Get{{pascalize .GoType}}({{ $flagNameVar }})
if err != nil{
return err, false
}
{{- /* reciever by convention is m for CLI */}}
m.{{ pascalize .Name }} = {{- if .IsNullable }}&{{- end }}{{ $flagValueVar }}
{{- else if or (eq .GoType "strfmt.DateTime") (eq .GoType "strfmt.ObjectId") (eq .GoType "strfmt.UUID" ) }} {{/*Get flag value as string, then parse it*/}}
{{/*Many of the strfmt types can be added here*/}}
{{ template "flagnamevar" . }}
{{ $flagValueVar }}Str, err := cmd.Flags().GetString({{ $flagNameVar }})
if err != nil{
return err, false
}
var {{ $flagValueVar }} {{ .GoType }}
if err := {{ $flagValueVar }}.UnmarshalText([]byte({{ $flagValueVar }}Str)); err != nil{
return err, false
}
m.{{ pascalize .Name }} = {{- if .IsNullable }}&{{- end }}{{ $flagValueVar }}
{{- else }}
// warning: primitive {{.Name}} {{.GoType }} is not supported by go-swagger cli yet
{{- end }}
{{ end }}
{{ define "arrayretriever" }}
{{- $flagValueVar := printf "%vFlagValues" (camelize .Name) }}
{{- $flagNameVar := printf "%vFlagName" (camelize .Name )}}
{{- if or (eq .GoType "[]int64") (eq .GoType "[]int32") (eq .GoType "[]string") (eq .GoType "[]float64") (eq .GoType "[]float32") (eq .GoType "[]bool") }}
{{ template "flagnamevar" . }}
{{ $flagValueVar }}, err := cmd.Flags().Get{{pascalize .GoType}}Slice({{ $flagNameVar }})
if err != nil{
return err, false
}
{{- /* reciever by convention is m for CLI */}}
m.{{ pascalize .Name }} = {{ $flagValueVar }}
{{- else if or (eq .GoType "[]strfmt.DateTime") (eq .GoType "[]strfmt.ObjectId") (eq .GoType "[]strfmt.UUID") }} {{/*Get flag value as string, then parse it*/}}
{{ template "flagnamevar" . }}
{{ $flagValueVar }}Str, err := cmd.Flags().GetStringSlice({{ $flagNameVar }})
if err != nil{
return err, false
}
{{ $flagValueVar }} := make({{ .GoType }}, len({{ $flagValueVar }}Str))
for i, v := range {{ $flagValueVar }}Str {
if err := {{ $flagValueVar }}[i].UnmarshalText([]byte(v)); err != nil{
return err, false
}
}
m.{{ pascalize .Name }} = {{- if .IsNullable }}&{{- end }}{{ $flagValueVar }}
{{- else }}
// warning: array {{.Name}} {{.GoType }} is not supported by go-swagger cli yet
{{- end }}
{{ end }}

View file

@ -0,0 +1,193 @@
{{/*util functions to generate register and retrieve functions for a model*/}}
{{ define "modelschemacli" }}
{{/*some guards to prevent rendering unsupported models types. TODO: remove this guard*/}}
{{if or .IsPrimitive .IsComplexObject }}
{{ template "modelschemacliinternal" . }}
{{ else }}
// Name: [{{.Name}}], Type:[{{ .GoType }}], register and retrieve functions are not rendered by go-swagger cli
{{ end }}
{{ end }}
{{/*since register and retrieve are the same for properties and all of, share them here*/}}
{{ define "propertyregistor" }}
{{- if .IsPrimitive }}
{{ template "primitiveregistrator" . }}
{{- else if .IsArray }}
// warning: {{.Name}} {{ .GoType }} array type is not supported by go-swagger cli yet
{{- else if .IsMap }}
// warning: {{.Name}} {{ .GoType }} map type is not supported by go-swagger cli yet
{{- else if .IsComplexObject }} {{/* struct case */}}
{{ template "flagnamevar" . }}
if err := registerModel{{pascalize (dropPackage .GoType) }}Flags(depth + 1, {{ camelize .Name }}FlagName, cmd); err != nil{
return err
}
{{- else }}
// warning: {{.Name}} {{ .GoType }} unkown type is not supported by go-swagger cli yet
{{- end }}
{{ end }}
{{ define "propertyretriever" }}
{{- $flagNameVar := printf "%vFlagName" (camelize .Name) }}
{{- $flagValueVar := printf "%vFlagValue" (camelize .Name) }}
{{ $flagNameVar }} := fmt.Sprintf("%v.{{ .Name }}", cmdPrefix)
if cmd.Flags().Changed({{ $flagNameVar }}) {
{{- if .IsPrimitive }}
{{ template "primitiveretriever" . }}
retAdded = true
{{- else if .IsArray }}
// warning: {{ .Name }} array type {{ .GoType }} is not supported by go-swagger cli yet
{{- else if .IsMap }}
// warning: {{ .Name }} map type {{ .GoType }} is not supported by go-swagger cli yet
{{- else if .IsComplexObject }}
// info: complex object {{.Name}} {{.GoType}} is retrieved outside this Changed() block
{{- else }}
// warning: {{.Name}} {{ .GoType }} unkown type is not supported by go-swagger cli yet
{{- end }}
}
{{- if and .IsComplexObject (not .IsArray) (not .IsMap) (not .IsStream) }}
{{ $flagValueVar }} := m.{{pascalize .Name}}
if swag.IsZero({{ $flagValueVar }}){
{{ $flagValueVar }} = {{if .IsNullable }}&{{end}}{{if containsPkgStr .GoType}}{{ .GoType }}{{else}}{{ .Pkg }}.{{.GoType}}{{ end }}{}
}
{{/* always lift the payload to pointer and pass to model retrieve function. If .GoType has pkg str, use it, else use .Pkg+.GoType */}}
err, {{camelize .Name }}Added := retrieveModel{{pascalize (dropPackage .GoType) }}Flags(depth + 1, {{if not .IsNullable }}&{{end}}{{ $flagValueVar }}, {{ $flagNameVar }}, cmd)
if err != nil{
return err, false
}
retAdded = retAdded || {{camelize .Name }}Added
if {{camelize .Name }}Added {
m.{{pascalize .Name}} = {{ $flagValueVar }}
}
{{- end }}
{{ end }}
{{ define "modelschemacliinternal" }} {{/*used by model definition and in params model*/}}
{{- $modelName := .Name }}
{{/*model package is filled by generator*/}}
{{ $modelPkg := toPackageName .Pkg}}
{{ $modelType := .GoType }}
// register flags to command
func registerModel{{pascalize .Name}}Flags(depth int, cmdPrefix string, cmd *cobra.Command) error {
{{ range .AllOf }}
{{- if not .IsAnonymous }}{{/* named type composition */}}
{{ if or .IsPrimitive .IsComplexObject }}
// register embedded {{ .GoType }} flags
{{/*defer all of registration to the model's regristor method. embed should not lift cmdPrefix */}}
if err := registerModel{{ pascalize (dropPackage .GoType) }}Flags(depth, cmdPrefix, cmd); err != nil{
return err
}
{{ else }}
// {{ .Name }} {{ .GoType }} register is skipped
{{ end }}
{{ else }}{{/*inline definition. assume only properties are used*/}}
// register anonymous fields for {{.Name}}
{{ $anonName := .Name }}
{{ range .Properties }}
if err := register{{ pascalize $modelName }}Anon{{pascalize $anonName }}{{ pascalize .Name }}(depth, cmdPrefix, cmd); err != nil{
return err
}
{{ end }}
{{ end }}
{{ end }}
{{ range .Properties }}
if err := register{{ pascalize $modelName }}{{ pascalize .Name }}(depth, cmdPrefix, cmd); err != nil{
return err
}
{{ end }}
return nil
}
{{ range .AllOf }}
{{- if .IsAnonymous }}{{/* inline definition. schema case is defered. */}}
// inline definition name {{ .Name }}, type {{.GoType}}
{{ $anonName := .Name }}
{{ range .Properties }}
func register{{ pascalize $modelName }}Anon{{pascalize $anonName }}{{ pascalize .Name }}(depth int, cmdPrefix string, cmd *cobra.Command) error {
if depth > maxDepth {
return nil
}
{{ template "propertyregistor" . }}
return nil
}
{{ end }}
{{ end }}
{{ end }}
{{/*register functions for each fields in this model */}}
{{ range .Properties }}
func register{{ pascalize $modelName }}{{ pascalize .Name }}(depth int, cmdPrefix string, cmd *cobra.Command) error{
if depth > maxDepth {
return nil
}
{{ template "propertyregistor" .}}
return nil
}
{{ end }} {{/*Properties*/}}
// retrieve flags from commands, and set value in model. Return true if any flag is passed by user to fill model field.
func retrieveModel{{pascalize $modelName }}Flags(depth int, m *{{if containsPkgStr .GoType}}{{ .GoType }}{{else}}{{ .Pkg }}.{{.GoType}}{{ end }}, cmdPrefix string, cmd *cobra.Command) (error, bool) {
retAdded := false
{{ range .AllOf }}
{{- if not .IsAnonymous }}{{/* named type composition */}}
{{ if or .IsPrimitive .IsComplexObject }}
// retrieve model {{.GoType}}
err, {{camelize .Name }}Added := retrieveModel{{ pascalize (dropPackage .GoType) }}Flags(depth, &m.{{pascalize (dropPackage .GoType) }}, cmdPrefix, cmd)
if err != nil{
return err, false
}
retAdded = retAdded || {{camelize .Name }}Added
{{ else }} {{/*inline anonymous case*/}}
{{ end }}
{{- else }}
// retrieve allOf {{.Name}} fields
{{ $anonName := .Name }}
{{ range .Properties }}
err, {{camelize .Name}}Added := retrieve{{ pascalize $modelName }}Anon{{pascalize $anonName }}{{ pascalize .Name }}Flags(depth, m, cmdPrefix, cmd)
if err != nil{
return err, false
}
retAdded = retAdded || {{ camelize .Name }}Added
{{ end }}
{{- end }}
{{ end }}
{{ range .Properties }}
err, {{ camelize .Name }}Added := retrieve{{pascalize $modelName }}{{pascalize .Name }}Flags(depth, m, cmdPrefix, cmd)
if err != nil{
return err, false
}
retAdded = retAdded || {{ camelize .Name }}Added
{{ end }}
return nil, retAdded
}
{{ range .AllOf }}
{{- if .IsAnonymous }}{{/* inline definition. schema case is defered. */}}
// define retrieve functions for fields for inline definition name {{ .Name }}
{{ $anonName := .Name }}
{{ range .Properties }} {{/*anonymous fields will be registered directly on parent model*/}}
func retrieve{{ pascalize $modelName }}Anon{{pascalize $anonName }}{{ pascalize .Name }}Flags(depth int, m *{{if containsPkgStr $modelType}}{{ $modelType }}{{else}}{{ $modelPkg }}.{{$modelType}}{{ end }},cmdPrefix string, cmd *cobra.Command) (error,bool) {
if depth > maxDepth {
return nil, false
}
retAdded := false
{{ template "propertyretriever" . }}
return nil, retAdded
}
{{ end }}
{{ end }}
{{ end }}
{{ range .Properties }}
func retrieve{{pascalize $modelName }}{{pascalize .Name }}Flags(depth int, m *{{if $modelPkg}}{{$modelPkg}}.{{ dropPackage $modelType }}{{else}}{{ $modelType }}{{end}}, cmdPrefix string, cmd *cobra.Command) (error, bool) {
if depth > maxDepth {
return nil, false
}
retAdded := false
{{ template "propertyretriever" . }}
return nil, retAdded
}
{{ end }} {{/*properties*/}}
{{ end }} {{/*define*/}}

View file

@ -0,0 +1,127 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Name }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// New creates a new {{ humanize .Name }} API client.
func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService {
return &Client{transport: transport, formats: formats}
}
/*
Client {{ if .Summary }}{{ .Summary }}{{ if .Description }}
{{ blockcomment .Description }}{{ end }}{{ else if .Description}}{{ blockcomment .Description }}{{ else }}for {{ humanize .Name }} API{{ end }}
*/
type Client struct {
transport runtime.ClientTransport
formats strfmt.Registry
}
// ClientOption is the option for Client methods
type ClientOption func(*runtime.ClientOperation)
// ClientService is the interface for Client methods
type ClientService interface {
{{ range .Operations }}
{{ pascalize .Name }}(params *{{ pascalize .Name }}Params{{ if .Authorized }}, authInfo runtime.ClientAuthInfoWriter{{end}}{{ if .HasStreamingResponse }}, writer io.Writer{{ end }}, opts ...ClientOption) {{ if .SuccessResponse }}({{ range .SuccessResponses }}*{{ pascalize .Name }}, {{ end }}{{ end }}error{{ if .SuccessResponse }}){{ end }}
{{ end }}
SetTransport(transport runtime.ClientTransport)
}
{{ range .Operations }}
/*
{{ pascalize .Name }} {{ if .Summary }}{{ pluralizeFirstWord (humanize .Summary) }}{{ if .Description }}
{{ blockcomment .Description }}{{ end }}{{ else if .Description}}{{ blockcomment .Description }}{{ else }}{{ humanize .Name }} API{{ end }}
*/
func (a *Client) {{ pascalize .Name }}(params *{{ pascalize .Name }}Params{{ if .Authorized }}, authInfo runtime.ClientAuthInfoWriter{{end}}{{ if .HasStreamingResponse }}, writer io.Writer{{ end }}, opts ...ClientOption) {{ if .SuccessResponse }}({{ range .SuccessResponses }}*{{ pascalize .Name }}, {{ end }}{{ end }}error{{ if .SuccessResponse }}){{ end }} {
// TODO: Validate the params before sending
if params == nil {
params = New{{ pascalize .Name }}Params()
}
op := &runtime.ClientOperation{
ID: {{ printf "%q" .Name }},
Method: {{ printf "%q" .Method }},
PathPattern: {{ printf "%q" .Path }},
ProducesMediaTypes: {{ printf "%#v" .ProducesMediaTypes }},
ConsumesMediaTypes: {{ printf "%#v" .ConsumesMediaTypes }},
Schemes: {{ printf "%#v" .Schemes }},
Params: params,
Reader: &{{ pascalize .Name }}Reader{formats: a.formats{{ if .HasStreamingResponse }}, writer: writer{{ end }}},{{ if .Authorized }}
AuthInfo: authInfo,{{ end}}
Context: params.Context,
Client: params.HTTPClient,
}
for _, opt := range opts {
opt(op)
}
{{ $length := len .SuccessResponses }}
{{ if .SuccessResponse }}result{{else}}_{{ end }}, err := a.transport.Submit(op)
if err != nil {
return {{ if .SuccessResponse }}{{ padSurround "nil" "nil" 0 $length }}, {{ end }}err
}
{{- if .SuccessResponse }}
{{- if eq $length 1 }}
success, ok := result.(*{{ pascalize .SuccessResponse.Name }})
if ok {
return success,nil
}
// unexpected success response
{{- if .DefaultResponse }}{{/* if a default response is provided, fill this and return an error */}}
unexpectedSuccess := result.(*{{ pascalize .DefaultResponse.Name }})
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
{{- else }}
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
msg := fmt.Sprintf("unexpected success response for {{ .Name }}: API contract not enforced by server. Client expected to get an error, but got: %T", result)
panic(msg)
{{- end }}
{{- else }}{{/* several possible success responses */}}
switch value := result.(type) {
{{- range $i, $v := .SuccessResponses }}
case *{{ pascalize $v.Name }}:
return {{ padSurround "value" "nil" $i $length }}, nil
{{- end }}
}
{{- if .DefaultResponse }}{{/* if a default response is provided, fill this and return an error */}}
// unexpected success response
unexpectedSuccess := result.(*{{ pascalize .DefaultResponse.Name }})
return {{ padSurround "nil" "nil" 0 $length }}, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
{{- else }}
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
msg := fmt.Sprintf("unexpected success response for {{ $.Name }}: API contract not enforced by server. Client expected to get an error, but got: %T", result)
panic(msg)
{{- end }}
{{- end }}
{{- else }}
return nil
{{- end }}
}
{{- end }}
// SetTransport changes the transport on the client
func (a *Client) SetTransport(transport runtime.ClientTransport) {
a.transport = transport
}

View file

@ -0,0 +1,129 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// Default {{ humanize .Name }} HTTP client.
var Default = NewHTTPClient(nil)
const (
// DefaultHost is the default Host
// found in Meta (info) section of spec file
DefaultHost string = {{ printf "%#v" .Host }}
// DefaultBasePath is the default BasePath
// found in Meta (info) section of spec file
DefaultBasePath string = {{ printf "%#v" .BasePath }}
)
// DefaultSchemes are the default schemes found in Meta (info) section of spec file
var DefaultSchemes = {{ printf "%#v" .Schemes }}
// NewHTTPClient creates a new {{ humanize .Name }} HTTP client.
func NewHTTPClient(formats strfmt.Registry) *{{ pascalize .Name }} {
return NewHTTPClientWithConfig(formats, nil)
}
// NewHTTPClientWithConfig creates a new {{ humanize .Name }} HTTP client,
// using a customizable transport config.
func NewHTTPClientWithConfig(formats strfmt.Registry, cfg *TransportConfig) *{{ pascalize .Name }} {
// ensure nullable parameters have default
if cfg == nil {
cfg = DefaultTransportConfig()
}
// create transport and client
transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes)
return New(transport, formats)
}
// New creates a new {{ humanize .Name }} client
func New(transport runtime.ClientTransport, formats strfmt.Registry) *{{ pascalize .Name }} {
// ensure nullable parameters have default
if formats == nil {
formats = strfmt.Default
}
cli := new({{ pascalize .Name }})
cli.Transport = transport
{{- range .OperationGroups }}
cli.{{ pascalize .Name }} = {{ .PackageAlias }}.New(transport, formats)
{{- end }}
return cli
}
// DefaultTransportConfig creates a TransportConfig with the
// default settings taken from the meta section of the spec file.
func DefaultTransportConfig() *TransportConfig {
return &TransportConfig {
Host: DefaultHost,
BasePath: DefaultBasePath,
Schemes: DefaultSchemes,
}
}
// TransportConfig contains the transport related info,
// found in the meta section of the spec file.
type TransportConfig struct {
Host string
BasePath string
Schemes []string
}
// WithHost overrides the default host,
// provided by the meta section of the spec file.
func (cfg *TransportConfig) WithHost(host string) *TransportConfig {
cfg.Host = host
return cfg
}
// WithBasePath overrides the default basePath,
// provided by the meta section of the spec file.
func (cfg *TransportConfig) WithBasePath(basePath string) *TransportConfig {
cfg.BasePath = basePath
return cfg
}
// WithSchemes overrides the default schemes,
// provided by the meta section of the spec file.
func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig {
cfg.Schemes = schemes
return cfg
}
// {{ pascalize .Name }} is a client for {{ humanize .Name }}
type {{ pascalize .Name }} struct {
{{ range .OperationGroups }}
{{ pascalize .Name }} {{ .PackageAlias }}.ClientService
{{ end }}
Transport runtime.ClientTransport
}
// SetTransport changes the transport on the client and all its subresources
func (c *{{pascalize .Name}}) SetTransport(transport runtime.ClientTransport) {
c.Transport = transport
{{- range .OperationGroups }}
c.{{ pascalize .Name }}.SetTransport(transport)
{{- end }}
}

View file

@ -0,0 +1,406 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"fmt"
"net/http"
"time"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
cr "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// New{{ pascalize .Name }}Params creates a new {{ pascalize .Name }}Params object,
// with the default timeout for this client.
//
// Default values are not hydrated, since defaults are normally applied by the API server side.
//
// To enforce default values in parameter, use SetDefaults or WithDefaults.
func New{{ pascalize .Name }}Params() *{{ pascalize .Name }}Params {
return &{{ pascalize .Name}}Params{
{{ camelize .TimeoutName }}: cr.DefaultTimeout,
}
}
// New{{ pascalize .Name }}ParamsWithTimeout creates a new {{ pascalize .Name }}Params object
// with the ability to set a timeout on a request.
func New{{ pascalize .Name }}ParamsWithTimeout(timeout time.Duration) *{{ pascalize .Name }}Params {
return &{{ pascalize .Name}}Params{
{{ camelize .TimeoutName }}: timeout,
}
}
// New{{ pascalize .Name }}ParamsWithContext creates a new {{ pascalize .Name }}Params object
// with the ability to set a context for a request.
func New{{ pascalize .Name }}ParamsWithContext(ctx context.Context) *{{ pascalize .Name }}Params {
return &{{ pascalize .Name}}Params{
Context: ctx,
}
}
// New{{ pascalize .Name }}ParamsWithHTTPClient creates a new {{ pascalize .Name }}Params object
// with the ability to set a custom HTTPClient for a request.
func New{{ pascalize .Name }}ParamsWithHTTPClient(client *http.Client) *{{ pascalize .Name }}Params {
return &{{ pascalize .Name}}Params{
HTTPClient: client,
}
}
/* {{ pascalize .Name }}Params contains all the parameters to send to the API endpoint
for the {{ humanize .Name }} operation.
Typically these are written to a http.Request.
*/
type {{ pascalize .Name }}Params struct {
{{- range .Params }}
{{- if .Description }}
/* {{ pascalize .Name }}.
{{ blockcomment .Description }}
{{- if or .SwaggerFormat .Default }}
{{ print "" }}
{{- if .SwaggerFormat }}
Format: {{ .SwaggerFormat }}
{{- end }}
{{- if .Default }}
Default: {{ json .Default }}
{{- end }}
{{- end }}
*/
{{- else }}
// {{ pascalize .Name }}.
{{- if or .SwaggerFormat .Default }}
//
{{- if .SwaggerFormat }}
// Format: {{ .SwaggerFormat }}
{{- end }}
{{- if .Default }}
// Default: {{ json .Default }}
{{- end }}
{{- end }}
{{- end }}
{{ pascalize .ID }} {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) (or .IsNullable ) }}*{{ end }}{{ if not .IsFileParam }}{{ .GoType }}{{ else }}runtime.NamedReadCloser{{ end }}
{{- end }}
{{ camelize .TimeoutName }} time.Duration
Context context.Context
HTTPClient *http.Client
}
// WithDefaults hydrates default values in the {{ humanize .Name }} params (not the query body).
//
// All values with no default are reset to their zero value.
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WithDefaults() *{{ pascalize .Name }}Params {
{{ .ReceiverName }}.SetDefaults()
return {{ .ReceiverName }}
}
// SetDefaults hydrates default values in the {{ humanize .Name }} params (not the query body).
//
// All values with no default are reset to their zero value.
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) SetDefaults() {
{{- if .Params.HasSomeDefaults }}
var (
{{- range .Params }}
{{- if .HasDefault }}
{{- if .IsFileParam }}
// NOTE: no default supported for file parameter {{ .ID }}
{{- else if .IsStream }}
// NOTE: no default supported for stream parameter {{ .ID }}
{{- else if not .IsBodyParam }}
{{ template "simpleschemaDefaultsvar" . }}
{{- end }}
{{- end }}
{{- end }}
)
{{- range .Params }}
{{- if and .HasDefault (not .IsFileParam) (not .IsStream) (not .IsBodyParam) }}
{{ template "simpleschemaDefaultsinit" . }}
{{- end }}
{{- end }}
val := {{ pascalize .Name }}Params{
{{- range .Params }}
{{- if and .HasDefault (not .IsFileParam) (not .IsStream) (not .IsBodyParam) }}
{{ pascalize .ID }}: {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (or .IsNullable ) }}&{{ end }}{{ varname .ID }}Default,
{{- end }}
{{- end }}
}
val.{{ camelize .TimeoutName }} = {{ .ReceiverName }}.{{ camelize .TimeoutName }}
val.Context = {{ .ReceiverName }}.Context
val.HTTPClient = {{ .ReceiverName }}.HTTPClient
*{{ .ReceiverName }} = val
{{- else }}
// no default values defined for this parameter
{{- end }}
}
// With{{ pascalize .TimeoutName }} adds the timeout to the {{ humanize .Name }} params
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) With{{ pascalize .TimeoutName }}(timeout time.Duration) *{{ pascalize .Name }}Params {
{{ .ReceiverName }}.Set{{ pascalize .TimeoutName }}(timeout)
return {{ .ReceiverName }}
}
// Set{{ pascalize .TimeoutName }} adds the timeout to the {{ humanize .Name }} params
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) Set{{ pascalize .TimeoutName }}(timeout time.Duration) {
{{ .ReceiverName }}.{{ camelize .TimeoutName }} = timeout
}
// WithContext adds the context to the {{ humanize .Name }} params
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WithContext(ctx context.Context) *{{ pascalize .Name }}Params {
{{ .ReceiverName }}.SetContext(ctx)
return {{ .ReceiverName }}
}
// SetContext adds the context to the {{ humanize .Name }} params
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) SetContext(ctx context.Context) {
{{ .ReceiverName }}.Context = ctx
}
// WithHTTPClient adds the HTTPClient to the {{ humanize .Name }} params
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WithHTTPClient(client *http.Client) *{{ pascalize .Name }}Params {
{{ .ReceiverName }}.SetHTTPClient(client)
return {{ .ReceiverName }}
}
// SetHTTPClient adds the HTTPClient to the {{ humanize .Name }} params
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) SetHTTPClient(client *http.Client) {
{{ .ReceiverName }}.HTTPClient = client
}
{{- range .Params }}
// With{{ pascalize .ID }} adds the {{ varname .Name }} to the {{ humanize $.Name }} params
func ({{ $.ReceiverName }} *{{ pascalize $.Name }}Params) With{{ pascalize .ID }}({{ varname .Name }} {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (not .IsStream) (or .IsNullable ) }}*{{ end }}{{ if not .IsFileParam }}{{ .GoType }}{{ else }}runtime.NamedReadCloser{{ end }}) *{{ pascalize $.Name }}Params {
{{ $.ReceiverName }}.Set{{ pascalize .ID }}({{ varname .Name }})
return {{ .ReceiverName }}
}
// Set{{ pascalize .ID }} adds the {{ camelize .Name }} to the {{ humanize $.Name }} params
func ({{ $.ReceiverName }} *{{ pascalize $.Name }}Params) Set{{ pascalize .ID }}({{ varname .Name }} {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (not .IsStream) (or .IsNullable ) }}*{{ end }}{{ if not .IsFileParam }}{{ .GoType }}{{ else }}runtime.NamedReadCloser{{ end }}) {
{{ $.ReceiverName }}.{{ pascalize .ID }} = {{ varname .Name }}
}
{{- end }}
// WriteToRequest writes these params to a swagger request
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
if err := r.SetTimeout({{ .ReceiverName }}.{{ camelize .TimeoutName }}); err != nil {
return err
}
var res []error
{{- range .Params }}
{{- if not (or .IsArray .IsMap .IsBodyParam) }}
{{- if and .IsNullable (not .AllowEmptyValue) }}
if {{ .ValueExpression }} != nil {
{{- end}}
{{- if .IsQueryParam }}
// query param {{ .Name }}
{{- if .IsNullable }}
var qr{{ pascalize .Name }} {{ .GoType }}
if {{ .ValueExpression }} != nil {
qr{{ pascalize .Name }} = *{{ .ValueExpression }}
}
{{- else }}
qr{{ pascalize .Name }} := {{ .ValueExpression }}
{{- end}}
q{{ pascalize .Name}} := {{ if .Formatter }}{{ .Formatter }}(qr{{ pascalize .Name }}){{ else }}qr{{ pascalize .Name }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
{{- if not .AllowEmptyValue }}
if q{{ pascalize .Name }} != "" {
{{- end }}
if err := r.SetQueryParam({{ printf "%q" .Name }}, q{{ pascalize .Name }}); err != nil {
return err
}
{{- if not .AllowEmptyValue }}
}
{{- end }}
{{- else if .IsPathParam }}
// path param {{ .Name }}
if err := r.SetPathParam({{ printf "%q" .Name }}, {{ if .Formatter }}{{ .Formatter }}({{ if .IsNullable }}*{{end}}{{ .ValueExpression }}){{ else }}{{ if and (not .IsCustomFormatter) .IsNullable }}*{{end}}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}); err != nil {
return err
}
{{- else if .IsHeaderParam }}
// header param {{ .Name }}
if err := r.SetHeaderParam({{ printf "%q" .Name }}, {{ if .Formatter }}{{ .Formatter }}({{ if .IsNullable }}*{{end}}{{ .ValueExpression }}){{ else }}{{ if and (not .IsCustomFormatter) .IsNullable }}*{{end}}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}); err != nil {
return err
}
{{- else if .IsFormParam }}
{{- if .IsFileParam }}
{{- if .IsNullable }}
if {{ .ValueExpression }} != nil {
{{- end }}
// form file param {{ .Name }}
if err := r.SetFileParam({{ printf "%q" .Name }}, {{ .ValueExpression }}); err != nil {
return err
}
{{- if .IsNullable}}
}
{{- end }}
{{- else }}
// form param {{ .Name }}
{{- if .IsNullable }}
var fr{{ pascalize .Name }} {{ .GoType }}
if {{ .ValueExpression }} != nil {
fr{{ pascalize .Name }} = *{{ .ValueExpression }}
}
{{- else }}
fr{{ pascalize .Name }} := {{ .ValueExpression }}
{{- end}}
f{{ pascalize .Name}} := {{ if .Formatter }}{{ .Formatter }}(fr{{ pascalize .Name }}){{ else }}fr{{ pascalize .Name }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
{{- if not .AllowEmptyValue }}
if f{{ pascalize .Name }} != "" {
{{- end }}
if err := r.SetFormParam({{ printf "%q" .Name }}, f{{ pascalize .Name }}); err != nil {
return err
}
{{- if not .AllowEmptyValue }}
}
{{- end }}
{{- end }}
{{- end }}
{{- if and .IsNullable (not .AllowEmptyValue) }}
}
{{- end }}
{{- else if .IsArray }}
{{- if not .IsBodyParam }}
if {{ .ValueExpression }} != nil {
{{- if .Child }}{{/* bind complex parameters (arrays and nested structures) */}}
// binding items for {{ .Name }}
joined{{ pascalize .Name }} := {{ .ReceiverName }}.bindParam{{ pascalize .Name }}(reg)
{{- else }}
values{{ pascalize .Name }} := {{ if and (not .IsArray) (not .IsStream) (not .IsMap) (.IsNullable) }}*{{end}}{{ .ValueExpression }}
joined{{ pascalize .Name}} := swag.JoinByFormat(values{{ pascalize .Name }}, "{{.CollectionFormat}}")
{{- end }}
{{- if .IsQueryParam }}
// query array param {{ .Name }}
if err := r.SetQueryParam({{ printf "%q" .Name }}, joined{{ pascalize .Name }}...); err != nil {
return err
}
{{- else if and .IsFormParam }}
// form array param {{ .Name }}
if err := r.SetFormParam({{ printf "%q" .Name }}, joined{{ pascalize .Name }}...); err != nil {
return err
}
{{- else if and .IsPathParam }}
// path array param {{ .Name }}
// SetPathParam does not support variadic arguments, since we used JoinByFormat
// we can send the first item in the array as it's all the items of the previous
// array joined together
if len(joined{{ pascalize .Name }}) > 0 {
if err := r.SetPathParam({{ printf "%q" .Name }}, joined{{ pascalize .Name }}[0]); err != nil {
return err
}
}
{{- else if .IsHeaderParam }}
// header array param {{ .Name }}
if len(joined{{ pascalize .Name }}) > 0 {
if err := r.SetHeaderParam({{ printf "%q" .Name }}, joined{{ pascalize .Name }}[0]); err != nil {
return err
}
}
{{- end }}
}
{{- end }}
{{- end }}
{{- if .IsBodyParam }}
{{- if or .Schema.IsInterface .Schema.IsStream (and .Schema.IsArray .Child) (and .Schema.IsMap .Child) (and .Schema.IsNullable (not .HasDiscriminator)) }}
if {{ .ValueExpression }} != nil {
{{- end }}
if err := r.SetBodyParam({{ .ValueExpression }}); err != nil {
return err
}
{{- if or .Schema.IsInterface .Schema.IsStream (and .Schema.IsArray .Child) (and .Schema.IsMap .Child) (and .Schema.IsNullable (not .HasDiscriminator)) }}
}
{{- end }}
{{- end }}
{{- end }}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
{{- range .Params }}
{{- if and (not .IsBodyParam) (not .IsFileParam) .IsArray }}
// bindParam{{ pascalize $.Name }} binds the parameter {{ .Name }}
func ({{ .ReceiverName }} *{{ pascalize $.Name }}Params) bindParam{{ pascalize .Name }}(formats strfmt.Registry) []string {
{{ varname .Child.ValueExpression }}R := {{ if and (not .IsArray) (not .IsStream) (not .IsMap) (.IsNullable) }}*{{end}}{{ .ValueExpression }}
{{ template "sliceclientparambinder" . }}
return {{ varname .Child.ValueExpression }}S
}
{{- end }}
{{- end }}
{{- define "sliceclientparambinder" }}
{{- if .IsArray }}
var {{ varname .Child.ValueExpression }}C []string
for _, {{ varname .Child.ValueExpression }}IR := range {{ varname .Child.ValueExpression }}R { // explode {{ .GoType }}
{{ template "sliceclientparambinder" .Child }}
{{ varname .Child.ValueExpression }}C = append({{ varname .Child.ValueExpression }}C, {{ varname .Child.ValueExpression }}IV)
}
// {{ .Child.ItemsDepth }}CollectionFormat: {{ printf "%q" .CollectionFormat }}
{{ varname .Child.ValueExpression }}S := swag.JoinByFormat({{ varname .Child.ValueExpression }}C, {{ printf "%q" .CollectionFormat }})
{{- if .Child.Parent }}{{/* NOTE: we cannot expect a "multi" CollectionFormat within an inner array */}}
{{ varname .Child.ValueExpression }}V := {{ varname .Child.ValueExpression }}S[0]
{{- end }}
{{- else }}
{{ varname .ValueExpression }}IV :=
{{- if .IsCustomFormatter }}
{{- print " " }}{{ varname .ValueExpression }}IR.String()
{{- else if eq .GoType "string" }}
{{- print " " }}{{ varname .ValueExpression }}IR
{{- else if .Formatter }}
{{- print " "}}{{ .Formatter }}({{ varname .ValueExpression }}IR)
{{- else }}
{{- print " " }}fmt.Sprintf("%v", {{ varname .ValueExpression }}IR)
{{- end }} // {{ .GoType }} as string
{{- end }}
{{- end }}

View file

@ -0,0 +1,346 @@
{{- define "clientresponse" }}
// New{{ pascalize .Name }} creates a {{ pascalize .Name }} with default headers values
func New{{ pascalize .Name }}({{ if eq .Code -1 }}code int{{ end }}{{ if .Schema }}{{ if and (eq .Code -1) .Schema.IsStream }}, {{end}}{{ if .Schema.IsStream }}writer io.Writer{{ end }}{{ end }}) *{{ pascalize .Name }} {
{{- if .Headers.HasSomeDefaults }}
var (
// initialize headers with default values
{{- range .Headers }}
{{- if .HasDefault }}
{{ template "simpleschemaDefaultsvar" . }}
{{- end }}
{{- end }}
)
{{- range .Headers }}
{{- if .HasDefault }}
{{ template "simpleschemaDefaultsinit" . }}
{{- end }}
{{- end }}
{{- end }}
return &{{ pascalize .Name }}{
{{- if eq .Code -1 }}
_statusCode: code,
{{- end }}
{{ range .Headers }}
{{- if .HasDefault }}
{{ pascalize .Name}}: {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}&{{ end }}{{ varname .ID }}Default,
{{- end }}
{{- end }}
{{- if .Schema }}
{{- if .Schema.IsStream }}
Payload: writer,
{{- end }}
{{- end }}
}
}
/* {{ pascalize .Name}} describes a response with status code {{ .Code }}, with default header values.
{{ if .Description }}{{ blockcomment .Description }}{{else}}{{ pascalize .Name }} {{ humanize .Name }}{{end}}
*/
type {{ pascalize .Name }} struct {
{{- if eq .Code -1 }}
_statusCode int
{{- end }}
{{- range .Headers }}
{{- if .Description }}
/* {{ blockcomment .Description }}
{{- if or .SwaggerFormat .Default }}
{{ print "" }}
{{- if .SwaggerFormat }}
Format: {{ .SwaggerFormat }}
{{- end }}
{{- if .Default }}
Default: {{ json .Default }}
{{- end }}
{{- end }}
*/
{{- end }}
{{ pascalize .Name }} {{ .GoType }}
{{- end }}
{{- if .Schema }}
Payload {{ if and (not .Schema.IsBaseType) (not .Schema.IsInterface) .Schema.IsComplexObject (not .Schema.IsStream) }}*{{ end }}{{ if (not .Schema.IsStream) }}{{ .Schema.GoType }}{{ else }}io.Writer{{end}}
{{- end }}
}
// IsSuccess returns true when this {{ humanize .Name }} response has a 2xx status code
func ({{ .ReceiverName }} *{{ pascalize .Name }}) IsSuccess() bool {
{{- if eq .Code -1 }}
return {{ .ReceiverName }}._statusCode/100 == 2
{{- else }}
return {{ and (ge .Code 200) (lt .Code 300) }}
{{- end }}
}
// IsRedirect returns true when this {{ humanize .Name }} response has a 3xx status code
func ({{ .ReceiverName }} *{{ pascalize .Name }}) IsRedirect() bool {
{{- if eq .Code -1 }}
return {{ .ReceiverName }}._statusCode/100 == 3
{{- else }}
return {{ and (ge .Code 300) (lt .Code 400) }}
{{- end }}
}
// IsClientError returns true when this {{ humanize .Name }} response has a 4xx status code
func ({{ .ReceiverName }} *{{ pascalize .Name }}) IsClientError() bool {
{{- if eq .Code -1 }}
return {{ .ReceiverName }}._statusCode/100 == 4
{{- else }}
return {{ and (ge .Code 400) (lt .Code 500) }}
{{- end }}
}
// IsServerError returns true when this {{ humanize .Name }} response has a 5xx status code
func ({{ .ReceiverName }} *{{ pascalize .Name }}) IsServerError() bool {
{{- if eq .Code -1 }}
return {{ .ReceiverName }}._statusCode/100 == 5
{{- else }}
return {{ and (ge .Code 500) (lt .Code 600) }}
{{- end }}
}
// IsCode returns true when this {{ humanize .Name }} response a status code equal to that given
func ({{ .ReceiverName }} *{{ pascalize .Name }}) IsCode(code int) bool {
{{- if eq .Code -1 }}
return {{ .ReceiverName }}._statusCode == code
{{- else }}
return code == {{ .Code }}
{{- end }}
}
// Code gets the status code for the {{ humanize .Name }} response
func ({{ .ReceiverName }} *{{ pascalize .Name }}) Code() int {
{{- if eq .Code -1 }}
return {{ .ReceiverName }}._statusCode
{{- else }}
return {{ .Code }}
{{- end }}
}
func ({{ .ReceiverName }} *{{ pascalize .Name }}) Error() string {
return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown error {{ end }}{{ if .Schema }} %+v{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}, o.Payload{{ end }})
}
func ({{ .ReceiverName }} *{{ pascalize .Name }}) String() string {
return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown response {{ end }}{{ if .Schema }} %+v{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}, o.Payload{{ end }})
}
{{ if .Schema }}
func ({{ .ReceiverName }} *{{ pascalize .Name }}) GetPayload() {{ if and (not .Schema.IsBaseType) (not .Schema.IsInterface) .Schema.IsComplexObject (not .Schema.IsStream) }}*{{ end }}{{ if (not .Schema.IsStream) }}{{ .Schema.GoType }}{{ else }}io.Writer{{end}} {
return o.Payload
}
{{- end }}
func ({{ .ReceiverName }} *{{ pascalize .Name }}) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
{{- range .Headers }}
// hydrates response header {{.Name}}
hdr{{ pascalize .Name }} := response.GetHeader("{{ .Name }}")
if hdr{{ pascalize .Name }} != "" {
{{- if .Converter }}
val{{ camelize .Name }}, err := {{ .Converter }}(hdr{{ pascalize .Name }})
if err != nil {
return errors.InvalidType({{ .Path }}, "header", "{{ .GoType }}", hdr{{ pascalize .Name }})
}
{{ .ReceiverName }}.{{ pascalize .Name }} = val{{ camelize .Name }}
{{- else if .Child }}
// binding header items for {{ .Name }}
val{{ pascalize .Name }}, err := {{ .ReceiverName }}.bindHeader{{ pascalize .Name }}(hdr{{ pascalize .Name }}, formats)
if err != nil {
return err
}
{{ .ReceiverName }}.{{ pascalize .Name }} = val{{ pascalize .Name }}
{{- else if .IsCustomFormatter }}
val{{ camelize .Name }}, err := formats.Parse({{ printf "%q" .SwaggerFormat }}, hdr{{ pascalize .Name }})
if err != nil {
return errors.InvalidType({{ .Path }}, "header", "{{ .GoType }}", hdr{{ pascalize .Name }})
}
{{- if .IsNullable }}
v := (val{{ camelize .Name }}.({{ .GoType }}))
{{ .ReceiverName }}.{{ pascalize .Name }} = &v
{{- else }}
{{ .ReceiverName }}.{{ pascalize .Name }} = *(val{{ camelize .Name }}.(*{{ .GoType }}))
{{- end }}
{{- else }}
{{- if eq .GoType "string" }}
{{ .ReceiverName }}.{{ pascalize .Name }} = hdr{{ pascalize .Name }}
{{- else }}
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ .GoType }}(hdr{{ pascalize .Name }})
{{- end }}
{{- end }}
}
{{- end }}
{{- if .Schema }}
{{- if .Schema.IsBaseType }}
// response payload as interface type
payload, err := {{ toPackageName .ModelsPackage }}.Unmarshal{{ dropPackage .Schema.GoType }}{{ if .Schema.IsArray}}Slice{{ end }}(response.Body(), consumer)
if err != nil {
return err
}
{{ .ReceiverName }}.Payload = payload
{{- else if .Schema.IsComplexObject }}
{{ .ReceiverName }}.Payload = new({{ .Schema.GoType }})
{{- end }}
{{- if not .Schema.IsBaseType }}
// response payload
if err := consumer.Consume(response.Body(), {{ if not (or .Schema.IsComplexObject .Schema.IsStream) }}&{{ end}}{{ .ReceiverName }}.Payload); err != nil && err != io.EOF {
return err
}
{{- end }}
{{- end }}
return nil
}
{{- range .Headers }}
{{- if .Child }}
// bindHeader{{ pascalize $.Name }} binds the response header {{ .Name }}
func ({{ .ReceiverName }} *{{ pascalize $.Name }}) bindHeader{{ pascalize .Name }}(hdr string, formats strfmt.Registry) ({{ .GoType }}, error) {
{{ varname .Child.ValueExpression }}V := hdr
{{ template "sliceclientheaderbinder" . }}
return {{ varname .Child.ValueExpression }}C, nil
}
{{- end }}
{{- end }}
{{- end }}
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// {{ pascalize .Name }}Reader is a Reader for the {{ pascalize .Name }} structure.
type {{ pascalize .Name }}Reader struct {
formats strfmt.Registry
{{- if .HasStreamingResponse }}
writer io.Writer
{{- end }}
}
// ReadResponse reads a server response into the received {{ .ReceiverName }}.
func ({{ .ReceiverName }} *{{ pascalize .Name }}Reader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
{{- if .Responses}}
switch response.Code() {
{{- end }}
{{- range .Responses }}
case {{ .Code }}:
result := New{{ pascalize .Name }}({{ if .Schema }}{{ if .Schema.IsStream }}{{ $.ReceiverName }}.writer{{ end }}{{ end }})
if err := result.readResponse(response, consumer, {{ $.ReceiverName }}.formats); err != nil {
return nil, err
}
return {{ if .IsSuccess }}result, nil{{else}}nil, result{{ end }}
{{- end }}
{{- if .DefaultResponse }}
{{- with .DefaultResponse }}
{{- if $.Responses}}
default:
{{- end }}
result := New{{ pascalize .Name }}(response.Code(){{ if .Schema }}{{ if .Schema.IsStream }}, {{ $.ReceiverName }}.writer{{ end }}{{ end }})
if err := result.readResponse(response, consumer, {{ $.ReceiverName }}.formats); err != nil {
return nil, err
}
if response.Code() / 100 == 2 {
return result, nil
}
return nil, result
{{- end }}
{{- else }}
{{- if $.Responses}}
default:
{{- end }}
return nil, runtime.NewAPIError("[{{ upper .Method }} {{ .Path }}]{{ if .Name }} {{ .Name }}{{ end }}", response, response.Code())
{{- end }}
{{- if .Responses}}
}
{{- end }}
}
{{ range .Responses }}
{{ template "clientresponse" . }}
{{ end }}
{{ if .DefaultResponse }}
{{ template "clientresponse" .DefaultResponse }}
{{ end }}
{{ range .ExtraSchemas }}
/*{{ pascalize .Name }} {{ template "docstring" . }}
swagger:model {{ .Name }}
*/
{{- template "schema" . }}
{{- end }}
{{- define "sliceclientheaderbinder" }}
{{- if .IsArray }}
var (
{{ varname .Child.ValueExpression }}C {{ .GoType }}
)
// {{ .Child.ItemsDepth }}CollectionFormat: {{ printf "%q" .CollectionFormat }}
{{ varname .Child.ValueExpression }}R := swag.SplitByFormat({{ varname .Child.ValueExpression }}V, {{ printf "%q" .CollectionFormat }})
for {{ if or .Child.IsCustomFormatter .Child.Converter }}{{ .IndexVar }}{{ else }}_{{ end }}, {{ varname .Child.ValueExpression }}IV := range {{ varname .Child.ValueExpression }}R {
{{ template "sliceclientheaderbinder" .Child }}
{{ varname .Child.ValueExpression }}C = append({{ varname .Child.ValueExpression }}C, {{ varname .Child.ValueExpression }}IC) // roll-up {{ .Child.GoType }} into {{ .GoType }}
}
{{- else }}
// convert split string to {{ .GoType }}
{{- if .IsCustomFormatter }}
val, err := formats.Parse({{ printf "%q" .SwaggerFormat }}, {{ varname .ValueExpression }}IV)
if err != nil {
return nil, errors.InvalidType({{ .Path }}, "header{{ .ItemsDepth }}", "{{ .GoType }}", {{ varname .ValueExpression }}IV)
}
{{- if .IsNullable }}
{{ varname .ValueExpression }}IC := (&val).(*{{ .GoType }})
{{- else }}
{{ varname .ValueExpression }}IC := val.({{ .GoType }})
{{- end }}
{{- else if .Converter }}
val, err := {{- print " "}}{{ .Converter }}({{ varname .ValueExpression }}IV)
if err != nil {
return nil, errors.InvalidType({{ .Path }}, "header{{ .ItemsDepth }}", "{{ .GoType }}", {{ varname .ValueExpression }}IV)
}
{{- if .IsNullable }}
{{ varname .ValueExpression }}IC := &val
{{- else }}
{{ varname .ValueExpression }}IC := val
{{- end }}
{{- else }}
{{ varname .ValueExpression }}IC :=
{{- if eq .GoType "string" }}
{{- print " " }}{{ varname .ValueExpression }}IV
{{- else }}
{{- print " " }}fmt.Sprintf("%v", {{ varname .ValueExpression }}IV)
{{- end }} // string as {{ .GoType }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,311 @@
# swagger
In Stratoscale, we really like the idea of API-first services, and we also really like Go.
We saw the go-swagger library, and thought that most of it can really help us. Generating code from
swagger files is a big problem with a lot of corner cases, and go-swagger is doing great job.
The one thing that we felt missing, is customization of the server to run with our design principles:
* Custom `main` function
* Dependency injection
* Limited scopes with unit testing.
Also:
* Adding you functions to the generated `configure_swagger_*.go` seems to be a burden.
* Lack of Interface that the service implement.
* Complicated and custom http clients and runtime.
Those are the changes that this contributor templates are providing:
## Server
### The new `restapi` package exposes interfaces
* Those interfaces can implemented by the developer and are the business logic of the service.
* The implementation of those is extensible.
* The implementation is separated from the generated code.
### The `restapi` returns an `http.Handler`
The `restapi.Handler` (see [example](./example/restapi/configure_swagger_petstore.go)) function returns
a standard `http.Handler`
* Given objects that implements the business logic, we can create a simple http handler.
* This handler is standard go http.Handler, so we can now use any other middleware, library, or framework
that support it.
* This handler is standard, so we understand it better.
## Client
* The new client package exposes interfaces, so functions in our code can receive those
interfaces which can be mocked for testing.
* The new client has a config that gets an `*url.URL` to customize the endpoint.
* The new client has a config that gets an `http.RoundTripper` to customize client with libraries, middleware or
frameworks that support the standard library's objects.
# Example Walk-Through
In the [example package](https://github.com/Stratoscale/swagger/tree/master/example) you'll find generated code and usage of the pet-store
[swagger file](./example/swagger.yaml).
* The `restapi`, `models` and `client` are auto-generated by the stratoscale/swagger docker file.
* The `internal` package was manually added and contains the server's business logic.
* The `main.go` file is the entrypoint and contains initializations and dependency injections of the project.
## Server
### [restapi](https://github.com/Stratoscale/swagger/tree/master/example/restapi)
This package is autogenerated and contains the server routing and parameters parsing.
The modified version contains `restapi.PetAPI` and `restapi.StoreAPI` which were auto generated.
```go
// PetAPI
type PetAPI interface {
PetCreate(ctx context.Context, params pet.PetCreateParams) middleware.Responder
PetDelete(ctx context.Context, params pet.PetDeleteParams) middleware.Responder
PetGet(ctx context.Context, params pet.PetGetParams) middleware.Responder
PetList(ctx context.Context, params pet.PetListParams) middleware.Responder
PetUpdate(ctx context.Context, params pet.PetUpdateParams) middleware.Responder
}
//go:generate mockery -name StoreAPI -inpkg
// StoreAPI
type StoreAPI interface {
InventoryGet(ctx context.Context, params store.InventoryGetParams) middleware.Responder
OrderCreate(ctx context.Context, params store.OrderCreateParams) middleware.Responder
// OrderDelete is For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors
OrderDelete(ctx context.Context, params store.OrderDeleteParams) middleware.Responder
// OrderGet is For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions
OrderGet(ctx context.Context, params store.OrderGetParams) middleware.Responder
}
```
Each function matches an `operationId` in the swagger file and they are grouped according to
the operation `tags`.
There is also a `restapi.Config`:
```go
// Config is configuration for Handler
type Config struct {
PetAPI
StoreAPI
Logger func(string, ...interface{})
// InnerMiddleware is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
InnerMiddleware func(http.Handler) http.Handler
}
```
This config is auto generated and contains all the declared interfaces above.
It is used to initiate an http.Handler with the `Handler` function:
```go
// Handler returns an http.Handler given the handler configuration
// It mounts all the business logic implementers in the right routing.
func Handler(c Config) (http.Handler, error) {
...
```
Let's look how we use this generated code to build our server.
### [internal](https://github.com/Stratoscale/swagger/tree/master/example/internal)
The `internal` package is **not** auto generated and contains the business logic of our server.
We can see two structs that implements the `restapi.PetAPI` and `restapi.StoreAPI` interfaces,
needed to make our server work.
When adding or removing functions from our REST API, we can just add or remove functions to those
business logic units. We can also create new logical units when they are added to our REST API.
### [main.go](./example/main.go)
The main function is pretty straight forward. We initiate our business logic units.
Then create a config for our rest API. We then create a standard `http.Handler` which we can
update with middleware, test with `httptest`, or to use with other standard tools.
The last piece is to run the handler with `http.ListenAndServe` or to use it with an `http.Server` -
it is all very customizable.
```go
func main() {
// Initiate business logic implementers.
// This is the main function, so here the implementers' dependencies can be
// injected, such as database, parameters from environment variables, or different
// clients for different APIs.
p := internal.Pet{}
s := internal.Store{}
// Initiate the http handler, with the objects that are implementing the business logic.
h, err := restapi.Handler(restapi.Config{
PetAPI: &p,
StoreAPI: &s,
Logger: log.Printf,
})
if err != nil {
log.Fatal(err)
}
// Run the standard http server
log.Fatal(http.ListenAndServe(":8080", h))
}
```
## Client
The client code is in the [client package](https://github.com/Stratoscale/swagger/tree/master/example/client) and is autogenerated.
To create a new client we use the `client.Config` struct:
```go
type Config struct {
// URL is the base URL of the upstream server
URL *url.URL
// Transport is an inner transport for the client
Transport http.RoundTripper
}
```
This enables us to use custom server endpoint or custom client middleware. Easily, with the
standard components, and with any library that accepts them.
The client is then generated with the New method:
```go
// New creates a new swagger petstore HTTP client.
func New(c Config) *SwaggerPetstore { ... }
```
This method returns an object that has two important fields:
```go
type SwaggerPetstore {
...
Pet *pet.Client
Store *store.Client
}
```
Thos fields are objects, which implements interfaces declared in the [pet](./example/client/pet) and
[store](./example/client/store) packages:
For example:
```go
// API is the interface of the pet client
type API interface {
// PetCreate adds a new pet to the store
PetCreate(ctx context.Context, params *PetCreateParams) (*PetCreateCreated, error)
// PetDelete deletes a pet
PetDelete(ctx context.Context, params *PetDeleteParams) (*PetDeleteNoContent, error)
// PetGet gets pet by it s ID
PetGet(ctx context.Context, params *PetGetParams) (*PetGetOK, error)
// PetList lists pets
PetList(ctx context.Context, params *PetListParams) (*PetListOK, error)
// PetUpdate updates an existing pet
PetUpdate(ctx context.Context, params *PetUpdateParams) (*PetUpdateCreated, error)
}
```
They are very similar to the server interfaces, and can be used by consumers of those APIs
(instead of using the actual client or the `*Pet` struct)
# Authentication
Authenticating and policy enforcement of the application is done in several stages, described below.
## Define security in swagger.yaml
Add to the root of the swagger.yaml the security and security definitions sections.
```yaml
securityDefinitions:
token:
type: apiKey
in: header
name: Cookie
security:
- token: []
```
The securityDefinitions section defines different security types that your application can handle.
The supported types by go-swagger are:
* `apiKey` - token that should be able to processed.
* `oauth2` - token and scopes that should be processed.
* and `basic` - user/password that should be processed.
Here we defined an apiKey, that is passed through the Cookie header.
The `security` section defines the default security enforcement for the application. You can select
different securityDefinitions, as the keys, and apply "scopes" as the values. Those default definitions
can be overriden in each route by a section with the same name:
```yaml
paths:
/pets:
post:
[...]
security:
- token: [admin]
```
Here we overriden the scope of token in the POST /pets URL so that only admin can use this API.
Let's see how we can use this functionality.
## Writing Security Handlers
Once we created a security definition named "token", a function called "AuthToken" was added to the `restapi.Config`:
```go
type Config struct {
...
// AuthToken Applies when the "Cookie" header is set
AuthToken func(token string) (interface{}, error)
}
```
This function gets the content of the Cookie header, and should return an `interface{}` and `error`.
The `interface{}` is the object that should represent the user that performed the request, it should
be nil to return an unauthorized 401 HTTP response. If the returned `error` is not nil, an HTTP 500,
internal server error will be returned.
The returned object, will be stored in the request context under the `restapi.AuthKey` key.
There is another function that we should know about, in the `restapi.Config` struct:
```go
type Config struct {
...
// Authorizer is used to authorize a request after the Auth function was called using the "Auth*" functions
// and the principal was stored in the context in the "AuthKey" context value.
Authorizer func(*http.Request) error
}
```
This one is a custom defined function that gets the request and can return an error.
If the returned error is not nil, and 403 HTTP error will be returned to the client - here the policy
enforcement comes to place.
There are two things that this function should be aware of:
1. The user - it can retrieve the user information from the context: `ctx.Value(restapi.AuthKey).(MyUserType)`.
Usually, a server will have a function for extracting this user information and returns a concrete
type which could be used by all the routes.
2. The route - it can retrieve the route using the go-swagger function: `middleware.MatchedRouteFrom(*http.Request)`.
So no need to parse URL and test the request method.
This route struct contains the route information. If for example, we want to check the scopes that were
defined for the current route in the swagger.yaml we can use the code below:
```go
for _, auth := range route.Authenticators {
for scopeName, scopeValues := range auth.Scopes {
for _, scopeValue := range scopeValues {
...
}
}
}
```

View file

@ -0,0 +1,111 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Name }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/runtime"
"github.com/go-openapi/validate"
strfmt "github.com/go-openapi/strfmt"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
//go:generate mockery --name API --keeptree --with-expecter --case underscore
// API is the interface of the {{ humanize .Name }} client
type API interface {
{{ range .Operations -}}
/*
{{ pascalize .Name }} {{ if .Summary }}{{ pluralizeFirstWord (humanize .Summary) }}{{ if .Description }}
{{ blockcomment .Description }}{{ end }}{{ else if .Description}}{{ blockcomment .Description }}{{ else }}{{ humanize .Name }} API{{ end -}}
*/
{{ pascalize .Name }}(ctx context.Context, params *{{ pascalize .Name }}Params{{ if .HasStreamingResponse }}, writer io.Writer{{ end }}) {{ if .SuccessResponse }}({{ range .SuccessResponses }}*{{ pascalize .Name }}, {{ end }}{{ end }}error{{ if .SuccessResponse }}){{ end }}
{{ end -}}
}
// New creates a new {{ humanize .Name }} API client.
func New(transport runtime.ClientTransport, formats strfmt.Registry, authInfo runtime.ClientAuthInfoWriter) *Client {
return &Client{
transport: transport,
formats: formats,
authInfo: authInfo,
}
}
/*
Client {{ if .Summary }}{{ .Summary }}{{ if .Description }}
{{ blockcomment .Description }}{{ end }}{{ else if .Description}}{{ blockcomment .Description }}{{ else }}for {{ humanize .Name }} API{{ end }}
*/
type Client struct {
transport runtime.ClientTransport
formats strfmt.Registry
authInfo runtime.ClientAuthInfoWriter
}
{{ range .Operations -}}
/*
{{ pascalize .Name }} {{ if .Summary }}{{ pluralizeFirstWord (humanize .Summary) }}{{ if .Description }}
{{ blockcomment .Description }}{{ end }}{{ else if .Description}}{{ blockcomment .Description }}{{ else }}{{ humanize .Name }} API{{ end }}
*/
func (a *Client) {{ pascalize .Name }}(ctx context.Context, params *{{ pascalize .Name }}Params{{ if .HasStreamingResponse }}, writer io.Writer{{ end }}) {{ if .SuccessResponse }}({{ range .SuccessResponses }}*{{ pascalize .Name }}, {{ end }}{{ end }}error{{ if .SuccessResponse }}){{ end }} {
{{ $length := len .SuccessResponses }}
{{ $success := .SuccessResponses }}
{{ if .Responses }}result{{else}}_{{end}}, err := a.transport.Submit(&runtime.ClientOperation{
ID: {{ printf "%q" .Name }},
Method: {{ printf "%q" .Method }},
PathPattern: {{ printf "%q" .Path }},
ProducesMediaTypes: {{ printf "%#v" .ProducesMediaTypes }},
ConsumesMediaTypes: {{ printf "%#v" .ConsumesMediaTypes }},
Schemes: {{ printf "%#v" .Schemes }},
Params: params,
Reader: &{{ pascalize .Name }}Reader{formats: a.formats{{ if .HasStreamingResponse }}, writer: writer{{ end }}},
{{ if .Authorized -}}
AuthInfo: a.authInfo,
{{ end -}}
Context: ctx,
Client: params.HTTPClient,
})
if err != nil {
return {{ if $success }}{{ padSurround "nil" "nil" 0 $length }}, {{ end }}err
}
{{- if .Responses }}
switch value := result.(type) {
{{- range $i, $v := .Responses }}
case *{{ pascalize $v.Name }}:
{{- if $v.IsSuccess }}
return {{ if $success }}{{ padSurround "value" "nil" $i $length }},{{ end }}nil
{{- else }}
return {{ if $success }}{{ padSurround "nil" "nil" 0 $length }},{{ end }}runtime.NewAPIError("unsuccessful response", value, value.Code())
{{- end }}
{{- end }}
}
{{- if .DefaultResponse }}
// unexpected success response
unexpectedSuccess := result.(*{{ pascalize .DefaultResponse.Name }})
return {{ if $success }}{{ padSurround "nil" "nil" 0 $length }}, {{ end }}runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
{{- else }}
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
msg := fmt.Sprintf("unexpected success response for {{ .Name }}: API contract not enforced by server. Client expected to get an error, but got: %T", result)
panic(msg)
{{- end }}
{{- else }}
return nil
{{- end }}
}
{{ end }}

View file

@ -0,0 +1,83 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/url"
"net/http"
rtclient "github.com/go-openapi/runtime/client"
"github.com/go-openapi/swag"
"github.com/go-openapi/spec"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
const (
// DefaultHost is the default Host
// found in Meta (info) section of spec file
DefaultHost string = {{ printf "%#v" .Host }}
// DefaultBasePath is the default BasePath
// found in Meta (info) section of spec file
DefaultBasePath string = {{ printf "%#v" .BasePath }}
)
// DefaultSchemes are the default schemes found in Meta (info) section of spec file
var DefaultSchemes = {{ printf "%#v" .Schemes }}
type Config struct {
// URL is the base URL of the upstream server
URL *url.URL
// Transport is an inner transport for the client
Transport http.RoundTripper
// AuthInfo is for authentication
AuthInfo runtime.ClientAuthInfoWriter
}
// New creates a new {{ humanize .Name }} HTTP client.
func New(c Config) *{{ pascalize .Name }} {
var (
host = DefaultHost
basePath = DefaultBasePath
schemes = DefaultSchemes
)
if c.URL != nil {
host = c.URL.Host
basePath = c.URL.Path
schemes = []string{c.URL.Scheme}
}
transport := rtclient.New(host, basePath, schemes)
if c.Transport != nil {
transport.Transport = c.Transport
}
cli := new({{ pascalize .Name }})
cli.Transport = transport
{{ range .OperationGroups -}}
cli.{{ pascalize .Name }} = {{ .PackageAlias }}.New(transport, strfmt.Default, c.AuthInfo)
{{ end -}}
return cli
}
// {{ pascalize .Name }} is a client for {{ humanize .Name }}
type {{ pascalize .Name }} struct {
{{ range .OperationGroups -}}
{{ pascalize .Name }} {{ .PackageAlias }}.API
{{ end -}}
Transport runtime.ClientTransport
}

View file

@ -0,0 +1,222 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .APIPackage }}
import (
"context"
"crypto/tls"
"net/http"
"log"
"fmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/loads"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/runtime/security"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
{{ $package := .Package }}
type contextKey string
const AuthKey contextKey = "Auth"
{{ range .OperationGroups -}}
//go:generate mockery -name {{ pascalize .Name}}API -inpkg
/* {{ pascalize .Name }}API {{ .Description }} */
type {{ pascalize .Name }}API interface {
{{ range .Operations -}}
{{ if .Summary -}}
/* {{ pascalize .Name }} {{ .Summary }} */
{{ else if .Description -}}
/* {{ pascalize .Name }} {{ .Description }} */
{{ end -}}
{{ pascalize .Name }}(ctx context.Context, params {{.Package}}.{{ pascalize .Name }}Params) middleware.Responder
{{ end -}}
}
{{ end }}
// Config is configuration for Handler
type Config struct {
{{ range .OperationGroups -}}
{{ pascalize .Name }}API
{{ end -}}
Logger func(string, ...interface{})
// InnerMiddleware is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
InnerMiddleware func(http.Handler) http.Handler
// Authorizer is used to authorize a request after the Auth function was called using the "Auth*" functions
// and the principal was stored in the context in the "AuthKey" context value.
Authorizer func(*http.Request) error
{{ range .SecurityDefinitions -}}
{{ if .IsBasicAuth -}}
// Auth{{ pascalize .ID }} for basic authentication
Auth{{ pascalize .ID }} func(user string, pass string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
{{ end -}}
{{ if .IsAPIKeyAuth -}}
// Auth{{ pascalize .ID }} Applies when the "{{ .Name }}" {{ .Source }} is set
Auth{{ pascalize .ID }} func(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
{{ end }}
{{ if .IsOAuth2 -}}
// Auth{{ pascalize .ID }} For OAuth2 authentication
Auth{{ pascalize .ID }} func(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
{{ end -}}
{{ end -}}
// Authenticator to use for all APIKey authentication
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
// Authenticator to use for all Bearer authentication
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
// Authenticator to use for all Basic authentication
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
{{ range .Consumes -}}
{{ if .Implementation -}}
// {{ pascalize .Name }}Consumer is a {{ .Name }} consumer that will replace the default if not nil.
{{ pascalize .Name }}Consumer runtime.Consumer
{{ end -}}
{{ end -}}
}
// Handler returns an http.Handler given the handler configuration
// It mounts all the business logic implementers in the right routing.
func Handler(c Config) (http.Handler, error) {
h, _, err := HandlerAPI(c)
return h, err
}
// HandlerAPI returns an http.Handler given the handler configuration
// and the corresponding *{{ pascalize .Name }} instance.
// It mounts all the business logic implementers in the right routing.
func HandlerAPI(c Config) (http.Handler, *{{.Package}}.{{ pascalize .Name }}API, error) {
spec, err := loads.Analyzed(swaggerCopy(SwaggerJSON), "")
if err != nil {
return nil, nil, fmt.Errorf("analyze swagger: %v", err)
}
api := {{.Package}}.New{{ pascalize .Name }}API(spec)
api.ServeError = errors.ServeError
api.Logger = c.Logger
if c.APIKeyAuthenticator != nil {
api.APIKeyAuthenticator = c.APIKeyAuthenticator
}
if c.BasicAuthenticator != nil {
api.BasicAuthenticator = c.BasicAuthenticator
}
if c.BearerAuthenticator != nil {
api.BearerAuthenticator = c.BearerAuthenticator
}
{{ range .Consumes -}}
if c.{{ pascalize .Name }}Consumer != nil {
api.{{ pascalize .Name }}Consumer = c.{{ pascalize .Name }}Consumer
} else {
{{ if .Implementation -}}
api.{{ pascalize .Name }}Consumer = {{ .Implementation }}
{{ else }}
api.{{ pascalize .Name }}Consumer = runtime.ConsumerFunc(func(r io.Reader, target interface{}) error {
return errors.NotImplemented("{{.Name}} consumer has not yet been implemented")
})
{{ end -}}
}
{{ end -}}
{{ range .Produces -}}
{{ if .Implementation -}}
api.{{ pascalize .Name }}Producer = {{ .Implementation }}
{{ else -}}
api.{{ pascalize .Name }}Producer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("{{.Name}} producer has not yet been implemented")
})
{{ end -}}
{{ end -}}
{{ range .SecurityDefinitions -}}
{{ if .IsBasicAuth -}}
api.{{ pascalize .ID }}Auth = func(user string, pass string) ({{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
if c.Auth{{ pascalize .ID }} == nil {
{{- if eq .Principal "interface{}" }}
return "", nil
{{- else }}
panic("you specified a custom principal type, but did not provide the authenticator to provide this")
{{- end }}
}
return c.Auth{{ pascalize .ID }}(user, pass)
}
{{ end -}}
{{ if .IsAPIKeyAuth -}}
api.{{ pascalize .ID }}Auth = func(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
if c.Auth{{ pascalize .ID }} == nil {
{{- if eq .Principal "interface{}" }}
return token, nil
{{- else }}
panic("you specified a custom principal type, but did not provide the authenticator to provide this")
{{- end }}
}
return c.Auth{{ pascalize .ID }}(token)
}
{{ end }}
{{ if .IsOAuth2 -}}
api.{{ pascalize .ID }}Auth = func(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
if c.Auth{{ pascalize .ID }} == nil {
{{- if eq .Principal "interface{}" }}
return token, nil
{{- else }}
panic("you specified a custom principal type, but did not provide the authenticator to provide this")
{{- end }}
}
return c.Auth{{ pascalize .ID }}(token, scopes)
}
{{ end -}}
{{ end -}}
{{ if .SecurityDefinitions -}}
api.APIAuthorizer = authorizer(c.Authorizer)
{{ end -}}
{{ range .Operations -}}
api.{{if ne .Package $package}}{{pascalize .Package}}{{end}}{{ pascalize .Name }}Handler =
{{- .PackageAlias }}.{{ pascalize .Name }}HandlerFunc(func(params {{.PackageAlias}}.{{ pascalize .Name }}Params{{if .Authorized}}, principal {{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}{{end}}) middleware.Responder {
ctx := params.HTTPRequest.Context()
{{ if .Authorized -}}
ctx = storeAuth(ctx, principal)
{{ end -}}
return c.{{pascalize .Package}}API.{{pascalize .Name}}(ctx, params)
})
{{ end -}}
api.ServerShutdown = func() { }
return api.Serve(c.InnerMiddleware), api, nil
}
// swaggerCopy copies the swagger json to prevent data races in runtime
func swaggerCopy(orig json.RawMessage) json.RawMessage {
c := make(json.RawMessage, len(orig))
copy(c, orig)
return c
}
// authorizer is a helper function to implement the runtime.Authorizer interface.
type authorizer func(*http.Request) error
func (a authorizer) Authorize(req *http.Request, principal interface{}) error {
if a == nil {
return nil
}
ctx := storeAuth(req.Context(), principal)
return a(req.WithContext(ctx))
}
func storeAuth(ctx context.Context, principal interface{}) context.Context {
return context.WithValue(ctx, AuthKey, principal)
}

View file

@ -0,0 +1,9 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .APIPackage }}
// this file is intentionally empty. Otherwise go-swagger will generate a server which we don't want

View file

@ -0,0 +1,25 @@
{{ define "docstring" }}
{{- if .Title }}
{{- comment .Title }}
{{- if .Description }}
//
// {{ comment .Description }}
{{- end }}
{{- else if .Description}}
{{- comment .Description }}
{{- else }}
{{- humanize .Name }}
{{- end }}
{{- if or .MinProperties .MinProperties }}
//
{{- if .MinProperties }}
// Min Properties: {{ .MinProperties }}
{{- end }}
{{- if .MaxProperties }}
// Max Properties: {{ .MaxProperties }}
{{- end }}
{{- end }}
{{- if .Example }}
// Example: {{ print .Example }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,20 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}
// {{ comment .Copyright }}
{{- end }}
package {{.Package}}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"github.com/go-openapi/strfmt"
{{- if .DefaultImports }}
{{ imports .DefaultImports }}
{{- end }}
{{- if .Imports }}
{{ imports .Imports }}
{{- end }}
)

View file

@ -0,0 +1,527 @@
{{- define "externalDoc" }}{{/* renders external documentation */}}
{{- with .ExternalDocs }}
{{- if .URL }}
{{- if .Description }}
> [{{ trimSpace .Description }}]({{ .URL }})
{{- else }}
> [Read more]({{ .URL }})
{{- end }}
{{- else }}
> {{ trimSpace .Description }}
{{- end }}
{{- end }}
{{- end }}
{{- define "docParam" }}{{/* renders a parameter with simple schema */}}
| {{ .Name }} | `{{ .Location }}` | {{ paramDocType . }} | `{{ .GoType }}` | {{ if .CollectionFormat }}`{{ docCollectionFormat .CollectionFormat .Child }}`{{ end }} | {{ if .Required }}{{ end }} | {{ if .Default }}`{{ json .Default }}`{{ end }} | {{ trimSpace .Description }} |
{{- end }}
{{- define "docModelSchema" }}{{/* renders a schema */}}
{{- if .IsArray }}
{{- if .IsAliased }}
[{{- dropPackage .GoType }}](#{{ dasherize (dropPackage .GoType) -}})
{{- else if .Items }}
{{- if and .Items.IsPrimitive (not .Items.IsAliased) -}}
{{- schemaDocType . -}}
{{- else -}}
[][{{- dropPackage .Items.GoType }}](#{{ dasherize (dropPackage .Items.GoType) -}})
{{- end -}}
{{- else -}}
[]any{{ printf " " -}}
{{- end -}}
{{- else if and .IsMap (not .IsAdditionalProperties) -}}
{{- if .IsAliased -}}
[{{- dropPackage .GoType }}](#{{ dasherize (dropPackage .GoType) -}})
{{- else if .ElemType }}
{{- if and .ElemType.IsPrimitive (not .ElemType.IsAliased) (not .ElemType.IsInterface) -}}
{{ schemaDocMapType . -}}
{{- else if .ElemType.IsInterface -}}
map of any{{ printf " " -}}
{{- else -}}
map of [{{- dropPackage .ElemType.GoType }}](#{{ dasherize (dropPackage .ElemType.GoType) -}})
{{- end -}}
{{- else -}}
map of any{{ printf " " -}}
{{- end -}}
{{- else if and .IsAliased .IsPrimitive (not .IsSuperAlias) -}}
| Name | Type | Go type | Default | Description | Example |
|------|------|---------| ------- |-------------|---------|
| {{ .Name }} | {{ schemaDocType . }}| {{ .AliasedType }} | {{ if .Default }}`{{ json .Default }}`{{ end }}| {{ trimSpace .Description }} | {{ if .Example }}`{{ .Example }}`{{ end }} |
{{ printf "\n" }}
{{- else if or (and .IsAliased (not (.IsAdditionalProperties))) (and .IsComplexObject (not .Properties) (not .AllOf)) -}}
[{{- dropPackage .GoType }}](#{{ dasherize (dropPackage .GoType) -}})
{{- else if and .IsInterface (not .IsAliased) (not .IsMap) -}}
any
{{- else -}}
{{- range .AllOf }}
{{- if .IsAnonymous }}
* inlined member (*{{ .Name }}*)
{{ template "docModelSchema" . }}
{{- else if or .IsComplexObject .IsPrimitive }}
* composed type [{{- dropPackage .GoType }}](#{{ dasherize (dropPackage .GoType) -}})
{{- else }}
* {{ template "docModelSchema" . }}
{{- end }}
{{- end }}
{{- if .Properties }}
**{{ if .IsTuple }}Tuple members{{ else }}Properties{{ end }}**
| Name | Type | Go type | Required | Default | Description | Example |
|------|------|---------|:--------:| ------- |-------------|---------|
{{- range .Properties }}
| {{ .Name }} | {{ template "docSchemaSimple" . }}| `{{ .GoType }}` | {{ if .Required }}{{ end }} | {{ if .Default }}`{{ json .Default }}`{{ end }}| {{ trimSpace .Description }} | {{ if .Example }}`{{ .Example }}`{{ end }} |
{{- end }}
{{ printf "\n" }}
{{- end }}
{{- if .HasAdditionalProperties }}
**Additional Properties**
{{- with .AdditionalProperties }}
{{- if .IsInterface }}
any
{{- else if .IsPrimitive }}
| Type | Go type | Default | Description | Example |
|------|---------| ------- |-------------|---------|
| {{ template "docSchemaSimple" . }} | `{{ .GoType }}` |{{ if .Default }}`{{ json .Default }}`{{ end }}| {{ trimSpace .Description }} | {{ if .Example }}`{{ .Example }}`{{ end }} |
{{- else }}
{{ template "docModelSchema" . }}
{{- end }}
{{- end }}
{{- end }}
{{- if and .IsTuple .HasAdditionalItems }}
{{- with .AdditionalItems }}
**Additional Items**
{{- if .IsInterface }}
any
{{- else if .IsPrimitive }}
| Type | Go type | Default | Description | Example |
|------|---------| ------- |-------------|---------|
| {{ template "docSchemaSimple" . }} | `{{ .GoType }}` |{{ if .Default }}`{{ json .Default }}`{{ end }}| {{ trimSpace .Description }} | {{ if .Example }}`{{ .Example }}`{{ end }} |
{{- else }}
{{ template "docModelSchema" . }}
{{- end }}
{{- end }}
{{- end }}
{{- end -}}
{{- end }}
{{- define "docModel" }}{{/* renders a definition */}}
{{- with .Description }}
> {{ .}}
{{- end }}
{{- if .ExternalDocs }}
{{ template "externalDoc" . }}
{{- end }}
{{ if or .Description .ExternalDocs }}
{{ printf "\n" }}
{{- end }}
{{ template "docModelSchema" .}}
{{- end }}
{{- define "docSchemaSimple" }}{{/* renders a simple property */}}
{{- if .IsAliased -}}
[{{- dropPackage .GoType }}](#{{ dasherize (dropPackage .GoType) -}})
{{- else if .IsArray }}
{{- if .Items }}
{{- if and .Items.IsPrimitive (not .Items.IsAliased) -}}
{{- schemaDocType . -}}
{{- else -}}
[][{{- dropPackage .Items.GoType }}](#{{ dasherize (dropPackage .Items.GoType) -}})
{{- end -}}
{{- else -}}
[]any{{ printf " " -}}
{{- end -}}
{{- else if .IsMap -}}
{{- if .ElemType }}
{{- if and .ElemType.IsPrimitive (not .ElemType.IsAliased) (not .ElemType.IsInterface) -}}
{{ schemaDocMapType . -}}
{{- else if .ElemType.IsInterface -}}
map of any{{ printf " " -}}
{{- else -}}
map of [{{- dropPackage .ElemType.GoType }}](#{{ dasherize (dropPackage .ElemType.GoType) -}})
{{- end -}}
{{- else -}}
map of any{{ printf " " -}}
{{- end -}}
{{- else if .IsPrimitive -}}
{{- schemaDocType . -}}
{{- else -}}
[{{- dropPackage .GoType }}](#{{ dasherize (dropPackage .GoType) -}})
{{- end -}}
{{- end }}
{{- define "docModelBodyParam" }}{{/* layout for body param schema */}}
| {{ .Name }} | `body` | {{ template "docSchemaSimple" .Schema }} | `{{ .Schema.GoType }}` | | {{ if .Required }}{{ end }} | {{ if .Default }}`{{ json .Default }}`{{ end }}| {{ trimSpace .Description }} |
{{- end }}
{{- define "docHeaders" }}{{/* renders response headers */}}
{{- if .Headers }}
| Name | Type | Go type | Separator | Default | Description |
|------|------|---------|-----------|---------|-------------|
{{- range .Headers }}
| {{ .Name }} | {{ headerDocType . }} | `{{ .GoType }}` | {{ if .CollectionFormat }}`{{ docCollectionFormat .CollectionFormat .Child }}`{{ end }} | {{ if .Default }}`{{ json .Default }}`{{ end }} | {{ trimSpace .Description }} |
{{- end }}
{{- end }}
{{- end }}
{{/* spec top-level information block */}}
{{- if .Info }}
{{- with .Info.Title }}
# {{ . }}
{{- end }}
{{- with .Info.Description }}
{{ . }}
{{- end }}
{{ template "externalDoc" . }}
{{- if or .Info.Version .Info.License .Info.Contact .Info.TermsOfService }}
## Informations
{{- end }}
{{- with .Info.Version }}
### Version
{{ . }}
{{- end }}
{{- with .Info.License }}
### License
{{ if .Name }}[{{ .Name }}]({{ end}}{{ .URL }}{{ if .Name }}){{ end }}
{{- end }}
{{- with .Info.Contact }}
### Contact
{{ .Name }} {{ .Email }} {{ .URL }}
{{- end }}
{{- with .Info.TermsOfService }}
### Terms Of Service
{{ . }}
{{- end }}
{{- else }}
{{ template "externalDoc" . }}
{{- end }}
{{- if .Tags }}
## Tags
{{- range .Tags }}
### <span id="tag-{{ dasherize .Name }}"></span>{{ if .ExternalDocs }}[{{ .Name }}]({{ .ExternalDocs.URL }}{{ if .ExternalDocs.Description }} {{ printf "%q" .ExternalDocs.Description }}{{ end }}){{ else }}{{ .Name }}{{ end }}
{{- if .Description }}
{{ .Description }}
{{- end }}
{{- end }}
{{- end }}
{{- if or .Schemes .Consumes .Produces }}
## Content negotiation
{{- end }}
{{- if .Schemes }}
### URI Schemes
{{- range .Schemes }}
* {{ . }}
{{- end }}
{{- range .ExtraSchemes }}
* {{ . }}
{{- end }}
{{- end }}
{{- if .Consumes }}
### Consumes
{{- range .Consumes }}
{{- range .AllSerializers }}
* {{ .MediaType }}
{{- end }}
{{- end }}
{{- end }}{{/* end .Schemes */}}
{{- if .Produces }}
### Produces
{{- range .Produces }}
{{- range .AllSerializers }}
* {{ .MediaType }}
{{- end }}
{{- end }}
{{- end }}
{{- if or .SecurityDefinitions .SecurityRequirements }}
## Access control
{{- end }}
{{- if .SecurityDefinitions }}
### Security Schemes
{{- range .SecurityDefinitions }}
#### {{ .ID }}{{ if .Source }} ({{ .Source }}{{ with .Name }}: {{ . }}{{ end }}){{ end }}
{{ .Description }}
{{- with .Type }}
> **Type**: {{ . }}
{{- end }}
{{- if .IsOAuth2}}
{{- with .Flow }}
>
> **Flow**: {{ . }}
{{- end }}
{{- with .AuthorizationURL }}
>
> **Authorization URL**: {{ . }}
{{- end }}
{{- with .TokenURL }}
>
> **Token URL**: {{ . }}
{{- end }}
{{ if .ScopesDesc }}
##### Scopes
Name | Description
-----|-------------
{{- range .ScopesDesc }}
{{ .Name }} | {{ .Description }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}{{/* end .SecurityDefinitions */}}
{{- if .SecurityRequirements }}
### Security Requirements
{{- range .SecurityRequirements }}
* {{ .Name }}{{ if .Scopes }}: {{ range $idx, $scope := .Scopes }}{{ if gt $idx 0 }}, {{ end }}{{ $scope }}{{ end }}
{{- end }}
{{- end }}
{{- end }}{{/* end .SecurityRequirements */}}
## All endpoints{{/* an index of all API endpoints */}}
{{- $alltags := .Tags }}
{{- range .OperationGroups }}
### {{ .PackageAlias }}
{{- $pkg := .PackageAlias }}
{{- range $alltags }}
{{- if eq .Name $pkg }}
{{ template "externalDoc" . }}
{{- end }}
{{- end }}
| Method | URI | Name | Summary |
|---------|---------|--------|---------|
{{- range .Operations }}
| {{ upper .Method }} | {{ joinPath .BasePath .Path }} | [{{ humanize .Name }}](#{{ dasherize .Name }}) | {{ .Summary }} |
{{- end }}
{{ printf "\n" }}
{{- end }}
## Paths{{/* all paths to operations */}}
{{- range .Operations }}
{{- $opname := .Name }}
### <span id="{{ dasherize .Name }}"></span> {{ if .Summary }}{{ trimSpace .Summary }}{{ else }}{{ humanize .Name }}{{ end }} (*{{ .Name }}*)
```
{{ upper .Method }} {{ joinPath .BasePath .Path }}
```
{{- with .Description }}
{{ . }}
{{- end }}
{{- with .ExternalDocs }}
> {{ if .URL }}[Read more]({{ .URL }} "{{ .Description }}"){{ end }}
{{- end }}
{{- if or (gt (len .SchemeOverrides) 0) (gt (len .ExtraSchemeOverrides) 0) }}
#### URI Schemes
{{- range .SchemeOverrides }}
* {{ . }}
{{- end }}
{{- range .ExtraSchemeOverrides }}
* {{ . }}
{{- end }}
{{- end }}
{{- if .Consumes }}
#### Consumes
{{- range .Consumes }}
* {{ . }}
{{- end }}
{{- end }}
{{- if .Produces }}
#### Produces
{{- range .Produces }}
* {{ . }}
{{- end }}
{{- end }}
{{- if .SecurityRequirements }}
#### Security Requirements
{{- range .SecurityRequirements }}
* {{ .Name }}{{ if .Scopes }}: {{ range $idx, $scope := .Scopes }}{{ if gt $idx 0 }}, {{ end }}{{ $scope }}{{ end }}{{ end }}
{{- end }}
{{- end }}
{{- if .Params }}
#### Parameters
| Name | Source | Type | Go type | Separator | Required | Default | Description |
|------|--------|------|---------|-----------| :------: |---------|-------------|
{{- range .PathParams }}{{ template "docParam" . }}{{ end }}
{{- range .HeaderParams }}{{ template "docParam" . }}{{ end }}
{{- range .QueryParams }}{{ template "docParam" . }}{{ end }}
{{- range .FormParams }}{{ template "docParam" . }}{{ end }}
{{- range .Params }}
{{- if .IsBodyParam }}
{{- template "docModelBodyParam" . }}
{{- end }}
{{- end }}
{{- end }}{{/* end .Params */}}
#### All responses
| Code | Status | Description | Has headers | Schema |
|------|--------|-------------|:-----------:|--------|
{{- range .Responses }}
| [{{.Code}}](#{{ dasherize $opname }}-{{ .Code }}) | {{ httpStatus .Code }} | {{ trimSpace .Description }} | {{ if .Headers }}{{ end }} | [schema](#{{ dasherize $opname }}-{{ .Code }}-schema) |
{{- end }}
{{- with .DefaultResponse }}
| [default](#{{ dasherize $opname }}-default) | | {{ trimSpace .Description }} | {{ if .Headers }}{{ end }} | [schema](#{{ dasherize $opname }}-default-schema) |
{{- end }}
#### Responses
{{ range .Responses }}
##### <span id="{{ dasherize $opname }}-{{ .Code }}"></span> {{.Code}}{{ if .Description }} - {{ trimSpace .Description }}{{ end }}
Status: {{ httpStatus .Code }}
###### <span id="{{ dasherize $opname }}-{{ .Code }}-schema"></span> Schema
{{- if .Schema }}
{{ template "docModel" .Schema }}
{{- end }}
{{- if .Examples }}
###### Examples
{{ range .Examples }}
**{{ .MediaType }}**
```json
{{ prettyjson .Example }}
```
{{- end }}
{{- end }}
{{- if .Headers }}
###### Response headers
{{ template "docHeaders" . }}
{{- end }}
{{- end }}
{{- with .DefaultResponse }}
##### <span id="{{ dasherize $opname }}-default"></span> Default Response
{{ trimSpace .Description }}
###### <span id="{{ dasherize $opname }}-default-schema"></span> Schema
{{- if .Schema }}
{{ template "docModel" .Schema }}
{{- else }}
empty schema
{{- end }}
{{- if .Examples }}
###### Examples
{{ range .Examples }}
**{{ .MediaType }}**
```json
{{ .Example }}
```
{{- end }}
{{- end }}
{{- if .Headers }}
###### Response headers
{{ template "docHeaders" . }}
{{- end }}
{{- end }}
{{- if .ExtraSchemas }}
###### Inlined models
{{- range .ExtraSchemas }}
{{- if ne .Name "" }}
**<span id="{{ dasherize .Name }}"></span> {{ .Name }}**
{{ template "docModel" . }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}{{/* end .Operations */}}
## Models
{{- range .Models }}
### <span id="{{ dasherize .Name }}"></span> {{ .Name }}
{{ template "docModel" . }}
{{- if .ExtraSchemas }}
#### Inlined models
{{- range .ExtraSchemas }}
{{- if ne .Name "" }}
**<span id="{{ dasherize .Name }}"></span> {{ .Name }}**
{{ template "docModel" . }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,27 @@
{{ template "header" . }}
{{- if .IncludeModel }}
{{- if .IsExported }}
// {{ pascalize .Name }} {{ template "docstring" . }}
{{- template "annotations" . }}
{{- end }}
{{- template "schema" . }}
{{- end }}
{{ range .ExtraSchemas }}
{{- if .IncludeModel }}
{{- if .IsExported }}
// {{ pascalize .Name }} {{ template "docstring" . }}
{{- template "annotations" . }}
{{- end }}
{{- template "schema" . }}
{{- end }}
{{- end }}
{{- define "annotations" }}{{/* annotations to generate spec from source */}}
{{- if not .IsBaseType }}
//
// swagger:model {{ .Name }}
{{- else }}
//
// swagger:discriminator {{ .Name }} {{ .DiscriminatorField }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,131 @@
{{- if and .IsBaseType .IsExported (not .IsSuperAlias) }}
{{- template "schemaPolymorphic" . }}
{{- else if .IsSuperAlias }}
type {{ pascalize .Name }} {{ template "typeSchemaType" . }}{{/* For types declared as $ref on some other type, just declare the type as a golang _aliased_ type, e.g. type A = B. No method shall be redeclared. */}}
{{- if .IsBaseType }}
{{ template "baseTypeSerializer" . }}{{/* When the alias redeclares a polymorphic type, define factory methods with this alias. */}}
{{- end }}
{{- else if .IsEmbedded }}
{{- template "schemaEmbedded" . }}
{{- else }}
{{- if or .IsComplexObject .IsTuple .IsAdditionalProperties }}{{/* TODO(fred): handle case of subtype inheriting from base type with AdditionalProperties, issue #2220 */}}
{{ if .Name }}type {{ if not .IsExported }}{{ .Name }}{{ else }}{{ pascalize .Name }}{{ end }}{{ end }} {{ template "schemaBody" . }}
{{- range .Properties }}
{{- if .IsBaseType }}
// {{ pascalize .Name}} gets the {{ humanize .Name }} of this base type{{/* all properties which are of a base type propagate its interface */}}
func ({{ $.ReceiverName}} *{{ pascalize $.Name}}) {{ pascalize .Name}}() {{ template "schemaType" . }}{
{{- if eq $.DiscriminatorField .Name }}
return {{ printf "%q" $.DiscriminatorValue }}
{{- else }}
return {{ $.ReceiverName }}.{{camelize .Name}}Field
{{- end }}
}
// Set{{ pascalize .Name}} sets the {{ humanize .Name }} of this base type
func ({{ $.ReceiverName}} *{{ pascalize $.Name}}) Set{{ pascalize .Name}}(val {{ template "schemaType" . }}) {
{{- if ne $.DiscriminatorField .Name }}
{{ $.ReceiverName }}.{{camelize .Name}}Field = val
{{- end }}
}
{{- end }}
{{- end }}
{{- if .Default }}{{/* TODO(fred) - issue #2189 */}}
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(b []byte) error {
type {{ pascalize .Name }}Alias {{ pascalize .Name }}
var t {{ pascalize .Name }}Alias
if err := json.Unmarshal([]byte({{printf "%q" (json .Default)}}), &t); err != nil {
return err
}
if err := json.Unmarshal(b, &t); err != nil {
return err
}
*{{.ReceiverName}} = {{ pascalize .Name }}(t)
return nil
}
{{- end }}
{{- else }}
type {{ pascalize .Name }} {{ template "typeSchemaType" . }}
{{- end }}
{{- if (and .IsPrimitive .IsAliased .IsCustomFormatter (not (stringContains .Zero "(\""))) }}
{{ template "aliasedSerializer" . }}
{{- end }}
{{- if .IsSubType }}
{{ range .AllOf }}
{{ range .Properties }}
{{- if .IsBaseType }}
// {{ pascalize .Name}} gets the {{ humanize .Name }} of this subtype
func ({{$.ReceiverName}} *{{ pascalize $.Name}}) {{ pascalize .Name}}() {{ template "schemaType" . }}{
{{- if eq $.DiscriminatorField .Name }}
return {{ printf "%q" $.DiscriminatorValue }}
{{- else }}
return {{ $.ReceiverName }}.{{camelize .Name}}Field
{{- end }}
}
// Set{{ pascalize .Name}} sets the {{ humanize .Name }} of this subtype
func ({{$.ReceiverName}} *{{ pascalize $.Name}}) Set{{ pascalize .Name}}(val {{ template "schemaType" . }}) {
{{- if ne $.DiscriminatorField .Name }}
{{ $.ReceiverName }}.{{camelize .Name}}Field = val
{{- end }}
}
{{- end }}
{{- end }}{{/* TODO(fred): handle AdditionalProperties in base type */}}
{{- end }}
{{ template "mapOrSliceGetter" . }}
{{- end }}
{{ template "schemaSerializer" . }}
{{- end }}
{{- if and .IncludeValidator (not .IsSuperAlias) (not .IsEmbedded) }}{{/* aliased types type A = B do not redefine methods */}}
{{- if and (not (or .IsInterface .IsStream)) (or .Required .HasValidations .HasBaseType) }}
{{- if (eq .SwaggerType "string") }}{{/* Enum factory for enums for which we generate const (atm, only strings)*/}}
{{- if .Enum }}
func New{{ pascalize .Name }}(value {{ .GoType }}) *{{ .GoType }} {
return &value
}
// Pointer returns a pointer to a freshly-allocated {{ .GoType }}.
func ({{ .ReceiverName }} {{ .GoType }}) Pointer() *{{ .GoType }} {
return &{{ .ReceiverName }}
}
{{- end }}
{{- end }}
{{ template "schemavalidator" . }}
{{- else if not (or .IsInterface .IsStream) }}
// Validate validates this {{ humanize .Name }}{{/* this schema implements the runtime.Validatable interface but has no validations to check */}}
func ({{.ReceiverName}} {{ if or .IsTuple .IsComplexObject .IsAdditionalProperties }}*{{ end }}{{ if or (not .IsExported) .Discriminates }}{{ camelize .Name }}{{ else }}{{ pascalize .Name }}{{ end }}) Validate(formats strfmt.Registry) error {
return nil
}
{{- else }}{{/* {{ .Name }} does not implement the runtime.Validatable interface: noop */}}
{{- end }}
{{- if and (not (or .IsInterface .IsStream)) (or .HasContextValidations) }}
{{ template "schemacontextvalidator" . }}
{{- else if not (or .IsInterface .IsStream) }}
// ContextValidate validates this {{ humanize .Name }} based on context it is used {{/* this schema implements the runtime.ContextValidatable interface but has no validations to check */}}
func ({{.ReceiverName}} {{ if or .IsTuple .IsComplexObject .IsAdditionalProperties }}*{{ end }}{{ if or (not .IsExported) .Discriminates }}{{ camelize .Name }}{{ else }}{{ pascalize .Name }}{{ end }}) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
{{- else }}{{/* {{ .Name }} does not implement the runtime.Validatable interface: noop */}}
{{- end }}
{{- end }}
{{- if .WantsMarshalBinary }}
{{ template "marshalBinarySerializer" . }}
{{- end }}
{{- define "mapOrSliceGetter" }}{{/* signature for AdditionalProperties and AdditionalItems getter funcs */}}
{{- if not .IsBaseType }}
{{- if .HasAdditionalProperties }}
{{- with .AdditionalProperties }}
// {{- template "docstring" . }}{{- template "propertyValidationDocString" . }}
{{ pascalize .Name }}() map[string]{{ template "schemaType" . }}
{{- end }}
{{- end }}
{{- with .AdditionalItems }}
// {{- template "docstring" . }}{{- template "propertyValidationDocString" . }}
{{ pascalize .Name }}() []{{ template "schemaType" . }}
{{- end }}
{{- else }}
// AdditionalProperties in base type shoud be handled just like regular properties{{/* TODO(fred): add full support for AdditionalProperties in base type */}}
// At this moment, the base type property is pushed down to the subtype
{{- end }}
{{- end }}

View file

@ -0,0 +1,330 @@
{{ define "schemaBody" }}struct {
{{ range .AllOf }}
{{ if or (and $.IsSubType .IsBaseType .IsExported) .IsAnonymous }}
{{ range .Properties }}
{{ if ne $.DiscriminatorField .Name }}
{{ if or (not $.IsExported) (and $.IsSubType .IsBaseType) }}
{{ if $.IsTuple }}
{{ template "privtuplefield" . }}
{{ else }}
{{template "privstructfield" . }}
{{ end }}
{{ else }}
{{ if $.IsTuple }}
{{ template "tuplefield" . }}
{{ else }}
{{template "structfield" . }}
{{ end }}
{{ end}}
{{ end }}
{{ end }}
{{- if .HasAdditionalProperties }}
{{- if .AdditionalProperties }}
// {{ template "docstring" .AdditionalProperties }}
{{- template "propertyValidationDocString" .AdditionalProperties}}
{{- if and .IsExported (not .IsSubType) }}
{{ pascalize .AdditionalProperties.Name }}
{{- else if or (not .AdditionalProperties.IsExported) (.AdditionalProperties.IsBaseType) }}
{{ camelize .AdditionalProperties.Name }}Field
{{- else }}
{{ .AdditionalProperties.Name }}
{{- end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{- end }}
{{- end }}
{{- if .AdditionalItems }}
// {{ template "docstring" .AdditionalItems }}
{{- template "propertyValidationDocString" .AdditionalItems}}
{{- if and .IsExported (not $.IsSubType) }}{{/* TODO(fred): make sure inherited AdditionalItems are camelized */}}
{{ pascalize .AdditionalItems.Name }}
{{- else }}
{{ .AdditionalItems.Name }}
{{- end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{- end }}
{{ else }}{{/* named type composition */}}
{{ if not (and $.IsBaseType .IsExported) }}{{ .GoType }}{{ end }}
{{ end }}
{{ end }}
{{range .Properties}}
{{ if or (not $.IsExported) ($.IsBaseType) (.IsBaseType) }}
{{ if $.IsTuple }}{{ template "privtuplefield" . }}{{ else }}{{template "privstructfield" . }}{{ end }}{{ else }}{{ if $.IsTuple }}{{ template "tuplefield" . }}{{ else }}{{template "structfield" . }}{{ end }}{{ end}}
{{ end }}
{{ if .HasAdditionalProperties }}
{{- if .AdditionalProperties }}
// {{ template "docstring" .AdditionalProperties }}
{{- template "propertyValidationDocString" .AdditionalProperties}}
{{- if and .IsExported (not .IsSubType) }}
{{ pascalize .AdditionalProperties.Name }}
{{- else }}
{{ pascalize .AdditionalProperties.Name }}Field
{{- end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{ end }}
{{- end }}
{{- if .AdditionalItems }}
// {{ template "docstring" .AdditionalItems }}
{{- template "propertyValidationDocString" .AdditionalItems}}
{{ if and .IsExported (not .IsSubType) }}{{ pascalize .AdditionalItems.Name }}{{ else }}{{ pascalize .AdditionalItems.Name }}Field{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{ end }}
}
{{- end }}
{{ define "subTypeBody" }}struct {
{{- range .AllOf }}
{{- if or (and .IsBaseType .IsExported) .IsAnonymous }}
{{- range .Properties }}
{{- if not $.IsExported }}
{{- if $.IsTuple }}
{{- template "privtuplefield" . }}
{{- else }}
{{- template "privstructfield" . }}
{{- end }}
{{- else }}
{{- if $.IsTuple }}
{{- template "tuplefield" . }}
{{- else }}
{{- template "structfield" . }}
{{- end }}
{{- end }}
{{- end }}
{{- if .HasAdditionalProperties }}
{{- if .AdditionalProperties }}
{{- if .IsExported }}
{{ pascalize .AdditionalProperties.Name }}
{{- else }}
{{ .AdditionalProperties.Name }}
{{- end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{- end }}
{{- end }}
{{- if .AdditionalItems }}
{{- if .IsExported }}
{{ pascalize .AdditionalItems.Name }}
{{- else }}
{{ .AdditionalItems.Name }}
{{- end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{- end }}
{{- else }}
{{- if not (and .IsBaseType .IsExported) }}
{{ .GoType }}
{{- end }}
{{- end }}
{{- end }}
{{ range .Properties }}
{{- if not $.IsExported }}
{{- if $.IsTuple }}
{{ template "privtuplefield" . }}
{{- else }}
{{ template "privstructfield" . }}
{{- end }}
{{- else }}
{{- if $.IsTuple }}
{{ template "tuplefield" . }}
{{- else }}
{{ template "structfield" . }}
{{- end }}
{{- end}}
{{- end }}
{{- if .HasAdditionalProperties }}
{{- if .AdditionalProperties }}
{{- if and .IsExported }}
{{ pascalize .AdditionalProperties.Name }}
{{- else }}
{{ pascalize .AdditionalProperties.Name }}Field
{{- end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{- end }}
{{- end }}
{{- if .AdditionalItems }}
{{- if and .IsExported (not .IsSubType) }}
{{ pascalize .AdditionalItems.Name }}
{{- else }}
{{ pascalize .AdditionalItems.Name }}Field
{{- end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{- end }}
}
{{- end }}
{{ define "withBaseTypeBody" }}struct {
{{ range .AllOf }}
{{ if or (and .IsBaseType .IsExported) .IsAnonymous }}{{ range .Properties }}
{{ if not .IsExported }}{{ if .IsTuple }}{{ template "privtuplefield" . }}{{ else }}{{template "privstructfield" . }}{{ end }}{{ else }}{{ if $.IsTuple }}{{ template "tuplefield" . }}{{ else }}{{template "structfield" . }}{{ end }}{{ end}}
{{ end }}{{ if .HasAdditionalProperties }}{{ if .IsExported }}{{ pascalize .AdditionalProperties.Name }}{{ else }}{{ .AdditionalProperties.Name }}{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"` {{end}}
{{ if .AdditionalItems }}{{ if and .IsExported }}{{ pascalize .AdditionalItems.Name }}{{ else }}{{ .AdditionalItems.Name }}{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{ end }}
{{ else }}
{{ if not (and .IsBaseType .IsExported) }}{{ .GoType }}{{ end }}{{ end }}
{{ end }}
{{range .Properties}}{{ if .IsBaseType }}
{{ if not $.IsExported }}{{ else }}{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`{{ end}}
{{end}}{{ end }}
{{ if .HasAdditionalProperties }}{{ if and .IsExported }}{{ pascalize .AdditionalProperties.Name }}{{ else }}{{ pascalize .AdditionalProperties.Name }}Field{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{ end }}
{{ if .AdditionalItems }}{{ if and .IsExported (not .IsSubType) }}{{ pascalize .AdditionalItems.Name }}{{ else }}{{ pascalize .AdditionalItems.Name }}Field{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{ end }}
}
{{- end }}
{{ define "withoutBaseTypeBody" }}struct {
{{ range .AllOf }}
{{ if .IsAnonymous }}
{{ range .Properties }}
{{ if and .IsExported (not .IsBaseType) }}
{{ if .IsTuple }}
{{ template "tuplefield" . }}
{{ else }}
{{template "structfield" . }}
{{ end }}
{{ else }}
{{ pascalize .Name }} json.RawMessage `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end}}
{{ end }}
{{ if .HasAdditionalProperties }}
{{ if .AdditionalProperties }}
{{ if .IsExported }}{{ pascalize .AdditionalProperties.Name }}{{ else }}{{ .AdditionalProperties.Name }}{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{end}}
{{ end }}
{{ if .AdditionalItems }}
{{ if .IsExported }}{{ pascalize .AdditionalItems.Name }}{{ else }}{{ .AdditionalItems.Name }}{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{ end }}
{{ else }}
{{ if not (and .IsBaseType .IsExported) }}
{{ .GoType }}
{{ end }}
{{ end }}
{{ end }}
{{range .Properties}}
{{ if not .IsBaseType }}
{{ if not $.IsExported }}
{{template "privstructfield" . }}
{{ else }}
{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end}}
{{ else }}
{{ pascalize .Name }} json.RawMessage `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{end}}
{{ end }}
{{ if .HasAdditionalProperties }}
{{ pascalize .AdditionalProperties.Name }}{{ if .IsExported }}Field{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{ end }}
}
{{- end }}
{{ define "withoutBaseTypeBodyOrNonExported" }}struct {
{{ range .AllOf }}
{{ if .IsAnonymous }}
{{ range .Properties }}
{{ if and .IsExported (not .IsBaseType) }}
{{ if .IsTuple }}
{{ template "tuplefield" . }}
{{ else }}
{{template "structfield" . }}
{{ end }}
{{ end}}
{{ end }}
{{ if .HasAdditionalProperties }}
{{ if .AdditionalProperties }}
{{ if .IsExported }}{{ pascalize .AdditionalProperties.Name }}{{ else }}{{ .AdditionalProperties.Name }}{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{end}}
{{ end }}
{{ if .AdditionalItems }}
{{ if .IsExported }}{{ pascalize .AdditionalItems.Name }}{{ else }}{{ .AdditionalItems.Name }}{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{ end }}
{{ else }}
{{ if not (and .IsBaseType .IsExported) }}
{{ .GoType }}
{{ end }}
{{ end }}
{{ end }}
{{range .Properties}}
{{ if not .IsBaseType }}
{{ if not .IsExported }}
{{template "privstructfield" . }}
{{ else }}
{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end}}
{{end}}
{{ end }}
{{ if .HasAdditionalProperties }}
{{ pascalize .AdditionalProperties.Name }}{{ if .IsExported }}Field{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{ end }}}{
{{ range .AllOf }}
{{ if .IsAnonymous }}
{{ range .Properties }}
{{ if not .IsBaseType }}
{{ pascalize .Name }}: {{ .ReceiverName}}.{{ pascalize .Name }},
{{ end }}
{{ end }}
{{ else }}
{{ if not (and .IsBaseType .IsExported) }}
{{ .GoType }}: {{ .ReceiverName }}.{{ .GoType }},
{{ end }}
{{ end }}
{{ end }}
{{ range .Properties }}
{{ if and (not .IsBaseType) .IsExported }}
{{ pascalize .Name }}: {{ .ReceiverName }}.{{ pascalize .Name }},
{{ end }}
{{ end }}
},
{{- end }}
{{ define "withBaseTypeBodyAndNonExported" }}struct{
{{ range .AllOf }}
{{ range .Properties }}
{{ if .IsBaseType }}
{{ pascalize .Name }} {{ template "schemaType" . }} `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end }}
{{ end }}
{{ end }}
{{ range .Properties }}
{{ if or (not .IsExported) .IsBaseType }}
{{ pascalize .Name }} {{ template "schemaType" . }} `json:"{{ .Name }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end }}
{{end}}} {
{{ range .AllOf }}
{{ range .Properties }}
{{ if .IsBaseType }}
{{ pascalize .Name }}:
{{ if ne .DiscriminatorField .Name }}
{{ .ReceiverName }}.{{ if .IsSubType}}{{ camelize .Name }}Field{{ else }}{{ pascalize .Name }}(){{ end }},
{{ else }}
{{ .ReceiverName }}.{{pascalize .Name}}(),
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ range .Properties }}
{{ if or (not .IsExported) .IsBaseType }}
{{ pascalize .Name }}: {{ .ReceiverName }}.{{ if .IsBaseType}}{{ camelize .Name }}Field{{ else }}{{ pascalize .Name }}{{ end }},
{{ end }}
{{ end }} },
{{- end }}
{{ define "withoutAdditionalBody" }}struct {
{{ range .AllOf }}
{{ if or (and $.IsSubType .IsBaseType .IsExported) .IsAnonymous }}{{ range .Properties }}
{{ if ne $.DiscriminatorField .Name }}{{ if or (not $.IsExported) (and $.IsSubType .IsBaseType) }}{{ if $.IsTuple }}{{ template "privtuplefield" . }}{{ else }}{{template "privstructfield" . }}{{ end }}{{ else }}{{ if $.IsTuple }}{{ template "tuplefield" . }}{{ else }}{{template "structfield" . }}{{ end }}{{ end}}{{ end }}
{{ end }}
{{ else }}
{{ if not (and .IsBaseType .IsExported) }}{{ .GoType }}{{ end }}{{ end }}
{{ end }}
{{range .Properties}}
{{ if or (not $.IsExported) (and $.IsSubType .IsBaseType) }}{{ if $.IsTuple }}{{ template "privtuplefield" . }}{{ else }}{{template "privstructfield" . }}{{ end }}{{ else }}{{ if $.IsTuple }}{{ template "tuplefield" . }}{{ else }}{{template "structfield" . }}{{ end }}{{ end}}
{{end}}
}
{{- end }}
{{ define "JustBaseTypeBody" }}struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
{{ range .AllOf }}
{{ if .IsBaseType }}
{{ range .Properties }}
{{ if .IsExported }}
{{ if .IsTuple }}
{{ template "tuplefield" . }}
{{ else }}
{{template "structfield" . }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
}
{{- end }}

View file

@ -0,0 +1,21 @@
{{ define "schemaEmbedded" }}
type {{ pascalize .Name }} struct {
{{ if .ElemType.IsNullable }}*{{ end }}{{ .ElemType.GoType }}
}
func ({{.ReceiverName }} {{ if or .IsTuple .IsComplexObject }}*{{ end }}{{ if .Discriminates }}{{ camelize .Name }}{{ else if .IsExported }}{{ pascalize .Name }}{{ else }}{{ .Name }}{{ end }}) Validate(formats strfmt.Registry) error {
var f interface{} = {{ .ReceiverName }}.{{ dropPackage .ElemType.GoType }}
if v, ok := f.(runtime.Validatable) ; ok {
return v.Validate(formats)
}
return nil
}
func ({{.ReceiverName }} {{ if or .IsTuple .IsComplexObject }}*{{ end }}{{ if .Discriminates }}{{ camelize .Name }}{{ else if .IsExported }}{{ pascalize .Name }}{{ else }}{{ .Name }}{{ end }}) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var f interface{} = {{ .ReceiverName }}.{{ dropPackage .ElemType.GoType }}
if v, ok := f.(runtime.ContextValidatable) ; ok {
return v.ContextValidate(ctx, formats)
}
return nil
}
{{- end }}

View file

@ -0,0 +1,53 @@
{{ define "schemaPolymorphic" }}
type {{ pascalize .Name }} interface {
{{- if not (or .IsInterface .IsStream) }}{{/*
A base type is always Validatable.
Under normal conditions, we can't have a base type rendered a .IsStream or .IsInterface: this check is just for sanity check).
In the definition of the base type itself, this means that the unexported struct holding
the definition of the base type has a Validate() func and a ContextValitate() func.
*/}}
runtime.Validatable
runtime.ContextValidatable
{{- end }}
{{ range .AllOf }}
{{- if .IsAnonymous }}
{{ range .Properties }}
{{ if $.IsTuple }}{{ template "tuplefieldIface" . }}{{ else }}{{template "structfieldIface" . }}{{ end }}
{{- end }}
{{ template "mapOrSliceGetter" . }}
{{- else }}
{{ .GoType }}
{{- end }}
{{- end }}
{{ range .Properties }}
{{- if $.IsTuple }}
{{ template "tuplefieldIface" . }}
{{- else }}
{{ template "structfieldIface" . }}
{{- end }}
{{- end }}
{{ template "mapOrSliceGetter" . }}
}
type {{ camelize .Name }} {{ template "schemaBody" . }}{{/* unexported implementation of the interface (TODO(fred): atm, this is not used, issue #232) */}}
{{- range .Properties }}
// {{ pascalize .Name}} gets the {{ humanize .Name }} of this polymorphic type
func ({{ $.ReceiverName}} *{{ camelize $.Name}}) {{ pascalize .Name}}() {{ template "schemaType" . }}{
{{- if eq $.DiscriminatorField .Name }}
return {{ printf "%q" $.DiscriminatorValue }}
{{- else }}
return {{ $.ReceiverName }}.{{camelize .Name}}Field
{{- end }}
}
// Set{{ pascalize .Name}} sets the {{ humanize .Name }} of this polymorphic type
func ({{ $.ReceiverName}} *{{ camelize $.Name}}) Set{{ pascalize .Name}}(val {{ template "schemaType" . }}) {
{{- if ne $.DiscriminatorField .Name }}
{{ $.ReceiverName }}.{{camelize .Name}}Field = val
{{- end }}
}
{{- end }}{{/* TODO(fred): AdditionalProperties */}}
{{ template "polymorphicSerializer" . }}
{{- end }}

View file

@ -0,0 +1,29 @@
{{ define "schemaType" }}
{{- if and (or (gt (len .AllOf) 0) .IsAnonymous) ( not .IsMap) }}
{{- template "schemaBody" . }}
{{- else }}
{{- if and (not .IsMap) .IsNullable (not .IsSuperAlias) }}*{{ end }}
{{- if .IsSuperAlias }} = {{ end }}
{{- .GoType }}
{{- end}}
{{- end }}
{{ define "dereffedSchemaType" }}
{{- if and (or (gt (len .AllOf) 0) .IsAnonymous) ( not .IsMap) }}
{{- template "schemaBody" . }}
{{- else }}
{{- .GoType }}
{{- end}}
{{- end }}
{{ define "typeSchemaType" }}
{{- if and (or (gt (len .AllOf) 0) .IsAnonymous) ( not .IsMap) ( not .IsSuperAlias ) }}
{{- template "schemaBody" . }}
{{- else if and .IsSubType ( not .IsSuperAlias ) }}
{{- template "subTypeBody" . }}
{{- else }}
{{- if and (not .IsMap) .IsNullable (not .IsSuperAlias) }}*{{ end }}
{{- if .IsSuperAlias }} = {{ end }}
{{- if .AliasedType }}{{ .AliasedType }}{{ else }}{{ .GoType }}{{ end }}
{{- end}}
{{- end }}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,94 @@
{{ define "additionalPropertiesSerializer" }}
// UnmarshalJSON unmarshals this object with additional properties from JSON
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(data []byte) error {
// stage 1, bind the properties
var stage1 {{ template "withoutAdditionalBody" . }}
if err := json.Unmarshal(data, &stage1); err != nil {
return err
}
var rcv {{ pascalize .Name }}
{{ range .Properties }}
rcv.{{ pascalize .Name }} = stage1.{{ pascalize .Name }}
{{- end }}
*{{ .ReceiverName }} = rcv
// stage 2, remove properties and add to map
stage2 := make(map[string]{{ if .AdditionalProperties }}json.RawMessage{{ else }}interface{}{{ end }})
if err := json.Unmarshal(data, &stage2); err != nil {
return err
}
{{ range .Properties }}
delete(stage2, {{ printf "%q" .Name }})
{{- end }}
{{- if .AdditionalProperties }}
// stage 3, add additional properties values
if len(stage2) > 0 {
result := make(map[string]{{ template "schemaType" .AdditionalProperties }})
for k, v := range stage2 {
var toadd {{ template "schemaType" .AdditionalProperties }}
if err := json.Unmarshal(v, {{if not .AdditionalProperties.IsNullable }}&{{ end }}toadd); err != nil {
return err
}
result[k] = toadd
}
{{ .ValueExpression }} = result
}
{{- else }}
{{ .ValueExpression }} = stage2
{{- end }}
return nil
}
// MarshalJSON marshals this object with additional properties into a JSON object
func ({{.ReceiverName}} {{ pascalize .Name }}) MarshalJSON() ([]byte, error) {
var stage1 {{ template "withoutAdditionalBody" . }}
{{ range .Properties }}
stage1.{{ pascalize .Name }} = {{ .ValueExpression }}
{{- end }}
// make JSON object for known properties
props, err := json.Marshal(stage1)
if err != nil {
return nil, err
}
if len({{ .ValueExpression }}) == 0 { // no additional properties
return props, nil
}
// make JSON object for the additional properties
additional, err := json.Marshal({{ .ValueExpression }})
if err != nil {
return nil, err
}
if len(props) < 3 { // "{}": only additional properties
return additional, nil
}
// concatenate the 2 objects
return swag.ConcatJSON(props, additional), nil
}
{{- end }}
{{ define "noAdditionalPropertiesSerializer" }}
// UnmarshalJSON unmarshals this object while disallowing additional properties from JSON
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(data []byte) error {
var props {{ template "withoutAdditionalBody" . }}
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
if err := dec.Decode(&props); err != nil {
return err
}
{{- $rcv := .ReceiverName }}
{{ range .Properties }}
{{ .ReceiverName }}.{{ pascalize .Name }} = props.{{ pascalize .Name }}
{{- end }}
return nil
}
{{- end }}

View file

@ -0,0 +1,11 @@
{{ define "aliasedSerializer" }}
// UnmarshalJSON sets a {{ pascalize .Name }} value from JSON input
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(b []byte) error {
return ((*{{ .AliasedType }})({{ .ReceiverName}})).UnmarshalJSON(b)
}
// MarshalJSON retrieves a {{ pascalize .Name }} value as JSON output
func ({{.ReceiverName}} {{ pascalize .Name }}) MarshalJSON() ([]byte, error) {
return ({{ .AliasedType }}({{ .ReceiverName}})).MarshalJSON()
}
{{- end }}

View file

@ -0,0 +1,180 @@
{{ define "allOfSerializer" }}
{{- $receiverName := .ReceiverName }}
// UnmarshalJSON unmarshals this object from a JSON structure
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(raw []byte) error {
{{- range .AllOf }}
// {{ pascalize .Name }}
{{- if and .IsAnonymous .Properties }}{{/* unmarshalling properties in all of anonymous objects */}}
{{- $part := pascalize .Name }}
var data{{ $part }} struct {
{{- range .Properties }}
{{- if not .IsBaseType }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{ else }}
{{ if not $.IsExported }}
{{ template "privstructfield" . }}
{{ else }}
{{ pascalize .Name}} json.RawMessage `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end }}
{{ end }}
{{- end }}
{{- if .HasAdditionalProperties }}
{{ pascalize .AdditionalProperties.Name }}{{ if not .IsExported }}Field{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{- end }}
{{- if .AdditionalItems }}
{{ pascalize .AdditionalItems.Name }}{{ if or (not .IsExported) .IsSubType }}Field{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{- end }}
}
if err := swag.ReadJSON(raw, &data{{ $part }}); err != nil {
return err
}
{{ range .Properties }}
{{ $receiverName }}.{{ pascalize .Name }} = data{{ $part }}.{{ pascalize .Name }}
{{ end }}
{{- else if .IsAnonymous }}
var {{ varname .Name }} {{ .GoType }}
if err := {{ if .IsBaseType}}Unmarshal{{ .GoType }}(bytes.NewBuffer(raw), &{{ varname .Name }}){{ else }} swag.ReadJSON(raw, &{{ varname .Name }}){{ end }}; err != nil {
return err
}
{{ .ValueExpression }} = {{ varname .Name }}
{{- end }}
{{- if not .IsAnonymous }}{{/* unmarshalling allOf named objects */}}
var {{ varname .Name }} {{ .GoType }}
if err := {{ if .IsBaseType}}Unmarshal{{ .GoType }}(bytes.NewBuffer(raw), &{{ varname .Name }}){{ else }} swag.ReadJSON(raw, &{{ varname .Name }}){{ end }}; err != nil {
return err
}
{{ .ReceiverName }}.{{ dropPackage .GoType }} = {{ varname .Name }}
{{ end }}
{{ end }}
{{- if .Properties }}
// now for regular properties
{{- $part := pascalize .Name }}
var props{{ $part }} struct {
{{- range .Properties }}
{{- if not .IsBaseType }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{- else }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} json.RawMessage `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{- end }}
{{ end }}
}
if err := swag.ReadJSON(raw, &props{{ $part }}); err != nil {
return err
}
{{- range .Properties }}
{{ $receiverName }}.{{ pascalize .Name }} = props{{ $part }}.{{ pascalize .Name }}
{{ end }}
{{- end }}
{{ if .HasAdditionalProperties }}
// TODO: AdditionalProperties
{{- end }}
{{- if .AdditionalItems }}
// TODO: AdditionalItems
{{- end }}
return nil
}
// MarshalJSON marshals this object to a JSON structure
func ({{.ReceiverName}} {{ pascalize .Name }}) MarshalJSON() ([]byte, error) {
_parts := make([][]byte, 0, {{ len .AllOf }})
{{ range .AllOf }}
{{- if and .IsAnonymous .Properties }}
{{- $part := pascalize .Name }}
var data{{ $part }} struct {
{{- range .Properties }}
{{- if not .IsBaseType }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{- else }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} json.RawMessage `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{- end }}
{{ end }}
{{- if .HasAdditionalProperties }}
{{ pascalize .AdditionalProperties.Name }}{{ if not .IsExported }}Field{{ end }} map[string]{{ template "schemaType" .AdditionalProperties }} `json:"-"`
{{- end }}
{{- if .AdditionalItems }}
{{ pascalize .AdditionalItems.Name }}{{ if or (not .IsExported) .IsSubType }}Field{{ end }} []{{ template "schemaType" .AdditionalItems }} `json:"-"`
{{- end }}
}
{{ range .Properties }}
data{{ $part }}.{{ pascalize .Name }} = {{ $receiverName }}.{{ pascalize .Name }}
{{ end }}
jsonData{{ $part }}, err{{ $part }} := swag.WriteJSON(data{{ $part }})
if err{{ $part }} != nil {
return nil, err{{ $part }}
}
_parts = append(_parts, jsonData{{ $part }})
{{- else if .IsAnonymous }}{{/* unmarshalling anonymous type composition */}}
{{ varname .Name }}, err := swag.WriteJSON({{ .ValueExpression }})
if err != nil {
return nil, err
}
_parts = append(_parts, {{ varname .Name }})
{{- end }}
{{- if not .IsAnonymous }}
{{ varname .Name }}, err := swag.WriteJSON({{ $receiverName }}.{{ dropPackage .GoType }})
if err != nil {
return nil, err
}
_parts = append(_parts, {{ varname .Name }})
{{- end }}
{{- end }}
{{- if .Properties }}
// now for regular properties
{{- $part := pascalize .Name }}
var props{{ $part }} struct {
{{- range .Properties }}
{{- if not .IsBaseType }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} {{ template "schemaType" . }} `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{- else }}
{{- if not $.IsExported }}
{{ template "privstructfield" . }}
{{- else }}
{{ pascalize .Name}} json.RawMessage `json:"{{ .OriginalName }}{{ if and (not .Required) .IsEmptyOmitted }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{- end }}
{{- end }}
{{ end }}
}
{{- range .Properties }}
props{{ $part }}.{{ pascalize .Name }} = {{ $receiverName }}.{{ pascalize .Name }}
{{ end }}
jsonDataProps{{ $part }}, err{{ $part }} := swag.WriteJSON(props{{ $part }})
if err{{ $part }} != nil {
return nil, err{{ $part }}
}
_parts = append(_parts, jsonDataProps{{ $part }})
{{- end }}
{{- if .HasAdditionalProperties }}
{{- end }}
{{- if .HasAdditionalItems }}
{{- end }}
return swag.ConcatJSON(_parts...), nil
}
{{- end }}

View file

@ -0,0 +1,69 @@
{{ define "polymorphicSerializer" }}
// Unmarshal{{ pascalize .Name }}Slice unmarshals polymorphic slices of {{ pascalize .Name }}
func Unmarshal{{ pascalize .Name }}Slice(reader io.Reader, consumer runtime.Consumer) ([]{{ pascalize .Name }}, error) {
var elements []json.RawMessage
if err := consumer.Consume(reader, &elements); err != nil {
return nil, err
}
var result []{{ pascalize .Name }}
for _, element := range elements {
obj, err := unmarshal{{ pascalize .Name }}(element, consumer)
if err != nil {
return nil, err
}
result = append(result, obj)
}
return result, nil
}
// Unmarshal{{ pascalize .Name }} unmarshals polymorphic {{ pascalize .Name }}
func Unmarshal{{ pascalize .Name }}(reader io.Reader, consumer runtime.Consumer) ({{ pascalize .Name }}, error) {
// we need to read this twice, so first into a buffer
data, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
return unmarshal{{ pascalize .Name }}(data, consumer)
}
func unmarshal{{ pascalize .Name }}(data []byte, consumer runtime.Consumer) ({{ pascalize .Name }}, error) {
buf := bytes.NewBuffer(data)
{{ if .Discriminates }} buf2 := bytes.NewBuffer(data) {{ end }}
// the first time this is read is to fetch the value of the {{ .DiscriminatorField }} property.
var getType struct { {{ pascalize .DiscriminatorField }} string `json:{{ printf "%q" .DiscriminatorField }}` }
if err := consumer.Consume(buf, &getType); err != nil {
return nil, err
}
if err := validate.RequiredString({{ printf "%q" .DiscriminatorField }}, "body", getType.{{ pascalize .DiscriminatorField }}); err != nil {
return nil, err
}
// The value of {{ .DiscriminatorField }} is used to determine which type to create and unmarshal the data into
switch getType.{{ pascalize .DiscriminatorField }} {
{{- range $k, $v := .Discriminates }}
case {{ printf "%q" $k }}:
var result {{ if eq (upper (pascalize $.Name)) (upper $v) }}{{ camelize $.Name }}{{ else }}{{ $v }}{{ end }}
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
{{- end }}
}
return nil, errors.New(422, "invalid {{ .DiscriminatorField }} value: %q", getType.{{ pascalize .DiscriminatorField }})
}
{{- end }}
{{ define "baseTypeSerializer" }}
// Unmarshal{{ pascalize .Name }} unmarshals polymorphic {{ pascalize .Name }}
func Unmarshal{{ pascalize .Name }}(reader io.Reader, consumer runtime.Consumer) ({{ pascalize .Name }}, error) {
return Unmarshal{{ pascalize .GoType }}(reader, consumer)
}
// Unmarshal{{ pascalize .Name }}Slice unmarshals polymorphic slices of {{ pascalize .Name }}
func Unmarshal{{ pascalize .Name }}Slice(reader io.Reader, consumer runtime.Consumer) ([]{{ pascalize .Name }}, error) {
return Unmarshal{{ pascalize .GoType }}Slice(reader, consumer)
}
{{- end }}

View file

@ -0,0 +1,19 @@
{{ define "marshalBinarySerializer" }}
// MarshalBinary interface implementation
func ({{.ReceiverName}} *{{ pascalize .Name }}) MarshalBinary() ([]byte, error) {
if {{ .ReceiverName }} == nil {
return nil, nil
}
return swag.WriteJSON({{ .ReceiverName }})
}
// UnmarshalBinary interface implementation
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalBinary(b []byte) error {
var res {{ pascalize .Name }}
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*{{ .ReceiverName }} = res
return nil
}
{{- end }}

View file

@ -0,0 +1,15 @@
{{ define "schemaSerializer" }}{{/* switches to the appropriate serializer for any given type */}}
{{- if and .IsSubType (not .HasBaseType) }}
{{ template "hasDiscriminatedSerializer" . }}
{{- else if .IsTuple }}
{{ template "tupleSerializer" . }}
{{- else if .HasBaseType }}
{{ template "hasDiscriminatedSerializer" . }}
{{- else if .IsAdditionalProperties }}
{{ template "additionalPropertiesSerializer" . }}
{{- else if and (gt (len .AllOf) 0) (not .IsSubType ) }}
{{ template "allOfSerializer" . }}
{{- else if and .IsComplexObject .StrictAdditionalProperties }}
{{ template "noAdditionalPropertiesSerializer" . }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,172 @@
{{ define "hasDiscriminatedSerializer" }}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(raw []byte) error {
var data {{ template "withoutBaseTypeBody" . }}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
{{ if or .IsBaseType .IsSubType }}
var base {{ template "JustBaseTypeBody" . }}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
{{- end }}
{{ range .AllOf }}
{{- if not .IsBaseType }}
{{ range .Properties }}
{{- if or .IsBaseType (not .IsExported) }}
{{- if not .Required }}
var allOf{{ pascalize .Name }} {{ if .IsArray }}[]{{ pascalize .Items.GoType }}{{ else }}{{ pascalize .GoType }}{{ end }}
if string(data.{{ pascalize .Name }}) != "null" {
{{ camelize .Name }}, err := Unmarshal{{ if .IsArray }}{{ pascalize .Items.GoType }}Slice{{ else }}{{ pascalize .GoType }}{{ end }}(bytes.NewBuffer(data.{{ pascalize .Name }}), runtime.JSONConsumer())
if err != nil && err != io.EOF {
return err
}
allOf{{ pascalize .Name }} = {{ camelize .Name }}
}
{{- else }}
allOf{{ pascalize .Name }}, err := Unmarshal{{ if .IsArray }}{{ pascalize .Items.GoType }}Slice{{ else }}{{ pascalize .GoType }}{{ end }}(bytes.NewBuffer(data.{{ pascalize .Name }}), runtime.JSONConsumer())
if err != nil && err != io.EOF {
return err
}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{ range .Properties }}
{{- if or .IsBaseType (not .IsExported) }}
{{- if not .Required }}
var prop{{ pascalize .Name }} {{ if .IsArray }}[]{{ pascalize .Items.GoType }}{{ else }}{{ pascalize .GoType }}{{ end }}
if string(data.{{ pascalize .Name }}) != "null" {
{{ camelize .Name }}, err := Unmarshal{{ if .IsArray }}{{ pascalize .Items.GoType }}Slice{{ else }}{{ pascalize .GoType }}{{ end }}(bytes.NewBuffer(data.{{ pascalize .Name }}), runtime.JSONConsumer())
if err != nil && err != io.EOF {
return err
}
prop{{ pascalize .Name }} = {{ camelize .Name }}
}
{{- else }}
prop{{ pascalize .Name }}, err := Unmarshal{{ if .IsArray }}{{ pascalize .Items.GoType }}Slice{{ else }}{{ pascalize .GoType }}{{ end }}(bytes.NewBuffer(data.{{ pascalize .Name }}), runtime.JSONConsumer())
if err != nil && err != io.EOF {
return err
}
{{- end }}
{{- end }}
{{- end }}
var result {{ pascalize .Name }}
{{ range $_, $parent := .AllOf }}
{{- if $parent.IsAnonymous }}
{{- if $parent.IsBaseType }}
{{ range $idx, $val := $parent.Properties }}
{{- if ne $parent.DiscriminatorField $val.Name }}
{{- if $val.IsExported }}
result.{{ camelize $val.Name }}Field = base.{{ pascalize $val.Name }}
{{- else }}
result.{{ camelize $val.Name }}Field = allOf{{ pascalize $val.Name }}
{{- end }}
{{- else }}
if base.{{ pascalize $val.Name }} != result.{{ pascalize $val.Name }}() {
/* Not the type we're looking for. */
return errors.New(422, "invalid {{$val.Name}} value: %q", base.{{ pascalize $val.Name }})
}
{{- end }}
{{- end }}
{{- else }}
{{ range $idx, $val := $parent.Properties }}
{{- if $val.IsBaseType }}
result.{{ camelize $val.Name }}Field = allOf{{ pascalize $val.Name }}
{{- else }}
result.{{ pascalize $val.Name }} = data.{{ pascalize $val.Name }}
{{- end }}
{{- end }}
{{- end }}
{{- else }}
{{- if and $parent.IsBaseType $parent.IsExported }}
{{ range $idx, $val := $parent.Properties }}
{{- if ne $parent.DiscriminatorField $val.Name }}
{{- if $val.IsExported }}
result.{{ camelize $val.Name }}Field = base.{{ pascalize $val.Name }}
{{ else }}
result.{{ camelize $val.Name }}Field = allOf{{ pascalize $val.Name }}
{{- end }}
{{- else }}
if base.{{ pascalize $val.Name }} != result.{{ pascalize $val.Name }}() {
/* Not the type we're looking for. */
return errors.New(422, "invalid {{$val.Name}} value: %q", base.{{ pascalize $val.Name }})
}
{{- end }}
{{- end }}
{{- else }}
result.{{ $parent.GoType }} = data.{{ $parent.GoType }}
{{- end }}
{{- end }}
{{- end }}
{{ range .Properties }}
// {{ .Name }}
result.{{ if .IsBaseType }}{{ camelize .Name }}Field{{ else }}{{ pascalize .Name }}{{ end }} = {{ if .IsBaseType }}prop{{ pascalize .Name }}{{ else }}data.{{ pascalize .Name}}{{ end }}
{{ end }}
*{{ .ReceiverName }} = result
{{ if .IsAdditionalProperties }}
// Additional Properties: read raw, remove named properties, and add to map
rawProps := make(map[string]{{ if .AdditionalProperties }}json.RawMessage{{ else }}interface{}{{ end }})
if err := json.Unmarshal(raw, &rawProps); err != nil {
return err
}
{{ range .Properties }}
delete(rawProps, {{ printf "%q" .Name }})
{{- end }}
{{ if .AdditionalProperties }}
if len(rawProps) > 0 {
{{ .ValueExpression }} = make(map[string]{{ template "schemaType" .AdditionalProperties }})
for k, v := range rawProps {
var toadd {{ template "schemaType" .AdditionalProperties }}
if err := json.Unmarshal(v, {{if not .AdditionalProperties.IsNullable }}&{{ end }}toadd); err != nil {
return err
}
{{ .ValueExpression }}[k] = toadd
}
}
{{- else }}
{{ .ValueExpression }} = rawProps
{{- end }}
{{- end }}
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func ({{.ReceiverName}} {{ pascalize .Name }}) MarshalJSON() ([]byte, error) { {{ $receiverName := .ReceiverName }}
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal({{ template "withoutBaseTypeBodyOrNonExported" . }})
if err != nil {
return nil, err
}
b2, err = json.Marshal({{ template "withBaseTypeBodyAndNonExported" . }})
if err != nil {
return nil, err
}
{{ if .IsAdditionalProperties }}
if len({{ .ValueExpression }}) > 0 {
// make JSON object for the additional properties
b3, err = json.Marshal({{ .ValueExpression }})
if err != nil {
return nil, err
}
}
{{- end }}
return swag.ConcatJSON(b1, b2, b3), nil
}
{{- end }}

View file

@ -0,0 +1,66 @@
{{ define "tupleSerializer" }}
// UnmarshalJSON unmarshals this tuple type from a JSON array
func ({{.ReceiverName}} *{{ pascalize .Name }}) UnmarshalJSON(raw []byte) error {
// stage 1, get the array but just the array
var stage1 []json.RawMessage
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&stage1); err != nil {
return err
}
// stage 2: hydrates struct members with tuple elements
{{- if .AdditionalItems }}
var lastIndex int
{{ end }}
{{ range $idx, $val := .Properties }}if len(stage1) > {{ $idx }} {
var data{{ pascalize .Name }} {{ template "dereffedSchemaType" . }}
buf = bytes.NewBuffer(stage1[{{ $idx }}])
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data{{ pascalize .Name }}); err != nil {
return err
}
{{ .ReceiverName }}.{{ if .IsExported }}{{ pascalize .Name }}{{ else }}{{ camelize .Name }}{{ end }} = {{ if .IsNullable }}&{{ end }}data{{ pascalize .Name }}
{{ if $.AdditionalItems }}
lastIndex = {{ $idx }}
{{ end }}
}
{{ end }}
{{ if .AdditionalItems }}
// stage 3: hydrates AdditionalItems
if len(stage1) > lastIndex+1 {
for _, val := range stage1[lastIndex+1:] {
var toadd {{ template "schemaType" .AdditionalItems }}
buf = bytes.NewBuffer(val)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode({{ if not .AdditionalItems.IsNullable }}&{{ end }}toadd); err != nil {
return err
}
{{- with .AdditionalItems }}
{{ $.ValueExpression }}.{{- if .IsExported }}{{ pascalize .Name }}{{ else }}{{ camelize .Name }}{{ end }} = append({{ $.ValueExpression }}.{{- if .IsExported }}{{ pascalize .Name }}{{ else }}{{ camelize .Name }}{{ end }}, toadd)
{{- end }}
}
}
{{- end }}
return nil
}
// MarshalJSON marshals this tuple type into a JSON array
func ({{.ReceiverName}} {{ pascalize .Name }}) MarshalJSON() ([]byte, error) {
data := []interface{}{
{{ range .Properties -}}
{{.ReceiverName}}.{{ pascalize .Name }},
{{- end }}
}
{{ with .AdditionalItems }}
for _, v := range {{ $.ValueExpression }}.{{ if .IsExported }}{{ pascalize .Name }}{{ else }}{{ camelize .Name }}{{ end }} {
data = append(data, v)
}
{{- end }}
return json.Marshal(data)
}
{{- end }}

View file

@ -0,0 +1,205 @@
// Code generated by go-swagger; DO NOT EDIT.
// Auto configures api handlers Implementations.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .APIPackage }}
import (
"context"
"crypto/tls"
"io"
"log"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/runtime/security"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
{{ with .GenOpts }}
//go:generate swagger generate server --target {{ .TargetPath }} --name {{ .Name }} --spec {{ .SpecPath }}
{{- if .APIPackage }}{{ if ne .APIPackage "operations" }} --api-package {{ .APIPackage }}{{ end }}{{ end }}
{{- if .ModelPackage }}{{ if ne .ModelPackage "models" }} --model-package {{ .ModelPackage }}{{ end }}{{ end }}
{{- if .ServerPackage }}{{ if ne .ServerPackage "restapi"}} --server-package {{ .ServerPackage }}{{ end }}{{ end }}
{{- if .ClientPackage }}{{ if ne .ClientPackage "client" }} --client-package {{ .ClientPackage }}{{ end }}{{ end }}
{{- if .ImplementationPackage }} --implementation-package {{ .ImplementationPackage }}{{ end }}
{{- if .TemplateDir }} --template-dir {{ .TemplateDir }}{{ end }}
{{- range .Operations }} --operation {{ . }}{{ end }}
{{- range .Tags }} --tags {{ . }}{{ end }}
{{- if .Principal }} --principal {{ .Principal }}{{ end }}
{{- if .DefaultScheme }}{{ if ne .DefaultScheme "http" }} --default-scheme {{ .DefaultScheme }}{{ end }}{{ end }}
{{- range .Models }} --model {{ . }}{{ end }}
{{- if or (not .IncludeModel) (not .IncludeValidator) }} --skip-models{{ end }}
{{- if or (not .IncludeHandler) (not .IncludeParameters ) (not .IncludeResponses) }} --skip-operations{{ end }}
{{- if not .IncludeSupport }} --skip-support{{ end }}
{{- if not .IncludeMain }} --exclude-main{{ end }}
{{- if .ExcludeSpec }} --exclude-spec{{ end }}
{{- if .DumpData }} --dump-data{{ end }}
{{- if .StrictResponders }} --strict-responders{{ end }}
{{ end }}
// This file auto configures the api backend implementation.
// {{.ImplementationPackageAlias}} package must already exist.
// {{.ImplementationPackageAlias}}.New() is implemented by user, and must return an object
// or interface that implements Handler interface defined below.
var Impl Handler = {{.ImplementationPackageAlias}}.New()
// Handler handles all api server backend configurations and requests
type Handler interface{
{{- if .SecurityDefinitions }}
Authable
{{- end }}
Configurable
{{ range .OperationGroups -}}
{{ pascalize .Name }}Handler
{{ end -}}
}
// Configurable handles all server configurations
type Configurable interface {
ConfigureFlags(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API)
ConfigureTLS(tlsConfig *tls.Config)
ConfigureServer(s *http.Server, scheme, addr string)
CustomConfigure(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API)
SetupMiddlewares(handler http.Handler) http.Handler
SetupGlobalMiddleware(handler http.Handler) http.Handler
}
{{- if .SecurityDefinitions }}
// Authable handles server authentication
type Authable interface{
{{- range .SecurityDefinitions }}
{{- if .IsBasicAuth }}
// Applies when the Authorization header is set with the Basic scheme
{{ pascalize .ID }}Auth(user string, pass string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error)
{{- else if .IsAPIKeyAuth }}
// Applies when the "{{ .Name }}" {{ .Source }} is set
{{ pascalize .ID }}Auth(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error)
{{- else if .IsOAuth2 }}
{{ pascalize .ID }}Auth(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error)
{{- end }}
{{- end }}
}
{{- end }}
{{- $package := .Package }}
{{- $apipackagealias := .APIPackageAlias }}
{{ range .OperationGroups -}}
/* {{ pascalize .Name }}Handler {{ .Description }} */
type {{ pascalize .Name }}Handler interface {
{{ range .Operations -}}
{{ if .Summary -}}
/* {{ pascalize .Name }} {{ .Summary }} */
{{ else if .Description -}}
/* {{ pascalize .Name }} {{ .Description }} */
{{ end -}}
{{ pascalize .Name }}(params {{ if ne .Package $package }}{{ .PackageAlias }}{{ else }}{{- $apipackagealias }}{{ end }}.
{{- pascalize .Name }}Params {{- if .Authorized}}, principal {{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}{{end}})
{{- if $.GenOpts.StrictResponders }} {{.Package}}.{{ pascalize .Name }}Responder {{ else }} middleware.Responder {{ end }}
{{ end -}}
}
{{ end }}
func configureFlags(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API) {
Impl.ConfigureFlags(api)
}
func configureAPI(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API) http.Handler {
api.ServeError = errors.ServeError
api.UseSwaggerUI()
{{ range .Consumes }}
{{- if .Implementation }}
api.{{ pascalize .Name }}Consumer = {{ .Implementation }}
{{- else }}
api.{{ pascalize .Name }}Consumer = runtime.ConsumerFunc(func(r io.Reader, target interface{}) error {
return Impl.{{ pascalize .Name }}Consume(r, target)
})
{{- end }}
{{- end }}
{{ range .Produces }}
{{- if .Implementation }}
api.{{ pascalize .Name }}Producer = {{ .Implementation }}
{{- else }}
api.{{ pascalize .Name }}Producer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return Impl.{{ pascalize .Name }}Produce(w, target)
})
{{- end }}
{{- end}}
{{ range .SecurityDefinitions }}
{{- if .IsBasicAuth }}
// Applies when the Authorization header is set with the Basic scheme
api.{{ pascalize .ID }}Auth = func(user string, pass string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return Impl.{{ pascalize .ID }}Auth(user, pass)
}
{{- else if .IsAPIKeyAuth }}
// Applies when the "{{ .Name }}" {{ .Source }} is set
api.{{ pascalize .ID }}Auth = func(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return Impl.{{ pascalize .ID }}Auth(token)
}
{{- else if .IsOAuth2 }}
api.{{ pascalize .ID }}Auth = func(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return Impl.{{ pascalize .ID }}Auth(token, scopes)
}
{{- end }}
{{- end }}
{{- $package := .Package }}
{{- $apipackagealias := .APIPackageAlias }}
{{ range .Operations }}
api.{{ if ne .Package $package }}{{pascalize .Package}}{{ end }}{{ pascalize .Name }}Handler =
{{- if ne .Package $package }}
{{- .PackageAlias }}.{{- pascalize .Name }}HandlerFunc(func(params {{ .PackageAlias }}.{{- pascalize .Name }}Params
{{- else }}
{{- $apipackagealias }}.{{- pascalize .Name }}HandlerFunc(func(params {{ $apipackagealias }}.{{- pascalize .Name }}Params
{{- end }}
{{- if .Authorized}}, principal {{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}{{end}})
{{- if $.GenOpts.StrictResponders }} {{.Package}}.{{ pascalize .Name }}Responder { {{ else }} middleware.Responder { {{ end }}
return Impl.{{ pascalize .Name }}(params {{- if .Authorized}}, principal {{ end }})
})
{{- end }}
api.PreServerShutdown = func() { }
api.ServerShutdown = func() { }
// CustomConfigure can override or add to configurations set above
Impl.CustomConfigure(api)
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
}
// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
// Make all necessary changes to the TLS configuration here.
Impl.ConfigureTLS(tlsConfig)
}
// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix".
func configureServer(s *http.Server, scheme, addr string) {
Impl.ConfigureServer(s, scheme, addr)
}
// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation.
func setupMiddlewares(handler http.Handler) http.Handler {
return Impl.SetupMiddlewares(handler)
}
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics.
func setupGlobalMiddleware(handler http.Handler) http.Handler {
return Impl.SetupGlobalMiddleware(handler)
}

View file

@ -0,0 +1,446 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{.Package}}
{{ $package := .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"github.com/go-openapi/errors"
"github.com/go-openapi/loads"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/runtime/security"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// New{{ pascalize .Name }}API creates a new {{ pascalize .Name }} instance
func New{{ pascalize .Name }}API(spec *loads.Document) *{{ pascalize .Name }}API {
return &{{ pascalize .Name }}API{
handlers: make(map[string]map[string]http.Handler),
formats: strfmt.Default,
defaultConsumes: "{{ .DefaultConsumes }}",
defaultProduces: "{{ .DefaultProduces }}",
customConsumers: make(map[string]runtime.Consumer),
customProducers: make(map[string]runtime.Producer),
PreServerShutdown: func() { },
ServerShutdown: func() { },
spec: spec,
useSwaggerUI: false,
ServeError: errors.ServeError,
BasicAuthenticator: security.BasicAuth,
APIKeyAuthenticator: security.APIKeyAuth,
BearerAuthenticator: security.BearerAuth,
{{ range .Consumes }}
{{- if .Implementation }}
{{ pascalize .Name }}Consumer: {{ .Implementation }},
{{- else }}
{{ pascalize .Name }}Consumer: runtime.ConsumerFunc(func(r io.Reader, target interface{}) error {
return errors.NotImplemented("{{.Name}} consumer has not yet been implemented")
}),
{{- end }}
{{- end }}
{{ range .Produces }}
{{- if .Implementation }}
{{ pascalize .Name }}Producer: {{ .Implementation }},
{{- else }}
{{ pascalize .Name }}Producer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("{{.Name}} producer has not yet been implemented")
}),
{{- end }}
{{- end }}
{{ range .Operations }}
{{ if ne .Package $package }}{{ pascalize .Package }}{{ end }}{{ pascalize .Name }}Handler:
{{- if ne .Package $package }}{{ .PackageAlias }}.{{ end }}{{ pascalize .Name }}HandlerFunc(func(params {{ if ne .Package $package }}{{ .PackageAlias }}.{{end }}
{{- if $.GenOpts.StrictResponders}}
{{- pascalize .Name }}Params{{if .Authorized}}, principal {{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}{{end}}) {{if ne .Package $package }}{{ .Package }}.{{ end }}{{ pascalize .Name }}Responder {
return {{if ne .Package $package }}{{ .Package }}.{{ end }}{{ pascalize .Name }}NotImplemented()
{{else}}
{{- pascalize .Name }}Params{{if .Authorized}}, principal {{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}{{end}}) middleware.Responder {
return middleware.NotImplemented("operation {{ if ne .Package $package }}{{ .Package }}.{{ end }}{{pascalize .Name}} has not yet been implemented")
{{ end -}}
}),
{{- end }}
{{ range .SecurityDefinitions }}
{{- if .IsBasicAuth }}
// Applies when the Authorization header is set with the Basic scheme
{{ pascalize .ID }}Auth: func(user string, pass string) ({{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return nil, errors.NotImplemented("basic auth ({{ .ID }}) has not yet been implemented")
},
{{- end }}
{{- if .IsAPIKeyAuth }}
// Applies when the "{{ .Name }}" {{ .Source }} is set
{{ pascalize .ID }}Auth: func(token string) ({{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return nil, errors.NotImplemented("api key auth ({{ .ID }}) {{.Name}} from {{.Source}} param [{{ .Name }}] has not yet been implemented")
},
{{- end }}
{{- if .IsOAuth2 }}
{{ pascalize .ID }}Auth: func(token string, scopes []string) ({{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return nil, errors.NotImplemented("oauth2 bearer auth ({{ .ID }}) has not yet been implemented")
},
{{- end }}
{{- end }}
{{- if .SecurityDefinitions }}
// default authorizer is authorized meaning no requests are blocked
APIAuthorizer: security.Authorized(),
{{- end }}
}
}
/*{{ pascalize .Name }}API {{ if .Info }}{{ if .Info.Description }}{{.Info.Description}}{{ else }}the {{ humanize .Name }} API{{ end }}{{ end }} */
type {{ pascalize .Name }}API struct {
spec *loads.Document
context *middleware.Context
handlers map[string]map[string]http.Handler
formats strfmt.Registry
customConsumers map[string]runtime.Consumer
customProducers map[string]runtime.Producer
defaultConsumes string
defaultProduces string
Middleware func(middleware.Builder) http.Handler
useSwaggerUI bool
// BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
// It has a default implementation in the security package, however you can replace it for your particular usage.
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
// APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
// It has a default implementation in the security package, however you can replace it for your particular usage.
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
// BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
// It has a default implementation in the security package, however you can replace it for your particular usage.
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
{{ range .Consumes }}
// {{ pascalize .Name }}Consumer registers a consumer for the following mime types:
{{- range .AllSerializers }}
// - {{ .MediaType }}
{{- end }}
{{ pascalize .Name }}Consumer runtime.Consumer
{{- end }}
{{ range .Produces}}
// {{ pascalize .Name }}Producer registers a producer for the following mime types:
{{- range .AllSerializers }}
// - {{ .MediaType }}
{{- end }}
{{ pascalize .Name }}Producer runtime.Producer
{{- end }}
{{ range .SecurityDefinitions}}
{{- if .IsBasicAuth}}
// {{ pascalize .ID }}Auth registers a function that takes username and password and returns a principal
// it performs authentication with basic auth
{{ pascalize .ID }}Auth func(string, string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
{{- end }}
{{- if .IsAPIKeyAuth}}
// {{ pascalize .ID }}Auth registers a function that takes a token and returns a principal
// it performs authentication based on an api key {{ .Name }} provided in the {{.Source}}
{{ pascalize .ID }}Auth func(string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
{{- end }}
{{- if .IsOAuth2 }}
// {{ pascalize .ID }}Auth registers a function that takes an access token and a collection of required scopes and returns a principal
// it performs authentication based on an oauth2 bearer token provided in the request
{{ pascalize .ID }}Auth func(string, []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
{{- end }}
{{- end }}
{{- if .SecurityDefinitions }}
// APIAuthorizer provides access control (ACL/RBAC/ABAC) by providing access to the request and authenticated principal
APIAuthorizer runtime.Authorizer
{{- end }}
{{- $package := .Package }}
{{ range .Operations }}
// {{ if ne .Package $package }}{{ pascalize .Package }}{{ end }}{{ pascalize .Name }}Handler sets the operation handler for the {{ humanize .Name }} operation
{{ if ne .Package $package }}{{ pascalize .Package }}{{ end }}{{ pascalize .Name }}Handler {{ if ne .Package $package }}{{ .PackageAlias }}.{{ end }}{{ pascalize .Name }}Handler
{{- end }}
// ServeError is called when an error is received, there is a default handler
// but you can set your own with this
ServeError func(http.ResponseWriter, *http.Request, error)
// PreServerShutdown is called before the HTTP(S) server is shutdown
// This allows for custom functions to get executed before the HTTP(S) server stops accepting traffic
PreServerShutdown func()
// ServerShutdown is called when the HTTP(S) server is shut down and done
// handling all active connections and does not accept connections any more
ServerShutdown func()
// Custom command line argument groups with their descriptions
CommandLineOptionsGroups []swag.CommandLineOptionsGroup
// User defined logger function.
Logger func(string, ...interface{})
}
// UseRedoc for documentation at /docs
func ({{.ReceiverName}} *{{ pascalize .Name }}API) UseRedoc() {
{{.ReceiverName}}.useSwaggerUI = false
}
// UseSwaggerUI for documentation at /docs
func ({{.ReceiverName}} *{{ pascalize .Name }}API) UseSwaggerUI() {
{{.ReceiverName}}.useSwaggerUI = true
}
// SetDefaultProduces sets the default produces media type
func ({{.ReceiverName}} *{{ pascalize .Name }}API) SetDefaultProduces(mediaType string) {
{{.ReceiverName}}.defaultProduces = mediaType
}
// SetDefaultConsumes returns the default consumes media type
func ({{.ReceiverName}} *{{ pascalize .Name }}API) SetDefaultConsumes(mediaType string) {
{{.ReceiverName}}.defaultConsumes = mediaType
}
// SetSpec sets a spec that will be served for the clients.
func ({{.ReceiverName}} *{{ pascalize .Name }}API) SetSpec(spec *loads.Document) {
{{.ReceiverName}}.spec = spec
}
// DefaultProduces returns the default produces media type
func ({{.ReceiverName}} *{{ pascalize .Name }}API) DefaultProduces() string {
return {{.ReceiverName}}.defaultProduces
}
// DefaultConsumes returns the default consumes media type
func ({{.ReceiverName}} *{{ pascalize .Name }}API) DefaultConsumes() string {
return {{.ReceiverName}}.defaultConsumes
}
// Formats returns the registered string formats
func ({{.ReceiverName}} *{{ pascalize .Name }}API) Formats() strfmt.Registry {
return {{.ReceiverName}}.formats
}
// RegisterFormat registers a custom format validator
func ({{.ReceiverName}} *{{ pascalize .Name }}API) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) {
{{.ReceiverName}}.formats.Add(name, format, validator)
}
// Validate validates the registrations in the {{ pascalize .Name }}API
func ({{.ReceiverName}} *{{ pascalize .Name }}API) Validate() error {
var unregistered []string
{{ range .Consumes }}
if {{.ReceiverName}}.{{ pascalize .Name }}Consumer == nil {
unregistered = append(unregistered, "{{ pascalize .Name }}Consumer")
}
{{- end }}
{{ range .Produces }}
if {{.ReceiverName}}.{{ pascalize .Name }}Producer == nil {
unregistered = append(unregistered, "{{ pascalize .Name }}Producer")
}
{{- end }}
{{ range .SecurityDefinitions }}
if {{.ReceiverName}}.{{ pascalize .ID }}Auth == nil {
unregistered = append(unregistered, "{{if .IsAPIKeyAuth }}{{ pascalize .Name }}{{ else }}{{ pascalize .ID }}{{ end }}Auth")
}
{{- end }}
{{ range .Operations }}
if {{.ReceiverName}}.{{ if ne .Package $package }}{{ pascalize .Package }}{{ end }}{{ pascalize .Name }}Handler == nil {
unregistered = append(unregistered, "{{ if ne .Package $package }}{{ .Package }}.{{ end }}{{ pascalize .Name }}Handler")
}
{{- end }}
if len(unregistered) > 0 {
return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", "))
}
return nil
}
// ServeErrorFor gets a error handler for a given operation id
func ({{.ReceiverName}} *{{ pascalize .Name }}API) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) {
return {{.ReceiverName}}.ServeError
}
// AuthenticatorsFor gets the authenticators for the specified security schemes
func ({{.ReceiverName}} *{{ pascalize .Name }}API) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator {
{{- if .SecurityDefinitions }}
result := make(map[string]runtime.Authenticator)
for name := range schemes {
switch name {
{{- range .SecurityDefinitions }}
case "{{.ID}}":
{{- if .IsBasicAuth }}
result[name] = {{.ReceiverName}}.BasicAuthenticator({{ if not ( eq .Principal "interface{}" ) }}func(username, password string) (interface{}, error) {
return {{ end }}{{.ReceiverName}}.{{ pascalize .ID }}Auth{{ if not ( eq .Principal "interface{}" ) }}(username, password)
}{{ end }})
{{- end }}
{{- if .IsAPIKeyAuth }}
scheme := schemes[name]
result[name] = {{.ReceiverName}}.APIKeyAuthenticator(scheme.Name, scheme.In, {{ if not ( eq .Principal "interface{}" ) }}func(token string) (interface{}, error) {
return {{ end }}{{.ReceiverName}}.{{ pascalize .ID }}Auth{{ if not ( eq .Principal "interface{}" ) }}(token)
}{{ end }})
{{- end }}
{{- if .IsOAuth2 }}
result[name] = {{.ReceiverName}}.BearerAuthenticator(name, {{ if not ( eq .Principal "interface{}" ) }}func(token string, scopes []string) (interface{}, error) {
return {{ end }}{{.ReceiverName}}.{{ pascalize .ID }}Auth{{ if not ( eq .Principal "interface{}" ) }}(token, scopes)
}{{ end }})
{{- end }}
{{end}}
}
}
return result
{{- else }}
return nil
{{- end }}
}
// Authorizer returns the registered authorizer
func ({{.ReceiverName}} *{{ pascalize .Name }}API) Authorizer() runtime.Authorizer {
{{- if .SecurityDefinitions }}
return {{.ReceiverName}}.APIAuthorizer
{{- else }}
return nil
{{- end }}
}
// ConsumersFor gets the consumers for the specified media types.
// MIME type parameters are ignored here.
func ({{.ReceiverName}} *{{ pascalize .Name }}API) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
{{- if .Consumes }}
result := make(map[string]runtime.Consumer, len(mediaTypes))
for _, mt := range mediaTypes {
switch mt {
{{- range .Consumes }}
{{- range .AllSerializers }}
case "{{ .MediaType }}":
result["{{ .MediaType }}"] = {{.ReceiverName}}.{{ pascalize .Name }}Consumer
{{- end }}
{{- end }}
}
if c, ok := {{.ReceiverName}}.customConsumers[mt]; ok {
result[mt] = c
}
}
return result
{{- else }}
return nil
{{- end }}
}
// ProducersFor gets the producers for the specified media types.
// MIME type parameters are ignored here.
func ({{.ReceiverName}} *{{ pascalize .Name }}API) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
{{- if .Produces }}
result := make(map[string]runtime.Producer, len(mediaTypes))
for _, mt := range mediaTypes {
switch mt {
{{- range .Produces }}
{{- range .AllSerializers }}
case "{{ .MediaType }}":
result["{{ .MediaType }}"] = {{.ReceiverName}}.{{ pascalize .Name }}Producer
{{- end }}
{{- end }}
}
if p, ok := {{.ReceiverName}}.customProducers[mt]; ok {
result[mt] = p
}
}
return result
{{- else }}
return nil
{{- end }}
}
// HandlerFor gets a http.Handler for the provided operation method and path
func ({{.ReceiverName}} *{{ pascalize .Name }}API) HandlerFor(method, path string) (http.Handler, bool) {
if {{.ReceiverName}}.handlers == nil {
return nil, false
}
um := strings.ToUpper(method)
if _, ok := {{.ReceiverName}}.handlers[um]; !ok {
return nil, false
}
if path == "/" {
path = ""
}
h, ok := {{.ReceiverName}}.handlers[um][path]
return h, ok
}
// Context returns the middleware context for the {{ humanize .Name }} API
func ({{.ReceiverName}} *{{ pascalize .Name }}API) Context() *middleware.Context {
if {{.ReceiverName}}.context == nil {
{{.ReceiverName}}.context = middleware.NewRoutableContext({{.ReceiverName}}.spec, {{.ReceiverName}}, nil)
}
return {{ .ReceiverName }}.context
}
func ({{.ReceiverName}} *{{ pascalize .Name }}API) initHandlerCache() {
{{.ReceiverName}}.Context() // don't care about the result, just that the initialization happened
{{- if .Operations }}
if {{ .ReceiverName }}.handlers == nil {
{{.ReceiverName}}.handlers = make(map[string]map[string]http.Handler)
}
{{ range .Operations }}
if {{ .ReceiverName }}.handlers[{{ printf "%q" (upper .Method) }}] == nil {
{{ .ReceiverName }}.handlers[{{ printf "%q" (upper .Method) }}] = make(map[string]http.Handler)
}
{{.ReceiverName}}.handlers[{{ printf "%q" (upper .Method) }}][{{ if eq .Path "/" }}""{{ else }}{{ printf "%q" (cleanPath .Path) }}{{ end }}] = {{ if ne .Package $package }}{{ .PackageAlias }}.{{ end }}New{{ pascalize .Name }}({{.ReceiverName}}.context, {{.ReceiverName}}.{{if ne .Package $package}}{{ pascalize .Package }}{{end}}{{ pascalize .Name }}Handler)
{{- end }}
{{- end }}
}
// Serve creates a http handler to serve the API over HTTP
// can be used directly in http.ListenAndServe(":8000", api.Serve(nil))
func ({{.ReceiverName}} *{{ pascalize .Name }}API) Serve(builder middleware.Builder) http.Handler {
{{ .ReceiverName }}.Init()
if {{ .ReceiverName}}.Middleware != nil {
return {{ .ReceiverName }}.Middleware(builder)
}
if {{.ReceiverName}}.useSwaggerUI {
return {{.ReceiverName}}.context.APIHandlerSwaggerUI(builder)
}
return {{.ReceiverName}}.context.APIHandler(builder)
}
// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit
func ({{.ReceiverName}} *{{ pascalize .Name }}API) Init() {
if len({{.ReceiverName}}.handlers) == 0 {
{{.ReceiverName}}.initHandlerCache()
}
}
// RegisterConsumer allows you to add (or override) a consumer for a media type.
func ({{.ReceiverName}} *{{ pascalize .Name }}API) RegisterConsumer(mediaType string, consumer runtime.Consumer) {
{{.ReceiverName}}.customConsumers[mediaType] = consumer
}
// RegisterProducer allows you to add (or override) a producer for a media type.
func ({{.ReceiverName}} *{{ pascalize .Name }}API) RegisterProducer(mediaType string, producer runtime.Producer) {
{{.ReceiverName}}.customProducers[mediaType] = producer
}
// AddMiddlewareFor adds a http middleware to existing handler
func ({{.ReceiverName}} *{{ pascalize .Name }}API) AddMiddlewareFor(method, path string, builder middleware.Builder) {
um := strings.ToUpper(method)
if path == "/" {
path = ""
}
{{.ReceiverName}}.Init()
if h, ok := {{.ReceiverName}}.handlers[um][path]; ok {
{{.ReceiverName}}.handlers[um][path] = builder(h)
}
}

View file

@ -0,0 +1,167 @@
// This file is safe to edit. Once it exists it will not be overwritten
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .APIPackage }}
import (
"context"
"crypto/tls"
"io"
"log"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/runtime/security"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
{{ with .GenOpts }}
//go:generate swagger generate server --target {{ .TargetPath }} --name {{ .Name }} --spec {{ .SpecPath }}
{{- if .APIPackage }}{{ if ne .APIPackage "operations" }} --api-package {{ .APIPackage }}{{ end }}{{ end }}
{{- if .ModelPackage }}{{ if ne .ModelPackage "models" }} --model-package {{ .ModelPackage }}{{ end }}{{ end }}
{{- if .ServerPackage }}{{ if ne .ServerPackage "restapi"}} --server-package {{ .ServerPackage }}{{ end }}{{ end }}
{{- if .ClientPackage }}{{ if ne .ClientPackage "client" }} --client-package {{ .ClientPackage }}{{ end }}{{ end }}
{{- if .TemplateDir }} --template-dir {{ .TemplateDir }}{{ end }}
{{- range .Operations }} --operation {{ . }}{{ end }}
{{- range .Tags }} --tags {{ . }}{{ end }}
{{- if .Principal }} --principal {{ .Principal }}{{ end }}
{{- if .DefaultScheme }}{{ if ne .DefaultScheme "http" }} --default-scheme {{ .DefaultScheme }}{{ end }}{{ end }}
{{- range .Models }} --model {{ . }}{{ end }}
{{- if or (not .IncludeModel) (not .IncludeValidator) }} --skip-models{{ end }}
{{- if or (not .IncludeHandler) (not .IncludeParameters ) (not .IncludeResponses) }} --skip-operations{{ end }}
{{- if not .IncludeSupport }} --skip-support{{ end }}
{{- if not .IncludeMain }} --exclude-main{{ end }}
{{- if .ExcludeSpec }} --exclude-spec{{ end }}
{{- if .DumpData }} --dump-data{{ end }}
{{- if .StrictResponders }} --strict-responders{{ end }}
{{ end }}
func configureFlags(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API) {
// api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... }
}
func configureAPI(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API) http.Handler {
// configure the api here
api.ServeError = errors.ServeError
// Set your custom logger if needed. Default one is log.Printf
// Expected interface func(string, ...interface{})
//
// Example:
// api.Logger = log.Printf
api.UseSwaggerUI()
// To continue using redoc as your UI, uncomment the following line
// api.UseRedoc()
{{ range .Consumes }}
{{- if .Implementation }}
api.{{ pascalize .Name }}Consumer = {{ .Implementation }}
{{- else }}
api.{{ pascalize .Name }}Consumer = runtime.ConsumerFunc(func(r io.Reader, target interface{}) error {
return errors.NotImplemented("{{.Name}} consumer has not yet been implemented")
})
{{- end }}
{{- end }}
{{ range .Produces }}
{{- if .Implementation }}
api.{{ pascalize .Name }}Producer = {{ .Implementation }}
{{- else }}
api.{{ pascalize .Name }}Producer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("{{.Name}} producer has not yet been implemented")
})
{{- end }}
{{- end}}
{{ range .SecurityDefinitions }}
{{- if .IsBasicAuth }}
// Applies when the Authorization header is set with the Basic scheme
if api.{{ pascalize .ID }}Auth == nil {
api.{{ pascalize .ID }}Auth = func(user string, pass string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return nil, errors.NotImplemented("basic auth ({{ .ID }}) has not yet been implemented")
}
}
{{- else if .IsAPIKeyAuth }}
// Applies when the "{{ .Name }}" {{ .Source }} is set
if api.{{ pascalize .ID }}Auth == nil {
api.{{ pascalize .ID }}Auth = func(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return nil, errors.NotImplemented("api key auth ({{ .ID }}) {{.Name}} from {{.Source}} param [{{ .Name }}] has not yet been implemented")
}
}
{{- else if .IsOAuth2 }}
if api.{{ pascalize .ID }}Auth == nil {
api.{{ pascalize .ID }}Auth = func(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
return nil, errors.NotImplemented("oauth2 bearer auth ({{ .ID }}) has not yet been implemented")
}
}
{{- end }}
{{- end }}
{{- if .SecurityDefinitions }}
// Set your custom authorizer if needed. Default one is security.Authorized()
// Expected interface runtime.Authorizer
//
// Example:
// api.APIAuthorizer = security.Authorized()
{{- end }}
{{- $package := .Package }}
{{- $apipackagealias := .APIPackageAlias }}
{{- range .Operations }}
{{- if .HasFormParams }}
// You may change here the memory limit for this multipart form parser. Below is the default (32 MB).
// {{ if ne .Package $package }}{{ .PackageAlias }}{{ else }}{{ $apipackagealias }}{{ end }}.{{ pascalize .Name }}MaxParseMemory = 32 << 20
{{- end }}
{{- end }}
{{ range .Operations }}
if api.{{ if ne .Package $package }}{{ pascalize .Package }}{{ end }}{{ pascalize .Name }}Handler == nil {
api.{{ if ne .Package $package }}{{pascalize .Package}}{{ end }}{{ pascalize .Name }}Handler =
{{- if ne .Package $package }}
{{- .PackageAlias }}.{{- pascalize .Name }}HandlerFunc(func(params {{ .PackageAlias }}.{{- pascalize .Name }}Params
{{- else }}
{{- $apipackagealias }}.{{- pascalize .Name }}HandlerFunc(func(params {{ $apipackagealias }}.{{- pascalize .Name }}Params
{{- end }}
{{- if $.GenOpts.StrictResponders }}
{{- if .Authorized}}, principal {{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}{{end}}) {{.Package}}.{{ pascalize .Name }}Responder {
return {{.Package}}.{{ pascalize .Name }}NotImplemented()
{{ else }}
{{- if .Authorized}}, principal {{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}{{end}}) middleware.Responder {
return middleware.NotImplemented("operation {{ .Package}}.{{pascalize .Name}} has not yet been implemented")
{{ end -}}
})
}
{{- end }}
api.PreServerShutdown = func() { }
api.ServerShutdown = func() { }
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
}
// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
// Make all necessary changes to the TLS configuration here.
}
// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix".
func configureServer(s *http.Server, scheme, addr string) {
}
// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation.
func setupMiddlewares(handler http.Handler) http.Handler {
return handler
}
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics.
func setupGlobalMiddleware(handler http.Handler) http.Handler {
return handler
}

View file

@ -0,0 +1,63 @@
// Code generated by go-swagger; DO NOT EDIT.
{{- if .Copyright }}
// {{ comment .Copyright }}
{{- end }}
// Package {{ .APIPackage }} {{ if .Info.Title }}{{ comment .Info.Title }}{{ else }}{{ comment (humanize .Name) }}{{end}}
//
{{- if .Info.Description }}
// {{ comment .Info.Description " " }}
{{- end }}
{{- if .Info.TermsOfService }}
// Terms Of Service:
// {{ comment .Info.TermsOfService " " }}
{{- end }}
{{- if or .Schemes .Host .BasePath .Info }}
{{- if .Schemes }}
// Schemes:
{{- range .Schemes }}
// {{ . }}
{{- end }}
{{- end }}
{{- if .Host }}
// Host: {{ .Host }}
{{- end }}
{{- if .BasePath }}
// BasePath: {{ .BasePath }}
{{- end}}
{{- with .Info }}
{{- if .Version }}
// Version: {{ .Version }}
{{- end }}
{{- if .License }}
// License: {{ if .License.Name }}{{ .License.Name}} {{ end }}{{ if .License.URL }}{{ .License.URL }}{{ end }}
{{- end }}
{{- if .Contact }}
// Contact: {{ if .Contact.Name }}{{ .Contact.Name }}{{ end }}{{ if .Contact.Email }}<{{ .Contact.Email }}>{{ end }}{{ if .Contact.URL }} {{ .Contact.URL }}{{ end }}
{{- end }}
{{- end }}
{{- end }}
{{- if .Consumes }}
//
// Consumes:
{{- range .Consumes }}
{{- range .AllSerializers }}
// - {{ .MediaType -}}
{{- end }}
{{- end }}
{{- end }}
{{- if .Produces }}
//
// Produces:
{{- range .Produces }}
{{- range .AllSerializers }}
// - {{ .MediaType -}}
{{- end }}
{{- end }}
{{- end }}
//
// swagger:meta
package {{ .APIPackage }}

View file

@ -0,0 +1,186 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/go-openapi/loads"
{{- if .UseGoStructFlags }}
flags "github.com/jessevdk/go-flags"
{{- end }}
{{- if .UsePFlags }}
flag "github.com/spf13/pflag"
{{- end }}
{{- if .UseFlags }}
"flag"
{{- end }}
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// This file was generated by the swagger tool.
// Make sure not to overwrite this file after you generated it because all your edits would be lost!
{{ if .ExcludeSpec }}
func init() {
loads.AddLoader(fmts.YAMLMatcher, fmts.YAMLDoc)
}
{{ end }}
func main() {
{{ if .UsePFlags }}
{{- if not .ExcludeSpec }}
swaggerSpec, err := loads.Embedded({{ .ServerPackageAlias }}.SwaggerJSON, {{ .ServerPackageAlias }}.FlatSwaggerJSON)
if err != nil {
log.Fatalln(err)
}
{{- end }}
var server *{{ .ServerPackageAlias }}.Server // make sure init is called
flag.Usage = func() {
fmt.Fprint(os.Stderr, "Usage:\n")
fmt.Fprint(os.Stderr, " {{ dasherize .Name }}-server [OPTIONS]\n\n")
title := {{ if .Info }}{{ if .Info.Title }}{{ printf "%q" .Info.Title }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Title{{ end }}{{ end }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Title{{ end }}{{ end}}
fmt.Fprint(os.Stderr, title+"\n\n")
desc := {{ if .Info }}{{ if .Info.Description }}{{ printf "%q" .Info.Description }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Description{{ end }}{{ end }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Description{{ end }}{{ end}}
if desc != "" {
fmt.Fprintf(os.Stderr, desc+"\n\n")
}
fmt.Fprintln(os.Stderr, flag.CommandLine.FlagUsages())
}
// parse the CLI flags
flag.Parse()
{{- if .ExcludeSpec }}
server = {{ .ServerPackageAlias }}.NewServer(nil)
swaggerSpec, err := loads.Spec(string(server.Spec))
if err != nil {
log.Fatalln(err)
}
api := {{.APIPackageAlias}}.New{{ pascalize .Name }}API(swaggerSpec)
server.SetAPI(api)
{{- else }}
api := {{.APIPackageAlias}}.New{{ pascalize .Name }}API(swaggerSpec)
// get server with flag values filled out
server = {{ .ServerPackageAlias }}.NewServer(api)
{{- end }}
defer server.Shutdown()
server.ConfigureAPI()
if err := server.Serve(); err != nil {
log.Fatalln(err)
}
{{ end }}
{{ if .UseGoStructFlags}}
{{- if .ExcludeSpec }}
server := {{ .ServerPackageAlias }}.NewServer(nil)
{{- else }}
swaggerSpec, err := loads.Embedded({{ .ServerPackageAlias }}.SwaggerJSON, {{ .ServerPackageAlias }}.FlatSwaggerJSON)
if err != nil {
log.Fatalln(err)
}
api := {{.APIPackageAlias}}.New{{ pascalize .Name }}API(swaggerSpec)
server := {{ .ServerPackageAlias }}.NewServer(api)
defer server.Shutdown()
{{- end }}
parser := flags.NewParser(server, flags.Default)
parser.ShortDescription = {{ if .Info }}{{ if .Info.Title }}{{ printf "%q" .Info.Title }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Title{{ end }}{{ end }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Title{{ end }}{{ end}}
parser.LongDescription = {{ if .Info }}{{ if .Info.Description }}{{ printf "%q" .Info.Description }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Description{{ end }}{{ end }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Description{{ end }}{{ end}}
{{- if not .ExcludeSpec }}
server.ConfigureFlags()
for _, optsGroup := range api.CommandLineOptionsGroups {
_, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options)
if err != nil {
log.Fatalln(err)
}
}
{{- end }}
if _, err := parser.Parse(); err != nil {
code := 1
if fe, ok := err.(*flags.Error); ok {
if fe.Type == flags.ErrHelp {
code = 0
}
}
os.Exit(code)
}
{{- if .ExcludeSpec }}
swaggerSpec, err := loads.Spec(string(server.Spec))
if err != nil {
log.Fatalln(err)
}
api := {{.APIPackageAlias}}.New{{ pascalize .Name }}API(swaggerSpec)
server.SetAPI(api)
defer server.Shutdown()
{{- end }}
server.ConfigureAPI()
if err := server.Serve(); err != nil {
log.Fatalln(err)
}
{{ end }}
{{ if .UseFlags}}
{{- if not .ExcludeSpec }}
swaggerSpec, err := loads.Embedded({{ .ServerPackageAlias }}.SwaggerJSON, {{ .ServerPackageAlias }}.FlatSwaggerJSON)
if err != nil {
log.Fatalln(err)
}
{{- end }}
var server *{{ .ServerPackageAlias }}.Server // make sure init is called
flag.Usage = func() {
fmt.Fprint(os.Stderr, "Usage:\n")
fmt.Fprint(os.Stderr, " {{ dasherize .Name }}-server [OPTIONS]\n\n")
title := {{ if .Info }}{{ if .Info.Title }}{{ printf "%q" .Info.Title }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Title{{ end }}{{ end }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Title{{ end }}{{ end}}
fmt.Fprint(os.Stderr, title+"\n\n")
desc := {{ if .Info }}{{ if .Info.Description }}{{ printf "%q" .Info.Description }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Description{{ end }}{{ end }}{{ else }}{{ if .ExcludeSpec }}""{{ else }}swaggerSpec.Spec().Info.Description{{ end }}{{ end}}
if desc != "" {
fmt.Fprintf(os.Stderr, desc+"\n\n")
}
flag.CommandLine.SetOutput(os.Stderr)
flag.PrintDefaults()
}
// parse the CLI flags
flag.Parse()
{{- if .ExcludeSpec }}
server = {{ .ServerPackageAlias }}.NewServer(nil)
swaggerSpec, err := loads.Spec(string(server.Spec))
if err != nil {
log.Fatalln(err)
}
api := {{.APIPackageAlias}}.New{{ pascalize .Name }}API(swaggerSpec)
server.SetAPI(api)
{{- else }}
api := {{.APIPackageAlias}}.New{{ pascalize .Name }}API(swaggerSpec)
// get server with flag values filled out
server = {{ .ServerPackageAlias }}.NewServer(api)
{{- end }}
defer server.Shutdown()
server.ConfigureAPI()
if err := server.Serve(); err != nil {
log.Fatalln(err)
}
{{ end }}
}

View file

@ -0,0 +1,92 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/runtime/security"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// {{ pascalize .Name }}HandlerFunc turns a function with the right signature into a {{ humanize .Name }} handler
type {{ pascalize .Name }}HandlerFunc func({{ pascalize .Name }}Params{{ if .Authorized }}, {{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}{{ end }}) {{ if $.StrictResponders }} {{ pascalize .Name }}Responder {{else}} middleware.Responder {{end}}
// Handle executing the request and returning a response
func (fn {{ pascalize .Name }}HandlerFunc) Handle(params {{ pascalize .Name }}Params{{ if .Authorized }}, principal {{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}{{ end }}) {{ if $.StrictResponders }} {{ pascalize .Name }}Responder {{else}} middleware.Responder {{end}} {
return fn(params{{ if .Authorized }}, principal{{ end }})
}
// {{ pascalize .Name }}Handler interface for that can handle valid {{ humanize .Name }} params
type {{ pascalize .Name }}Handler interface {
Handle({{ pascalize .Name }}Params{{ if .Authorized }}, {{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}{{ end }}) {{ if $.StrictResponders }} {{ pascalize .Name }}Responder {{else}} middleware.Responder {{end}}
}
// New{{ pascalize .Name }} creates a new http.Handler for the {{ humanize .Name }} operation
func New{{ pascalize .Name }}(ctx *middleware.Context, handler {{ pascalize .Name }}Handler) *{{ pascalize .Name }} {
return &{{ pascalize .Name }}{Context: ctx, Handler: handler}
}
/* {{ pascalize .Name }} swagger:route {{ .Method }} {{ .Path }}{{ range .Tags }} {{ . }}{{ end }} {{ camelize .Name }}
{{ if .Summary }}{{ .Summary }}{{ if .Description }}
{{ blockcomment .Description }}{{ end }}{{ else if .Description}}{{ blockcomment .Description }}{{ else }}{{ pascalize .Name }} {{ humanize .Name }} API{{ end }}
*/
type {{ pascalize .Name }} struct {
Context *middleware.Context
Handler {{ pascalize .Name }}Handler
}
func ({{ .ReceiverName }} *{{ pascalize .Name }}) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := {{ .ReceiverName }}.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = New{{ pascalize .Name }}Params()
{{- if .Authorized }}
uprinc, aCtx, err := {{ .ReceiverName }}.Context.Authorize(r, route)
if err != nil {
{{ .ReceiverName }}.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
*r = *aCtx
}
var principal {{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}
if uprinc != nil {
principal = {{ if eq .Principal "inferface{}" }}uprinc{{ else }}uprinc.({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}) // this is really a {{ .Principal }}, I promise{{ end }}
}
{{ end }}
if err := {{ .ReceiverName }}.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
{{ .ReceiverName }}.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := {{ .ReceiverName }}.Handler.Handle(Params{{ if .Authorized }}, principal{{ end }}) // actually handle the request
{{ .ReceiverName }}.Context.Respond(rw, r, route.Produces, route, res)
}
{{ range .ExtraSchemas }}
// {{ .Name }} {{ template "docstring" . }}
//
// swagger:model {{ .Name }}
{{- template "schema" . }}
{{- end }}

View file

@ -0,0 +1,720 @@
{{ define "bindprimitiveparam" }}{{/* an empty test definition to test template repo dependencies resolution - DO NOT CHANGE THIS */}}
{{ end }}
{{ define "bodyvalidator" }}
{{- if .HasModelBodyParams }}
// validate body object{{/* delegate validation to model object */}}
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
ctx := validate.WithOperationRequest(r.Context())
if err := body.ContextValidate(ctx, route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
}
{{- else if and .HasSimpleBodyParams .HasModelBodyItems }}
{{- if or .Schema.HasSliceValidations .Schema.Items.HasValidations }}
// validate array of body objects
{{- end }}
{{- if .Schema.HasSliceValidations }}
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{ template "sliceparamvalidator" . }}
{{- end }}
{{- if and .Schema.Items.HasValidations (not (or .Schema.Items.IsInterface .Schema.Items.IsStream)) }}
for {{ .IndexVar }} := range body {
{{- if .Schema.Items.IsNullable }}
if body[{{ .IndexVar }}] == nil {
{{- if .Schema.Items.Required }}
res = append(res, errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, body[{{ .IndexVar }}]))
break
{{- else }}
continue
{{- end }}
}
{{- end }}
if err := body[{{ .IndexVar }}].Validate(route.Formats); err != nil {
res = append(res, err)
break
}
}
{{- if not .Schema.HasSliceValidations }}
if len(res) == 0 {
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
}
{{- end }}
{{- else }}
// no validation for items in this slice
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end }}
{{- else if and .HasSimpleBodyParams .HasModelBodyMap }}
{{- if and .Schema.HasValidations (not (or .Schema.AdditionalProperties.IsInterface .Schema.AdditionalProperties.IsStream)) }}
// validate map of body objects
for {{ .KeyVar }} := range body {
{{- if .Schema.AdditionalProperties.Required }}
if err := validate.Required({{ if .Child.Path }}{{ .Child.Path }}{{ else }}""{{ end }}, {{ printf "%q" .Child.Location }}, {{ if not .IsAnonymous }}{{ .Schema.GoType }}({{ end }}body[{{ .KeyVar }}]{{ if not .IsAnonymous }}){{ end }}); err != nil {
return err
}
{{- end }}
{{- if and .Schema.AdditionalProperties.IsNullable (not .IsMapNullOverride) }}
if body[{{ .KeyVar }}] == nil {
{{- if .Schema.AdditionalProperties.Required }}
res = append(res, errors.Required({{ .Path }}, {{ printf "%q" .Location }}, body[{{ .KeyVar }}]))
break
{{- else }}
continue
{{- end }}
}
{{- end }}
if val , ok :=body[{{ .KeyVar }}]; ok {
{{- if and .IsNullable (not .IsMapNullOverride) }}
if val != nil {
{{- end }}
if err := val.Validate(route.Formats); err != nil {
res = append(res, err)
break
}
{{- if and .IsNullable (not .IsMapNullOverride) }}
}
{{- end }}
}
}
if len(res) == 0 {
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
}
{{- else }}
// no validation for this map
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end }}
{{- else if .HasSimpleBodyParams }}
{{- if and (not .IsArray) (not .IsMap) .Schema.HasValidations }}
// validate inline body
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}Body(route.Formats); err != nil {
res = append(res, err)
}
{{- else if and (or .IsArray .IsMap) .Schema.HasValidations }}
// validate inline body {{ if .IsArray }}array{{ else }}map{{ end }}
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}Body(route.Formats); err != nil {
res = append(res, err)
}
{{- else }}
// no validation required on inline body
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end}}
{{- else }}
{{- if .IsInterface }}
// no validation on generic interface
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end }}
{{- end }}
{{- end }}
{{ define "sliceparamvalidator"}}
{{- if or .MinItems .MaxItems }}
{{ camelize .Name }}Size := int64(len({{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ if and .Child (not (hasPrefix .ValueExpression "o.")) }}{{ .Child.ValueExpression }}C{{ else }}{{ .ValueExpression }}{{ end }}))
{{- end }}
{{- if .MinItems }}
// {{ .ItemsDepth }}minItems: {{ .MinItems }}
if err := validate.MinItems({{ .Path }}, {{ printf "%q" .Location }}, {{ camelize .Name }}Size, {{ .MinItems }}); err != nil {
return err
}
{{- end }}
{{- if .MaxItems }}
// {{ .ItemsDepth }}maxItems: {{ .MaxItems }}
if err := validate.MaxItems({{ .Path }}, {{ printf "%q" .Location }}, {{ camelize .Name }}Size, {{.MaxItems}}); err != nil {
return err
}
{{- end }}
{{- if .UniqueItems }}
// {{ .ItemsDepth }}uniqueItems: true
if err := validate.UniqueItems({{ .Path }}, {{ printf "%q" .Location }}, {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ if and .Child (not ( hasPrefix .ValueExpression "o." )) }}{{ .Child.ValueExpression }}C{{ else }}{{ .ValueExpression }}{{ end }}); err != nil {
return err
}
{{- end }}
{{- if .Enum }}
// {{ .ItemsDepth }}Enum: {{ .Enum }}
if err := validate.EnumCase(
{{- .Path }}, {{ printf "%q" .Location }},
{{- if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end -}}
{{- if .Child -}}
{{- if not ( hasPrefix .ValueExpression "o." ) -}}
{{- .Child.ValueExpression }}C{{- if .IsCustomFormatter }}.String(){{ end -}}
{{- else -}}
{{- .ValueExpression -}}{{- if .Child.IsCustomFormatter }}.String(){{ end -}}
{{- end -}}
{{- end -}},
{{- printf "%#v" .Enum -}}, {{ if .IsEnumCI }}false{{ else }}true{{ end }}); err != nil {
return err
}
{{- end }}
{{- end }}
{{- define "childvalidator" }}
{{- if .Converter }}
{{- if ne .SwaggerFormat "" }}
// {{ .ItemsDepth }}Format: {{ printf "%q" .SwaggerFormat }}
{{- end }}
{{ varname .ValueExpression }}, err := {{ .Converter }}({{ varname .ValueExpression }}V)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, "{{ .GoType }}", {{ varname .ValueExpression }})
}
{{- else if and .IsCustomFormatter (not .SkipParse) }}{{/* parsing is skipped for simple body items */}}
// {{ .ItemsDepth }}Format: {{ printf "%q" .SwaggerFormat }}
value, err := formats.Parse({{ printf "%q" .SwaggerFormat }},{{ varname .ValueExpression }}V)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, "{{ .GoType }}", value)
}
{{ varname .ValueExpression }} := *(value.(*{{.GoType}}))
{{- else if and .IsComplexObject .HasValidations }}{{/* dedicated to nested body params */}}
{{ varname .ValueExpression }} := {{ varname .ValueExpression }}V
if err := {{ .ValueExpression }}.Validate(formats) ; err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName({{ .Path }})
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName({{ .Path }})
}
return err
}
{{- else }}
{{ varname .ValueExpression }} := {{ varname .ValueExpression }}V
{{- end }}
{{ template "propertyparamvalidator" . }}
{{- end }}
{{- define "mapparamvalidator" }}
{{- if and .Child.HasValidations (not (or .Child.IsInterface .Child.IsStream)) }}
// validations for map
{{- else }}
// map has no validations: copying all elements
{{- end }}
{{ varname .Child.ValueExpression }}R := make({{ .GoType }},len({{ .Child.ValueExpression }}C))
for {{ .KeyVar }}, {{ .Child.ValueExpression }}V := range {{ .Child.ValueExpression}}C {
{{- if .Child.IsArray }}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{- if .Child.HasSliceValidations }}
{{- template "sliceparamvalidator" .Child }}
{{- end }}
{{- template "sliceparambinder" .Child }}
{{- else if .Child.IsMap }}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{ template "mapparamvalidator" .Child }}
{{- else }}
{{- if and .Child.IsNullable }}
if {{ varname .Child.ValueExpression }}V == nil {
{{- if .Child.Required }}
return errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, {{ varname .Child.ValueExpression }}V)
{{- else }}
continue
{{- end }}
}
{{- end }}
{{- template "childvalidator" .Child }}
{{- end }}
{{ varname .Child.ValueExpression }}R[{{.KeyVar}}] = {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap}}IR{{end}}
}
{{- end }}
{{- define "propertyparamvalidator" }}
{{- if .IsPrimitive }}
{{ template "validationPrimitive" . }}
{{- end }}
{{- if and .IsCustomFormatter (not .IsStream) (not .IsBase64) }}
if err := validate.FormatOf({{.Path}}, "{{.Location}}", "{{.SwaggerFormat}}", {{ .ValueExpression}}.String(), formats); err != nil {
return err
}
{{- end }}
{{- if .IsArray }}{{/* slice validations */}}
{{ template "sliceparamvalidator" . }}
{{- else if .IsMap }}
{{ .Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{ template "mapparamvalidator" . }}
{{- end }}
{{- end }}
{{ define "sliceparambinder" }}
var {{ varname .Child.ValueExpression }}R {{ .GoType }}
for {{ if .Child.NeedsIndex }}{{ .IndexVar }}{{ else }}_{{ end }}, {{ varname .Child.ValueExpression }}V := range {{ varname .Child.ValueExpression }}C {
{{- if .Child.IsArray }}{{/* recursive resolution of arrays in params */}}
{{- if not .Child.SkipParse }}
// {{ .Child.ItemsDepth }}CollectionFormat: {{ .Child.CollectionFormat }}
{{- end }}
{{ .Child.Child.ValueExpression }}C := {{ if .Child.SkipParse }}{{ varname .Child.ValueExpression }}V{{ else }}swag.SplitByFormat({{ varname .Child.ValueExpression }}V, {{ printf "%q" .Child.CollectionFormat }}){{ end }}
{{- if .Child.HasSliceValidations }}
{{- template "sliceparamvalidator" .Child }}
{{- end }}
if len({{ varname .Child.Child.ValueExpression }}C) > 0 {
{{ template "sliceparambinder" .Child }}
{{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap }}IR{{end}})
}
{{- else if .Child.IsMap }}{{/* simple map in items (possible with body params)*/}}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{- template "mapparamvalidator" .Child }}
{{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap }}IR{{end}})
{{- else }}{{/* non-array && non-map type in items */}}
{{- if and .Child.IsNullable (not .IsMapNullOverride) }}
if {{ varname .Child.ValueExpression }}V == nil {
{{- if .Child.Required }}
return errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, {{ varname .Child.ValueExpression }}V)
{{- else }}
continue
{{- end }}
}
{{- end }}
{{- template "childvalidator" .Child }}
{{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap }}IR{{end}})
{{- end }}
}
{{ end }}
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/security"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
{{- if .HasFormParams }}
// {{ pascalize .Name }}MaxParseMemory sets the maximum size in bytes for
// the multipart form parser for this operation.
//
// The default value is 32 MB.
// The multipart parser stores up to this + 10MB.
var {{ pascalize .Name }}MaxParseMemory int64 = 32 << 20
{{- end }}
// New{{ pascalize .Name }}Params creates a new {{ pascalize .Name }}Params object
{{- if .Params.HasSomeDefaults }}
// with the default values initialized.
{{- else }}
//
// There are no default values defined in the spec.
{{- end }}
func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params {
{{ if .Params.HasSomeDefaults }}
var (
// initialize parameters with default values
{{ range .Params }}
{{ if .HasDefault -}}
{{ if not .IsFileParam }}{{ varname .ID}}Default =
{{- if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"" )) }}{{ .Zero }}{{/* strfmt type initializer requires UnmarshalText(), e.g. Date, Datetime, Duration */}}
{{- else if and .IsPrimitive .IsCustomFormatter (stringContains .Zero "(\"" ) }}{{.GoType}}({{- printf "%#v" .Default }}){{/* strfmt type initializer takes string */}}
{{- else if and .IsPrimitive (not .IsCustomFormatter) -}}{{.GoType}}({{- printf "%#v" .Default }}){{/* regular go primitive type initializer */}}
{{- else if .IsArray -}}{{- /* Do not initialize from possible defaults in nested arrays */ -}}
{{- if and .Child.IsPrimitive .Child.IsCustomFormatter }}{{ .Zero }}{{/* initialization strategy with UnmarshalText() */}}
{{- else if .Child.IsArray -}}{{ .Zero }}{{/* initialization strategy with json.Unmarshal() */}}
{{- else if and .Child.IsPrimitive (not .Child.IsCustomFormatter) -}}{{.GoType}}{{- arrayInitializer .Default }}{{/* regular go primitive type initializer: simple slice initializer */}}
{{- else }}{{ printf "%#v" .Default }}{{/* all other cases (e.g. schema) [should not occur] */}}
{{- end }}
{{- else }}{{ printf "%#v" .Default }}{{/* case .Schema */}}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
)
{{ range .Params }}{{ if .HasDefault -}}{{- /* carry out UnmarshalText initialization strategy */ -}}
{{ if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"")) }}{{ varname .ID}}Default.UnmarshalText([]byte({{ printf "%q" .Default }}))
{{ else if .IsArray -}}
{{ if or ( and .Child.IsPrimitive .Child.IsCustomFormatter ) .Child.IsArray -}}
if err := json.Unmarshal([]byte(`{{printf "%s" (json .Default)}}`), &{{ varname .ID }}Default); err != nil {
// panics if specification is invalid
msg := fmt.Sprintf("invalid default value for parameter {{ varname .ID }}: %v",err)
panic(msg)
}
{{ end -}}
{{- end }}
{{ end -}}
{{- end }}
{{ end }}
return {{ pascalize .Name }}Params{ {{ range .Params }}{{ if .HasDefault }}
{{ pascalize .ID}}: {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}&{{ end }}{{ varname .ID }}Default,
{{ end }}{{ end }} }
}
// {{ pascalize .Name }}Params contains all the bound params for the {{ humanize .Name }} operation
// typically these are obtained from a http.Request
//
// swagger:parameters {{ .Name }}
type {{ pascalize .Name }}Params struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
{{ range .Params }}/*{{ if .Description }}{{ blockcomment .Description }}{{ end }}{{ if .Required }}
Required: true{{ end }}{{ if .Maximum }}
Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}{{ end }}{{ if .Minimum }}
Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}{{ end }}{{ if .MultipleOf }}
Multiple Of: {{ .MultipleOf }}{{ end }}{{ if .MaxLength }}
Max Length: {{ .MaxLength }}{{ end }}{{ if .MinLength }}
Min Length: {{ .MinLength }}{{ end }}{{ if .Pattern }}
Pattern: {{ .Pattern }}{{ end }}{{ if .MaxItems }}
Max Items: {{ .MaxItems }}{{ end }}{{ if .MinItems }}
Min Items: {{ .MinItems }}{{ end }}{{ if .UniqueItems }}
Unique: true{{ end }}{{ if .Location }}
In: {{ .Location }}{{ end }}{{ if .CollectionFormat }}
Collection Format: {{ .CollectionFormat }}{{ end }}{{ if .HasDefault }}
Default: {{ printf "%#v" .Default }}{{ end }}
*/
{{ if not .Schema }}{{ pascalize .ID }} {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{.GoType}}{{ else }}{{ pascalize .Name }} {{ if and (not .Schema.IsBaseType) .IsNullable (not .Schema.IsStream) }}*{{ end }}{{.GoType}}{{ end }}
{{ end}}
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with New{{ pascalize .Name }}Params() beforehand.
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
{{ .ReceiverName }}.HTTPRequest = r
{{- if .HasQueryParams }}
qs := runtime.Values(r.URL.Query())
{{- end }}
{{- if .HasFormParams }}
if err := r.ParseMultipartForm({{ pascalize .Name }}MaxParseMemory); err != nil {
if err != http.ErrNotMultipart {
return errors.New(400,"%v",err)
} else if err := r.ParseForm(); err != nil {
return errors.New(400,"%v",err)
}
}
{{- if .HasFormValueParams }}
fds := runtime.Values(r.Form)
{{- end }}
{{- end }}
{{ range .Params }}
{{- if not .IsArray }}
{{- if .IsQueryParam }}
q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, _ := qs.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{- else if .IsPathParam }}
r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, _ := route.Params.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{- else if .IsHeaderParam }}
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r.Header[http.CanonicalHeaderKey({{ .Path }})], true, route.Formats); err != nil {
res = append(res, err)
}
{{- else if .IsFormParam }}
{{- if .IsFileParam }}
{{ camelize .Name }}, {{ camelize .Name }}Header, err := r.FormFile({{ .Path }})
if err != nil {{ if .IsNullable }}&& err != http.ErrMissingFile{{ end }}{
res = append(res, errors.New(400, "reading file %q failed: %v", {{ printf "%q" (camelize .Name) }}, err))
{{- if .IsNullable }}
} else if err == http.ErrMissingFile {
// no-op for missing but optional file parameter
{{- end }}
} else if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}({{ camelize .Name }}, {{ camelize .Name }}Header); err != nil {
{{- if .Required }}
// Required: true
{{- end }}
res = append(res, err)
} else {
{{ .ReceiverName }}.{{ pascalize .Name }} = &runtime.File{Data: {{ camelize .Name }}, Header: {{ camelize .Name }}Header}
}
{{- else }}
fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, _ := fds.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{- end }}
{{- end }}
{{- else if .IsArray }}
{{- if .IsQueryParam }}
q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, _ := qs.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{- else if .IsPathParam }}
r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, _ := route.Params.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{- else if .IsHeaderParam }}
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r.Header[http.CanonicalHeaderKey({{ .Path }})], true, route.Formats); err != nil {
res = append(res, err)
}
{{- else if and .IsFormParam }}
fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, _ := fds.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{- end }}
{{- end }}
{{- if and .IsBodyParam .Schema }}
if runtime.HasBody(r) {
{{- if .Schema.IsStream }}
{{ .ReceiverName }}.{{ pascalize .Name }} = r.Body
{{- else }}
defer r.Body.Close()
{{- if and .Schema.IsBaseType .Schema.IsExported }}
body, err := {{ toPackageName .ModelsPackage }}.Unmarshal{{ dropPackage .GoType }}{{ if .IsArray }}Slice{{ end }}(r.Body, route.Consumer)
if err != nil {
{{- if .Required }}
if err == io.EOF {
err = errors.Required({{ .Path }}, {{ printf "%q" .Location }}, "")
}
{{- end }}
res = append(res, err)
{{- else }}
var body {{ .GoType }}
if err := route.Consumer.Consume(r.Body, &body); err != nil {
{{- if .Required }}
if err == io.EOF {
res = append(res, errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, ""))
} else {
{{- end }}
res = append(res, errors.NewParseError({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, "", err))
{{- if .Required }}
}
{{- end }}
{{- end }}
} else {
{{- template "bodyvalidator" . }}
}
{{- end }}
}
{{- if .Required }} else {
res = append(res, errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, ""))
}
{{- end }}
{{- end }}
{{- end }}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
{{- $className := (pascalize .Name) }}
{{ range .Params }}
{{- if .IsFileParam }}
// bind{{ pascalize .ID }} binds file parameter {{ .ID }}.
//
// The only supported validations on files are MinLength and MaxLength
func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(file multipart.File, header *multipart.FileHeader) error {
{{- if or .MinLength .MaxLength }}
size, _ := file.Seek(0, io.SeekEnd)
file.Seek(0, io.SeekStart)
{{- end }}
{{- if .MinLength}}
if size < {{.MinLength}} {
return errors.ExceedsMinimum({{ .Path }}, {{ printf "%q" .Location }}, {{ .MinLength }}, false, size)
}
{{- end }}
{{- if .MaxLength}}
if size > {{.MaxLength}} {
return errors.ExceedsMaximum({{ .Path }}, {{ printf "%q" .Location }}, {{ .MaxLength }}, false, size)
}
{{- end }}
return nil
}
{{- else if not .IsBodyParam }}
{{- if or .IsPrimitive .IsCustomFormatter }}
// bind{{ pascalize .ID }} binds and validates parameter {{ .ID }} from {{ .Location }}.
func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(rawData []string, hasKey bool, formats strfmt.Registry) error {
{{- if and (not .IsPathParam) .Required }}
if !hasKey {
return errors.Required({{ .Path }}, {{ printf "%q" .Location }}, rawData)
}
{{- end }}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: {{ .Required }}
{{- if .IsQueryParam }}
// AllowEmptyValue: {{ .AllowEmptyValue }}
{{- end }}
{{- if .IsPathParam }}
// Parameter is provided by construction from the route
{{- end }}
{{- if and (not .IsPathParam) .Required (not .AllowEmptyValue) }}
if err := validate.RequiredString({{ .Path }}, {{ printf "%q" .Location }}, raw); err != nil {
return err
}
{{- else if and ( not .IsPathParam ) (or (not .Required) .AllowEmptyValue) }}
if raw == "" { // empty values pass all other validations
{{- if .HasDefault }}
// Default values have been previously initialized by New{{ $className }}Params()
{{- end }}
return nil
}
{{- end }}
{{- if .Converter }}
value, err := {{ .Converter }}(raw)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, raw)
}
{{ .ValueExpression }} = {{ if .IsNullable }}&{{ end }}value
{{- else if .IsCustomFormatter }}
// Format: {{ .SwaggerFormat }}
value, err := formats.Parse({{ printf "%q" .SwaggerFormat }}, raw)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, raw)
}
{{ .ValueExpression }} = {{ if or .IsArray .HasDiscriminator .IsFileParam .IsStream (not .IsNullable) }}*{{ end }}(value.(*{{ .GoType }}))
{{- else}}
{{ .ValueExpression }} = {{ if .IsNullable }}&{{ end }}raw
{{- end }}
{{- if .HasValidations }}
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil {
return err
}
{{- end }}
return nil
}
{{- else if .IsArray }}
// bind{{ pascalize .ID }} binds and validates array parameter {{ .ID }} from {{ .Location }}.
//
// Arrays are parsed according to CollectionFormat: "{{ .CollectionFormat }}" (defaults to "csv" when empty).
func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(rawData []string, hasKey bool, formats strfmt.Registry) error {
{{- if .Required }}
if !hasKey {
return errors.Required({{ .Path }}, {{ printf "%q" .Location }}, rawData)
}
{{- end }}
{{- if eq .CollectionFormat "multi" }}
// CollectionFormat: {{ .CollectionFormat }}
{{ varname .Child.ValueExpression }}C := rawData
{{- else }}
var qv{{ pascalize .Name }} string
if len(rawData) > 0 {
qv{{ pascalize .Name }} = rawData[len(rawData) - 1]
}
// CollectionFormat: {{ .CollectionFormat }}
{{ varname .Child.ValueExpression }}C := swag.SplitByFormat(qv{{ pascalize .Name }}, {{ printf "%q" .CollectionFormat }})
{{- end }}
{{- if and .Required (not .AllowEmptyValue) }}
if len({{ varname .Child.ValueExpression }}C) == 0 {
return errors.Required({{ .Path }}, {{ printf "%q" .Location }}, {{ varname .Child.ValueExpression }}C)
}
{{- else }}
if len({{ varname .Child.ValueExpression }}C) == 0 {
{{- if .HasDefault }}
// Default values have been previously initialized by New{{ $className }}Params()
{{- end }}
return nil
} {{- end }}
{{ template "sliceparambinder" . }}
{{ .ValueExpression }} = {{ varname .Child.ValueExpression }}R
{{- if .HasSliceValidations }}
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil {
return err
}
{{- end }}
return nil
}
{{- end }}
{{- if or (and (not .IsArray) .HasValidations) (and .IsArray .HasSliceValidations) }}
// validate{{ pascalize .ID }} carries on validations for parameter {{ .ID }}
func ({{ .ReceiverName }} *{{ $className }}Params) validate{{ pascalize .ID }}(formats strfmt.Registry) error {
{{ template "propertyparamvalidator" . }}
return nil
}
{{- end }}
{{- else if .IsBodyParam }}{{/* validation method for inline body parameters with validations */}}
{{- if and .HasSimpleBodyParams (not .HasModelBodyItems) (not .HasModelBodyMap) }}
{{- if .Schema.HasValidations }}
// validate{{ pascalize .ID }}Body validates an inline body parameter
func ({{ .ReceiverName }} *{{ $className }}Params) validate{{ pascalize .ID }}Body(formats strfmt.Registry) error {
{{- if .IsArray }}
{{- if .HasSliceValidations }}
{{- template "sliceparamvalidator" . }}
{{- end }}
{{- if .Child.HasValidations }}
{{ varname .Child.ValueExpression }}C := {{ .ValueExpression }}
{{ template "sliceparambinder" . }}
{{ .ValueExpression }} = {{ varname .Child.ValueExpression }}R
{{- end }}
{{- else if .IsMap }}
{{ varname .Child.ValueExpression }}C := {{ .ValueExpression }}
{{ template "mapparamvalidator" . }}
{{ .ValueExpression }} = {{ varname .Child.ValueExpression }}R
{{- else }}
{{ template "propertyparamvalidator" . }}
{{- end }}
return nil
}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,271 @@
{{ define "serverheaderbuilder" }}
{{ if not .IsArray }}{{ template "simpleserverheaderbuilder" . }}{{ else }}{{ template "sliceserverheaderbuilder" . }}{{ end }}
{{- end }}
{{ define "simpleserverheaderbuilder" }}
{{ if .IsNullable -}}
var {{ varname .ID }} string
if {{ .ReceiverName }}.{{ pascalize .ID }} != nil {
{{ varname .ID }} = {{ if .Formatter }}{{ .Formatter }}(*{{ .ReceiverName }}.{{ pascalize .ID }}){{ else }}{{ if not .IsCustomFormatter }}*{{ end }}{{ .ReceiverName }}.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
}
{{ else }}{{ varname .ID }} := {{ if .Formatter }}{{ .Formatter }}({{ .ReceiverName }}.{{ pascalize .ID }}){{ else }}{{ .ReceiverName }}.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
{{ end -}}
if {{ varname .ID }} != "" {
rw.Header().Set({{ printf "%q" .Name }}, {{ varname .ID }})
}
{{ end }}
{{ define "sliceitemserverheaderbuilder" }}
{{ if .IsNullable -}}
var {{ .ValueExpression }}S string
if {{ .ValueExpression }} != nil {
{{ .ValueExpression }}S = {{ if .Formatter }}{{ .Formatter }}(*{{ .ValueExpression }}){{ else }}*{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
}
{{ else -}}
{{ .ValueExpression }}S := {{ if .Formatter }}{{ .Formatter }}({{ .ValueExpression }}){{ else }}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
{{ end -}}
if {{ .ValueExpression }}S != "" {
{{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}S)
}
{{ end }}
{{define "sliceserverheaderbuilder" }}
var {{ varname .Child.ValueExpression }}R []string
for _, {{ varname .Child.ValueExpression }} := range {{ .ValueExpression }} {
{{- if not .Child.IsArray }}{{ template "sliceitemserverheaderbuilder" .Child }}{{ else }}{{ template "sliceserverheaderbuilder" .Child }}{{ end -}}
}
{{ if not .Child.Parent -}}
{{ varname .ID }} := swag.JoinByFormat({{ varname .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }})
if len({{ varname .ID }}) > 0 {
hv := {{ varname .ID }}[0]
if hv != "" {
rw.Header().Set({{ printf "%q" .Name }}, hv)
}
}
{{ else -}}
{{ .ValueExpression }}S := swag.JoinByFormat({{ varname .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }})
if len({{ .ValueExpression }}S) > 0 {
{{ .ValueExpression }}Ss := {{ .ValueExpression }}S[0]
if {{ .ValueExpression }}Ss != "" {
{{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}Ss)
}
}
{{ end -}}
{{ end -}}
{{ define "serverresponse" }}
{{ if ne .Code -1 }}// {{pascalize .Name}}Code is the HTTP code returned for type {{ pascalize .Name}}
const {{ pascalize .Name}}Code int = {{ .Code }}{{ end }}
/*{{ if .Description }}{{ pascalize .Name }} {{ blockcomment .Description }}{{else}}{{ pascalize .Name }} {{ humanize .Name }}{{end}}
swagger:response {{ camelize .Name }}
*/
type {{ pascalize .Name }} struct {
{{ if eq .Code -1 }}
_statusCode int
{{ end }}{{ range .Headers }}/*{{if .Description }}{{ blockcomment .Description }}{{ end }}
{{ if .Maximum }}
Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}{{ end }}{{ if .Minimum }}
Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}{{ end }}{{ if .MultipleOf }}
Multiple Of: {{ .MultipleOf }}{{ end }}{{ if .MaxLength }}
Max Length: {{ .MaxLength }}{{ end }}{{ if .MinLength }}
Min Length: {{ .MinLength }}{{ end }}{{ if .Pattern }}
Pattern: {{ .Pattern }}{{ end }}{{ if .MaxItems }}
Max Items: {{ .MaxItems }}{{ end }}{{ if .MinItems }}
Min Items: {{ .MinItems }}{{ end }}{{ if .UniqueItems }}
Unique: true{{ end }}{{ if .HasDefault }}
Default: {{ printf "%#v" .Default }}{{ end }}
*/
{{ pascalize .Name }} {{ .GoType }} `json:"{{.Name}}{{ if not .Required }},omitempty{{ end }}{{ if .IsJSONString }},string{{ end }}"`
{{ end }}
{{ if .Schema }}{{ with .Schema }}
/*{{if .Description }}{{ blockcomment .Description }}{{ end }}{{ if .Maximum }}
Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}{{ end }}{{ if .Minimum }}
Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}{{ end }}{{ if .MultipleOf }}
Multiple Of: {{ .MultipleOf }}{{ end }}{{ if .MaxLength }}
Max Length: {{ .MaxLength }}{{ end }}{{ if .MinLength }}
Min Length: {{ .MinLength }}{{ end }}{{ if .Pattern }}
Pattern: {{ .Pattern }}{{ end }}{{ if .MaxItems }}
Max Items: {{ .MaxItems }}{{ end }}{{ if .MinItems }}
Min Items: {{ .MinItems }}{{ end }}{{ if .UniqueItems }}
Unique: true{{ end }}
In: Body
*/{{ end }}
Payload {{ if and (not .Schema.IsBaseType) .Schema.IsComplexObject }}*{{ end }}{{ .Schema.GoType }} `json:"body,omitempty"`
{{ end }}
}
// New{{ pascalize .Name }} creates {{ pascalize .Name }} with default headers values
func New{{ pascalize .Name }}({{ if eq .Code -1 }}code int{{ end }}) *{{ pascalize .Name }} { {{ if eq .Code -1 }}
if code <= 0 {
code = 500
}
{{ end }}
{{ if .Headers.HasSomeDefaults }}
var (
// initialize headers with default values
{{ range .Headers }}
{{ if .HasDefault -}}
{{ varname .ID}}Default =
{{- if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"" )) }}{{ .Zero }}{{/* strfmt type initializer requires UnmarshalText(), e.g. Date, Datetime, Duration */}}
{{- else if and .IsPrimitive .IsCustomFormatter (stringContains .Zero "(\"" ) }}{{.GoType}}({{- printf "%#v" .Default }}){{/* strfmt type initializer takes string */}}
{{- else if and .IsPrimitive (not .IsCustomFormatter) -}}{{.GoType}}({{- printf "%#v" .Default }}){{/* regular go primitive type initializer */}}
{{- else if .IsArray -}}{{- /* Do not initialize from possible defaults in nested arrays */ -}}
{{- if and .Child.IsPrimitive .Child.IsCustomFormatter }}{{ .Zero }}{{/* initialization strategy with UnmarshalText() */}}
{{- else if .Child.IsArray -}}{{ .Zero }}{{/* initialization strategy with json.Unmarshal() */}}
{{- else if and .Child.IsPrimitive (not .Child.IsCustomFormatter) -}}{{.GoType}}{{- arrayInitializer .Default }}{{/* regular go primitive type initializer: simple slice initializer */}}
{{- else }}{{ printf "%#v" .Default }}{{/* all other cases (e.g. schema) [should not occur] */}}
{{- end }}
{{- else }}{{ printf "%#v" .Default }}{{/* case .Schema */}}
{{- end }}
{{- end }}
{{- end }}
)
{{ range .Headers }}{{ if .HasDefault -}}{{- /* carry out UnmarshalText initialization strategy */ -}}
{{ if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"")) }}{{ varname .ID}}Default.UnmarshalText([]byte({{ printf "%q" .Default }}))
{{ else if .IsArray -}}
{{ if or ( and .Child.IsPrimitive .Child.IsCustomFormatter ) .Child.IsArray -}}
if err := json.Unmarshal([]byte(`{{printf "%s" (json .Default)}}`), &{{ varname .ID }}Default); err != nil {
// panics if specification is invalid
msg := fmt.Sprintf("invalid default value for header {{ varname .ID }}: %v",err)
panic(msg)
}
{{ end -}}
{{- end }}
{{- end }}
{{- end }}
{{ end }}
return &{{ pascalize .Name }}{
{{ if eq .Code -1 }}_statusCode: code,{{ end }}
{{ range .Headers }}{{ if .HasDefault }}
{{ pascalize .Name}}: {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}&{{ end }}{{ varname .ID }}Default,
{{ end }}
{{ end -}}
}
}
{{ if eq .Code -1 }}
// WithStatusCode adds the status to the {{ humanize .Name }} response
func ({{ .ReceiverName }} *{{ pascalize .Name }}) WithStatusCode(code int) *{{ pascalize .Name }} {
{{ .ReceiverName }}._statusCode = code
return {{ .ReceiverName }}
}
// SetStatusCode sets the status to the {{ humanize .Name }} response
func ({{ .ReceiverName }} *{{ pascalize .Name }}) SetStatusCode(code int) {
{{ .ReceiverName }}._statusCode = code
}
{{ end }}{{ range .Headers }}
// With{{ pascalize .Name }} adds the {{ camelize .Name }} to the {{ humanize $.Name }} response
func ({{ $.ReceiverName }} *{{ pascalize $.Name }}) With{{ pascalize .Name }}({{ varname .Name }} {{ .GoType}}) *{{ pascalize $.Name }} {
{{ $.ReceiverName }}.{{ pascalize .Name }} = {{ varname .Name }}
return {{ .ReceiverName }}
}
// Set{{ pascalize .Name }} sets the {{ camelize .Name }} to the {{ humanize $.Name }} response
func ({{ $.ReceiverName }} *{{ pascalize $.Name }}) Set{{ pascalize .Name }}({{ varname .Name }} {{ .GoType}}) {
{{ $.ReceiverName }}.{{ pascalize .Name }} = {{ varname .Name }}
}
{{ end }}{{ if .Schema }}
// WithPayload adds the payload to the {{ humanize .Name }} response
func ({{ .ReceiverName }} *{{ pascalize .Name }}) WithPayload(payload {{ if and .Schema.IsComplexObject (not .Schema.IsBaseType) }}*{{ end }}{{ .Schema.GoType }}) *{{ pascalize .Name }} {
{{ .ReceiverName }}.Payload = payload
return {{ .ReceiverName }}
}
// SetPayload sets the payload to the {{ humanize .Name }} response
func ({{ .ReceiverName }} *{{ pascalize .Name }}) SetPayload(payload {{ if and .Schema.IsComplexObject (not .Schema.IsBaseType) }}*{{ end }}{{ .Schema.GoType }}) {
{{ .ReceiverName }}.Payload = payload
}
{{ end }}
// WriteResponse to the client
func ({{ .ReceiverName }} *{{ pascalize .Name }}) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
{{ range .Headers }}
// response header {{.Name}}
{{ template "serverheaderbuilder" . -}}
{{ end }}
{{ if not .Schema }}
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
{{ end }}
rw.WriteHeader({{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }})
{{- if .Schema }}
{{- if .Schema.IsComplexObject }}
if {{ .ReceiverName }}.Payload != nil {
{{- end }}
payload := {{ .ReceiverName }}.Payload
{{- if and (not .Schema.IsInterface) (or .Schema.IsArray .Schema.IsMap) }}
if payload == nil {
// return empty {{ if .Schema.IsArray }}array{{ else if .Schema.IsMap }}map{{ end }}
payload =
{{- if or .Schema.IsAliased .Schema.IsComplexObject }}
{{- if and (not .Schema.IsBaseType) .Schema.IsComplexObject }}&{{ end }}{{ .Schema.GoType -}} {}
{{- else }}
{{- .Schema.Zero }}
{{- end }}
}
{{ end }}
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
{{- if .Schema.IsComplexObject }}
}
{{- end }}
{{- end }}
}
{{ if $.StrictResponders }}
func ({{ .ReceiverName }} *{{ pascalize .Name }}) {{ pascalize .OperationName }}Responder() {}
{{- end }}
{{ end }}// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/security"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
"github.com/go-openapi/runtime/middleware"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
{{ range .Responses }}
{{ template "serverresponse" . }}
{{ end }}
{{ if .DefaultResponse }}
{{ template "serverresponse" .DefaultResponse }}
{{ end }}
{{ if $.StrictResponders }}
type {{ pascalize .Name }}NotImplementedResponder struct {
middleware.Responder
}
func (*{{ pascalize .Name }}NotImplementedResponder) {{ pascalize .Name }}Responder() {}
func {{ pascalize .Name }}NotImplemented() {{ pascalize .Name }}Responder {
return &{{ pascalize .Name }}NotImplementedResponder{
middleware.NotImplemented(
"operation authentication.{{ pascalize .Name }} has not yet been implemented",
),
}
}
type {{ pascalize .Name }}Responder interface {
middleware.Responder
{{ pascalize .Name }}Responder()
}
{{ end }}

View file

@ -0,0 +1,660 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .APIPackage }}
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/go-openapi/swag"
{{ if .UseGoStructFlags }}flags "github.com/jessevdk/go-flags"
{{ end -}}
"github.com/go-openapi/runtime/flagext"
{{ if .UsePFlags }}flag "github.com/spf13/pflag"
{{ end -}}
{{ if .UseFlags }}"flag"
"strings"
{{ end -}}
"golang.org/x/net/netutil"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
const (
schemeHTTP = "http"
schemeHTTPS = "https"
schemeUnix = "unix"
)
var defaultSchemes []string
func init() {
defaultSchemes = []string{ {{ if (hasInsecure .Schemes) }}
schemeHTTP,{{ end}}{{ if (hasSecure .Schemes) }}
schemeHTTPS,{{ end }}{{ if (contains .ExtraSchemes "unix") }}
schemeUnix,{{ end }}
}
}
{{ if not .UseGoStructFlags}}
var ({{ if .ExcludeSpec }}
specFile string
{{ end }}enabledListeners []string
cleanupTimeout time.Duration
gracefulTimeout time.Duration
maxHeaderSize flagext.ByteSize
socketPath string
host string
port int
listenLimit int
keepAlive time.Duration
readTimeout time.Duration
writeTimeout time.Duration
tlsHost string
tlsPort int
tlsListenLimit int
tlsKeepAlive time.Duration
tlsReadTimeout time.Duration
tlsWriteTimeout time.Duration
tlsCertificate string
tlsCertificateKey string
tlsCACertificate string
)
{{ if .UseFlags}}
// StringSliceVar support for flag
type sliceValue []string
func newSliceValue(vals []string, p *[]string) *sliceValue {
*p = vals
return (*sliceValue)(p)
}
func (s *sliceValue) Set(val string) error {
*s = sliceValue(strings.Split(val, ","))
return nil
}
func (s *sliceValue) Get() interface{} { return []string(*s) }
func (s *sliceValue) String() string { return strings.Join([]string(*s), ",") }
// end StringSliceVar support for flag
{{ end }}
func init() {
maxHeaderSize = flagext.ByteSize(1000000){{ if .ExcludeSpec }}
flag.StringVarP(&specFile, "spec", "", "", "the swagger specification to serve")
{{ end }}
{{ if .UseFlags }}
flag.Var(newSliceValue(defaultSchemes, &enabledListeners), "schema", "the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec")
{{ end }}
{{ if .UsePFlags }}
flag.StringSliceVar(&enabledListeners, "scheme", defaultSchemes, "the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec")
{{ end }}
flag.DurationVar(&cleanupTimeout, "cleanup-timeout", 10*time.Second, "grace period for which to wait before killing idle connections")
flag.DurationVar(&gracefulTimeout, "graceful-timeout", 15*time.Second, "grace period for which to wait before shutting down the server")
flag.Var(&maxHeaderSize, "max-header-size", "controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body")
flag.StringVar(&socketPath, "socket-path", "/var/run/todo-list.sock", "the unix socket to listen on")
flag.StringVar(&host, "host", "localhost", "the IP to listen on")
flag.IntVar(&port, "port", 0, "the port to listen on for insecure connections, defaults to a random value")
flag.IntVar(&listenLimit, "listen-limit", 0, "limit the number of outstanding requests")
flag.DurationVar(&keepAlive, "keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)")
flag.DurationVar(&readTimeout, "read-timeout", 30*time.Second, "maximum duration before timing out read of the request")
flag.DurationVar(&writeTimeout, "write-timeout", 30*time.Second, "maximum duration before timing out write of the response")
flag.StringVar(&tlsHost, "tls-host", "localhost", "the IP to listen on")
flag.IntVar(&tlsPort, "tls-port", 0, "the port to listen on for secure connections, defaults to a random value")
flag.StringVar(&tlsCertificate, "tls-certificate", "", "the certificate file to use for secure connections")
flag.StringVar(&tlsCertificateKey, "tls-key", "", "the private key file to use for secure connections (without passphrase)")
flag.StringVar(&tlsCACertificate, "tls-ca", "", "the certificate authority certificate file to be used with mutual tls auth")
flag.IntVar(&tlsListenLimit, "tls-listen-limit", 0, "limit the number of outstanding requests")
flag.DurationVar(&tlsKeepAlive, "tls-keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)")
flag.DurationVar(&tlsReadTimeout, "tls-read-timeout", 30*time.Second, "maximum duration before timing out read of the request")
flag.DurationVar(&tlsWriteTimeout, "tls-write-timeout", 30*time.Second, "maximum duration before timing out write of the response")
}
func stringEnvOverride(orig string, def string, keys ...string) string {
for _, k := range keys {
if os.Getenv(k) != "" {
return os.Getenv(k)
}
}
if def != "" && orig == "" {
return def
}
return orig
}
func intEnvOverride(orig int, def int, keys ...string) int {
for _, k := range keys {
if os.Getenv(k) != "" {
v, err := strconv.Atoi(os.Getenv(k))
if err != nil {
fmt.Fprintln(os.Stderr, k, "is not a valid number")
os.Exit(1)
}
return v
}
}
if def != 0 && orig == 0 {
return def
}
return orig
}
{{ end }}
// NewServer creates a new api {{ humanize .Name }} server but does not configure it
func NewServer(api *{{ .APIPackageAlias }}.{{ pascalize .Name }}API) *Server {
s := new(Server)
{{ if not .UseGoStructFlags }}
s.EnabledListeners = enabledListeners
s.CleanupTimeout = cleanupTimeout
s.GracefulTimeout = gracefulTimeout
s.MaxHeaderSize = maxHeaderSize
s.SocketPath = socketPath
s.Host = stringEnvOverride(host, "", "HOST")
s.Port = intEnvOverride(port, 0, "PORT")
s.ListenLimit = listenLimit
s.KeepAlive = keepAlive
s.ReadTimeout = readTimeout
s.WriteTimeout = writeTimeout
s.TLSHost = stringEnvOverride(tlsHost, s.Host, "TLS_HOST", "HOST")
s.TLSPort = intEnvOverride(tlsPort, 0, "TLS_PORT")
s.TLSCertificate = stringEnvOverride(tlsCertificate, "", "TLS_CERTIFICATE")
s.TLSCertificateKey = stringEnvOverride(tlsCertificateKey, "", "TLS_PRIVATE_KEY")
s.TLSCACertificate = stringEnvOverride(tlsCACertificate, "", "TLS_CA_CERTIFICATE")
s.TLSListenLimit = tlsListenLimit
s.TLSKeepAlive = tlsKeepAlive
s.TLSReadTimeout = tlsReadTimeout
s.TLSWriteTimeout = tlsWriteTimeout
{{- if .ExcludeSpec }}
s.Spec = specFile
{{- end }}
{{- end }}
s.shutdown = make(chan struct{})
s.api = api
s.interrupt = make(chan os.Signal, 1)
return s
}
// ConfigureAPI configures the API and handlers.
func (s *Server) ConfigureAPI() {
if s.api != nil {
s.handler = configureAPI(s.api)
}
}
// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
func (s *Server) ConfigureFlags() {
if s.api != nil {
configureFlags(s.api)
}
}
// Server for the {{ humanize .Name }} API
type Server struct {
EnabledListeners []string{{ if .UseGoStructFlags }} `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`{{ end }}
CleanupTimeout time.Duration{{ if .UseGoStructFlags }} `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`{{ end }}
GracefulTimeout time.Duration{{ if .UseGoStructFlags }} `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`{{ end }}
MaxHeaderSize flagext.ByteSize{{ if .UseGoStructFlags }} `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`{{ end }}
SocketPath {{ if not .UseGoStructFlags }}string{{ else }}flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/{{ dasherize .Name }}.sock"`{{ end }}
domainSocketL net.Listener
Host string{{ if .UseGoStructFlags }} `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`{{ end }}
Port int{{ if .UseGoStructFlags }} `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`{{ end }}
ListenLimit int{{ if .UseGoStructFlags }} `long:"listen-limit" description:"limit the number of outstanding requests"`{{ end }}
KeepAlive time.Duration{{ if .UseGoStructFlags }} `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`{{ end }}
ReadTimeout time.Duration{{ if .UseGoStructFlags }} `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`{{ end }}
WriteTimeout time.Duration{{ if .UseGoStructFlags }} `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`{{ end }}
httpServerL net.Listener
TLSHost string{{ if .UseGoStructFlags }} `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`{{ end }}
TLSPort int{{ if .UseGoStructFlags }} `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`{{ end }}
TLSCertificate {{ if not .UseGoStructFlags }}string{{ else }}flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`{{ end }}
TLSCertificateKey {{ if not .UseGoStructFlags }}string{{ else }}flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`{{ end }}
TLSCACertificate {{ if not .UseGoStructFlags }}string{{ else }}flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`{{ end }}
TLSListenLimit int{{ if .UseGoStructFlags }} `long:"tls-listen-limit" description:"limit the number of outstanding requests"`{{ end }}
TLSKeepAlive time.Duration{{ if .UseGoStructFlags }} `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`{{ end }}
TLSReadTimeout time.Duration{{ if .UseGoStructFlags }} `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`{{ end }}
TLSWriteTimeout time.Duration{{ if .UseGoStructFlags }} `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`{{ end }}
httpsServerL net.Listener
{{ if .ExcludeSpec }}Spec {{ if not .UseGoStructFlags }}string{{ else }}flags.Filename `long:"spec" description:"the swagger specification to serve"`{{ end }}{{ end }}
api *{{ .APIPackageAlias }}.{{ pascalize .Name }}API
handler http.Handler
hasListeners bool
shutdown chan struct{}
shuttingDown int32
interrupted bool
interrupt chan os.Signal
}
// Logf logs message either via defined user logger or via system one if no user logger is defined.
func (s *Server) Logf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
} else {
log.Printf(f, args...)
}
}
// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
// Exits with non-zero status after printing
func (s *Server) Fatalf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
os.Exit(1)
} else {
log.Fatalf(f, args...)
}
}
// SetAPI configures the server with the specified API. Needs to be called before Serve
func (s *Server) SetAPI(api *{{ .APIPackageAlias }}.{{ pascalize .Name }}API) {
if api == nil {
s.api = nil
s.handler = nil
return
}
s.api = api
s.handler = configureAPI(api)
}
func (s *Server) hasScheme(scheme string) bool {
schemes := s.EnabledListeners
if len(schemes) == 0 {
schemes = defaultSchemes
}
for _, v := range schemes {
if v == scheme {
return true
}
}
return false
}
// Serve the api
func (s *Server) Serve() (err error) {
if !s.hasListeners {
if err = s.Listen(); err != nil {
return err
}
}
// set default handler, if none is set
if s.handler == nil {
if s.api == nil {
return errors.New("can't create the default handler, as no api is set")
}
s.SetHandler(s.api.Serve(nil))
}
wg := new(sync.WaitGroup)
once := new(sync.Once)
signalNotify(s.interrupt)
go handleInterrupt(once, s)
servers := []*http.Server{}
if s.hasScheme(schemeUnix) {
domainSocket := new(http.Server)
domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
domainSocket.Handler = s.handler
if int64(s.CleanupTimeout) > 0 {
domainSocket.IdleTimeout = s.CleanupTimeout
}
configureServer(domainSocket, "unix", string(s.SocketPath))
servers = append(servers, domainSocket)
wg.Add(1)
s.Logf("Serving {{ humanize .Name }} at unix://%s", s.SocketPath)
go func(l net.Listener){
defer wg.Done()
if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving {{ humanize .Name }} at unix://%s", s.SocketPath)
}(s.domainSocketL)
}
if s.hasScheme(schemeHTTP) {
httpServer := new(http.Server)
httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpServer.ReadTimeout = s.ReadTimeout
httpServer.WriteTimeout = s.WriteTimeout
httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
if s.ListenLimit > 0 {
s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
}
if int64(s.CleanupTimeout) > 0 {
httpServer.IdleTimeout = s.CleanupTimeout
}
httpServer.Handler = s.handler
configureServer(httpServer, "http", s.httpServerL.Addr().String())
servers = append(servers, httpServer)
wg.Add(1)
s.Logf("Serving {{ humanize .Name }} at http://%s", s.httpServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving {{ humanize .Name }} at http://%s", l.Addr())
}(s.httpServerL)
}
if s.hasScheme(schemeHTTPS) {
httpsServer := new(http.Server)
httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpsServer.ReadTimeout = s.TLSReadTimeout
httpsServer.WriteTimeout = s.TLSWriteTimeout
httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
if s.TLSListenLimit > 0 {
s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
}
if int64(s.CleanupTimeout) > 0 {
httpsServer.IdleTimeout = s.CleanupTimeout
}
httpsServer.Handler = s.handler
// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
httpsServer.TLSConfig = &tls.Config{
// Causes servers to use Go's default ciphersuite preferences,
// which are tuned to avoid attacks. Does nothing on clients.
PreferServerCipherSuites: true,
// Only use curves which have assembly implementations
// https://github.com/golang/go/tree/master/src/crypto/elliptic
CurvePreferences: []tls.CurveID{tls.CurveP256},
{{- if .UseModernMode }}
// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
NextProtos: []string{"h2", "http/1.1"},
// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
MinVersion: tls.VersionTLS12,
// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
{{- end }}
}
// build standard config from server options
if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair({{ if .UseGoStructFlags }}string({{ end }}s.TLSCertificate{{ if .UseGoStructFlags }}){{ end }}, {{ if .UseGoStructFlags }}string({{ end }}s.TLSCertificateKey{{ if .UseGoStructFlags }}){{ end }})
if err != nil {
return err
}
}
if s.TLSCACertificate != "" {
// include specified CA certificate
caCert, caCertErr := os.ReadFile({{ if .UseGoStructFlags }}string({{ end }}s.TLSCACertificate{{ if .UseGoStructFlags }}){{ end }})
if caCertErr != nil {
return caCertErr
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(caCert)
if !ok {
return fmt.Errorf("cannot parse CA certificate")
}
httpsServer.TLSConfig.ClientCAs = caCertPool
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
// call custom TLS configurator
configureTLS(httpsServer.TLSConfig)
if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
// after standard and custom config are passed, this ends up with no certificate
if s.TLSCertificate == "" {
if s.TLSCertificateKey == "" {
s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
}
s.Fatalf("the required flag `--tls-certificate` was not specified")
}
if s.TLSCertificateKey == "" {
s.Fatalf("the required flag `--tls-key` was not specified")
}
// this happens with a wrong custom TLS configurator
s.Fatalf("no certificate was configured for TLS")
}
configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
servers = append(servers, httpsServer)
wg.Add(1)
s.Logf("Serving {{ humanize .Name }} at https://%s", s.httpsServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving {{ humanize .Name }} at https://%s", l.Addr())
}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
}
wg.Add(1)
go s.handleShutdown(wg, &servers)
wg.Wait()
return nil
}
// Listen creates the listeners for the server
func (s *Server) Listen() error {
if s.hasListeners { // already done this
return nil
}
if s.hasScheme(schemeHTTPS) {
// Use http host if https host wasn't defined
if s.TLSHost == "" {
s.TLSHost = s.Host
}
// Use http listen limit if https listen limit wasn't defined
if s.TLSListenLimit == 0 {
s.TLSListenLimit = s.ListenLimit
}
// Use http tcp keep alive if https tcp keep alive wasn't defined
if int64(s.TLSKeepAlive) == 0 {
s.TLSKeepAlive = s.KeepAlive
}
// Use http read timeout if https read timeout wasn't defined
if int64(s.TLSReadTimeout) == 0 {
s.TLSReadTimeout = s.ReadTimeout
}
// Use http write timeout if https write timeout wasn't defined
if int64(s.TLSWriteTimeout) == 0 {
s.TLSWriteTimeout = s.WriteTimeout
}
}
if s.hasScheme(schemeUnix) {
domSockListener, err := net.Listen("unix", string(s.SocketPath))
if err != nil {
return err
}
s.domainSocketL = domSockListener
}
if s.hasScheme(schemeHTTP) {
listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
if err != nil {
return err
}
h, p, err := swag.SplitHostPort(listener.Addr().String())
if err != nil {
return err
}
s.Host = h
s.Port = p
s.httpServerL = listener
}
if s.hasScheme(schemeHTTPS) {
tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
if err != nil {
return err
}
sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
if err != nil {
return err
}
s.TLSHost = sh
s.TLSPort = sp
s.httpsServerL = tlsListener
}
s.hasListeners = true
return nil
}
// Shutdown server and clean up resources
func (s *Server) Shutdown() error {
if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
close(s.shutdown)
}
return nil
}
func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
// wg.Done must occur last, after s.api.ServerShutdown()
// (to preserve old behaviour)
defer wg.Done()
<-s.shutdown
servers := *serversPtr
ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
defer cancel()
// first execute the pre-shutdown hook
s.api.PreServerShutdown()
shutdownChan := make(chan bool)
for i := range servers {
server := servers[i]
go func() {
var success bool
defer func() {
shutdownChan <- success
}()
if err := server.Shutdown(ctx); err != nil {
// Error from closing listeners, or context timeout:
s.Logf("HTTP server Shutdown: %v", err)
} else {
success = true
}
}()
}
// Wait until all listeners have successfully shut down before calling ServerShutdown
success := true
for range servers {
success = success && <-shutdownChan
}
if success {
s.api.ServerShutdown()
}
}
// GetHandler returns a handler useful for testing
func (s *Server) GetHandler() http.Handler {
return s.handler
}
// SetHandler allows for setting a http handler on this server
func (s *Server) SetHandler(handler http.Handler) {
s.handler = handler
}
// UnixListener returns the domain socket listener
func (s *Server) UnixListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.domainSocketL, nil
}
// HTTPListener returns the http listener
func (s *Server) HTTPListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpServerL, nil
}
// TLSListener returns the https listener
func (s *Server) TLSListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpsServerL, nil
}
func handleInterrupt(once *sync.Once, s *Server) {
once.Do(func(){
for range s.interrupt {
if s.interrupted {
s.Logf("Server already shutting down")
continue
}
s.interrupted = true
s.Logf("Shutting down... ")
if err := s.Shutdown(); err != nil {
s.Logf("HTTP server Shutdown: %v", err)
}
}
})
}
func signalNotify(interrupt chan<- os.Signal) {
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
}

View file

@ -0,0 +1,213 @@
{{ define "queryparambuilder" }}
{{ if not .IsArray }}{{ template "simplequeryparambuilder" . }}{{ else }}{{ template "slicequeryparambuilder" . }}{{ end }}
{{- end }}
{{ define "simplequeryparambuilder" }}
{{ if .IsNullable -}}
var {{ varname .ID }}Q string
if {{ .ReceiverName }}.{{ pascalize .ID }} != nil {
{{ varname .ID }}Q = {{ if .Formatter }}{{ .Formatter }}(*{{ .ReceiverName }}.{{ pascalize .ID }}){{ else }}{{ if not .IsCustomFormatter }}*{{ end }}{{ .ReceiverName }}.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
}
{{ else }}{{ varname .ID }}Q := {{ if .Formatter }}{{ .Formatter }}({{ .ReceiverName }}.{{ pascalize .ID }}){{ else }}{{ .ReceiverName }}.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
{{ end -}}
if {{ varname .ID }}Q != "" {
qs.Set({{ printf "%q" .Name }}, {{ varname .ID }}Q)
}
{{ end }}
{{ define "sliceitemqueryparambuilder" }}
{{ if .IsNullable -}}
var {{ .ValueExpression }}S string
if {{ .ValueExpression }} != nil {
{{ .ValueExpression }}S = {{ if .Formatter }}{{ .Formatter }}(*{{ .ValueExpression }}){{ else }}*{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
}
{{ else -}}
{{ .ValueExpression }}S := {{ if .Formatter }}{{ .Formatter }}({{ .ValueExpression }}){{ else }}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
{{ end -}}
if {{ .ValueExpression }}S != "" {
{{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}S)
}
{{ end }}
{{define "slicequeryparambuilder" }}
var {{ .Child.ValueExpression }}R []string
for _, {{ .Child.ValueExpression }} := range {{ .ValueExpression }} {
{{- if not .Child.IsArray }}{{ template "sliceitemqueryparambuilder" .Child }}{{ else }}{{ template "slicequeryparambuilder" .Child }}{{ end -}}
}
{{ if not .Child.Parent -}}
{{ varname .ID }} := swag.JoinByFormat({{ .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }})
{{ if eq .CollectionFormat "multi" }}
for _, qsv := range {{ varname .ID }} {
qs.Add({{ printf "%q" .Name }}, qsv)
}
{{ else }}
if len({{ varname .ID }}) > 0 {
qsv := {{ varname .ID }}[0]
if qsv != "" {
qs.Set({{ printf "%q" .Name }}, qsv)
}
}
{{ end }}
{{ else -}}
{{ .ValueExpression }}S := swag.JoinByFormat({{ .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }})
if len({{ .ValueExpression }}S) > 0 {
{{ .ValueExpression }}Ss := {{ .ValueExpression }}S[0]
if {{ .ValueExpression }}Ss != "" {
{{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}Ss)
}
}
{{ end -}}
{{ end -}}
{{ define "slicepathparambuilder" }}
var {{ .Child.ValueExpression }}R []string
for _, {{ .Child.ValueExpression }} := range {{ .ValueExpression }} {
{{- if not .Child.IsArray }}{{ template "sliceitemqueryparambuilder" .Child }}{{ else }}{{ template "slicepathparambuilder" .Child }}{{ end -}}
}
{{ if not .Child.Parent -}}
{{ varname .ID }} := swag.JoinByFormat({{ .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }})
if len({{ varname .ID }}) > 0 {
psv := {{ varname .ID }}[0]
if psv != "" {
_path = strings.Replace(_path, "{{ printf "{%s}" .Name }}", psv, -1)
} else {
return nil, errors.New("{{ camelize .ID }} is required on {{ pascalize $.Name }}URL")
}
}
{{ else -}}
{{ .ValueExpression }}S := swag.JoinByFormat({{ .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }})
if len({{ .ValueExpression }}S) > 0 {
{{ .ValueExpression }}Ss := {{ .ValueExpression }}S[0]
if {{ .ValueExpression }}Ss != "" {
{{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}Ss)
}
}
{{ end -}}
{{ end }}
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"fmt"
"errors"
"net/url"
golangswaggerpaths "path"
"strings"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// {{ pascalize .Name }}URL generates an URL for the {{ humanize .Name }} operation
type {{ pascalize .Name }}URL struct {
{{ range .PathParams }}
{{ pascalize .ID }} {{.GoType}}
{{- end }}
{{ range .QueryParams }}
{{ pascalize .ID }} {{ if and (not .IsArray) .IsNullable }}*{{ end }}{{.GoType}}
{{- end }}
_basePath string
{{ if or (gt (len .PathParams ) 0) (gt (len .QueryParams) 0) -}}
// avoid unkeyed usage
_ struct{}
{{- end }}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) WithBasePath(bp string) *{{pascalize .Name}}URL {
{{ .ReceiverName }}.SetBasePath(bp)
return {{ .ReceiverName }}
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) SetBasePath(bp string) {
{{ .ReceiverName }}._basePath = bp
}
// Build a url path and query string
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) Build() (*url.URL, error) {
var _result url.URL
var _path = {{ printf "%q" .Path }}
{{ range .PathParams }}{{ if .IsArray }}
{{ template "slicepathparambuilder" . -}}
{{ else }}
{{ varname .ID }} := {{ if .Formatter }}{{ .Formatter }}({{ .ReceiverName }}.{{ pascalize .ID }}){{ else }}{{ .ReceiverName }}.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}
if {{ varname .ID }} != "" {
_path = strings.Replace(_path, "{{ printf "{%s}" .Name }}", {{ varname .ID }}, -1)
} else {
return nil, errors.New("{{ camelize .ID }} is required on {{ pascalize $.Name }}URL")
}
{{ end }}
{{- end }}
_basePath := {{ .ReceiverName }}._basePath
{{ if .BasePath }}if _basePath == "" {
_basePath = {{ printf "%q" .BasePath }}
}
{{ end -}}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
{{ if gt (len .QueryParams) 0 -}}
qs := make(url.Values)
{{ range .QueryParams }}
{{ template "queryparambuilder" . -}}
{{- end }}
_result.RawQuery = qs.Encode()
{{- end }}
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) String() string {
return {{ .ReceiverName }}.Must({{ .ReceiverName }}.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on {{ pascalize .Name }}URL")
}
if host == "" {
return nil, errors.New("host is required for a full url on {{ pascalize .Name }}URL")
}
base, err := {{ .ReceiverName }}.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func ({{ .ReceiverName }} *{{ pascalize .Name }}URL) StringFull(scheme, host string) string {
return {{ .ReceiverName }}.Must( {{ .ReceiverName }}.BuildFull(scheme, host)).String()
}

View file

@ -0,0 +1,23 @@
{{- if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"")) }}
if err := {{ varname .ID}}Default.UnmarshalText([]byte({{ printf "%q" .Default }})) ; err != nil {
msg := fmt.Sprintf("invalid default value for {{ varname .ID }}: %v",err)
panic(msg) // panics if the specification is invalid
}
{{- else if .IsArray }}
{{- if or ( and .Child.IsPrimitive .Child.IsCustomFormatter ) .Child.IsArray }}
if err := json.Unmarshal([]byte(`{{printf "%s" (json .Default)}}`), &{{ varname .ID }}Default); err != nil {
msg := fmt.Sprintf("invalid default value for {{ varname .ID }}: %v",err)
panic(msg) // panics if the specification is invalid
}
{{- else if and (not .Child.IsPrimitive) (not .Child.IsArray) }} {{/* shouldn't get there: guard */}}
if err := json.Unmarshal([]byte(`{{printf "%s" (json .Default)}}`), &{{ varname .ID }}Default); err != nil {
msg := fmt.Sprintf("invalid default value for {{ varname .ID }}: %v",err)
panic(msg) // panics if the specification is invalid
}
{{- end }}
{{- else if not .IsPrimitive }} {{/* shouldn't get there: guard (defaults to letting json figure out) */}}
if err := json.Unmarshal([]byte(`{{printf "%s" (json .Default)}}`), &{{ varname .ID }}Default); err != nil {
msg := fmt.Sprintf("invalid default value for {{ varname .ID }}: %v",err)
panic(msg) // panics if the specification is invalid
}
{{- end }}

View file

@ -0,0 +1,29 @@
{{- varname .ID}}Default
{{- if .IsPrimitive }}
{{- print " " }}={{ print " " }}
{{- if .IsCustomFormatter }}
{{- if stringContains .Zero "(\"" }}
{{- .GoType }}({{ printf "%#v" .Default }}){{/* strfmt type initializer that takes string */}}
{{- else }}
{{- .Zero }}{{/* strfmt type initializer that requires UnmarshalText(), e.g. Date, Datetime, Duration */}}
{{- end }}
{{- else }}
{{- .GoType }}({{ printf "%#v" .Default }}){{/* regular go primitive type initializer */}}
{{- end }}
{{- else if .IsArray }}{{/* do not initialize from possible defaults in nested arrays */}}
{{- if .Child.IsPrimitive }}
{{- print " " }}={{ print " " }}
{{- if .Child.IsCustomFormatter }}
{{- .Zero }}{{/* initialization strategy with UnmarshalText() */}}
{{- else }}
{{- .GoType }}{{ arrayInitializer .Default }}{{/* regular go primitive type initializer: simple slice initializer */}}
{{- end }}
{{- else if .Child.IsArray }}
{{- print " " }}={{ print " " }}
{{- .Zero }}{{/* initialization strategy with json.Unmarshal() */}}
{{- else }}
{{- print " " }}{{ .GoType }}{{/* shouldn't have that: simple schema is either primitive or array */}}
{{- end }}
{{- else }}
{{- print " " }}{{ .GoType }}{{/* shouldn't have that: simple schema is either primitive or array */}}
{{- end }}

View file

@ -0,0 +1,41 @@
{{ define "structfield" }}
{{- if not $.IsBaseType -}}
// {{ template "docstring" . }}
{{- template "propertyValidationDocString" .}}
{{- end}}
{{ pascalize .Name}} {{ template "schemaType" . }} {{ .PrintTags }}
{{ end }}
{{- define "tuplefield" }}
{{- if not $.IsBaseType -}}
// {{ template "docstring" . }}
{{- template "propertyValidationDocString" .}}
{{ end }}
{{- pascalize .Name}} {{ template "schemaType" . }} `json:"-"
{{- if .CustomTag }} {{ .CustomTag }}{{ end }}` // custom serializer
{{ end }}
{{- define "structfieldIface" }}
{{- if not $.IsBaseType -}}
// {{ template "docstring" . }}
{{- template "propertyValidationDocString" .}}
{{- end }}
{{ pascalize .Name}}() {{ template "schemaType" . }}
Set{{ pascalize .Name}}({{ template "schemaType" . }})
{{ end }}
{{ define "tuplefieldIface" }}
{{- if not $.IsBaseType -}}
// {{ template "docstring" . }}
{{- template "propertyValidationDocString" . }}
{{ end }}
{{- pascalize .Name}}() {{ template "schemaType" . }}
Set{{ pascalize .Name}}({{ template "schemaType" . }})
{{ end }}
{{- define "privstructfield" }}
{{- camelize .Name}}Field {{ template "schemaType" . }}
{{ end }}
{{- define "privtuplefield" }}
{{- camelize .Name}}Field {{ template "schemaType" . }}
{{ end }}

View file

@ -0,0 +1,30 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .APIPackage }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"encoding/json"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
var (
// SwaggerJSON embedded version of the swagger document used at generation time
SwaggerJSON json.RawMessage
// FlatSwaggerJSON embedded flattened version of the swagger document used at generation time
FlatSwaggerJSON json.RawMessage
)
func init() {
SwaggerJSON = json.RawMessage([]byte(`{{ .SwaggerJSON }}`))
FlatSwaggerJSON = json.RawMessage([]byte(`{{ .FlatSwaggerJSON }}`))
}

View file

@ -0,0 +1,3 @@
if err := validate.FormatOf({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ printf "%q" .SwaggerFormat }}, {{ .ToString }}, formats); err != nil {
return err
}

View file

@ -0,0 +1,23 @@
{{- if or (hasPrefix .UnderlyingType "int") }}
{{- if and (hasPrefix .UnderlyingType "int64") (not .IsAliased) }}
if err := validate.MaximumInt({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.Maximum }}, {{.ExclusiveMaximum }}); err != nil {
{{- else }}
if err := validate.MaximumInt({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, int64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.Maximum }}, {{.ExclusiveMaximum }}); err != nil {
{{- end }}
{{- else }}
{{- if hasPrefix .UnderlyingType "uint" }}
{{- if and (hasPrefix .UnderlyingType "uint64") (not .IsAliased) }}
if err := validate.MaximumUint({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.Maximum }}, {{.ExclusiveMaximum }}); err != nil {
{{- else }}
if err := validate.MaximumUint({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, uint64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.Maximum }}, {{.ExclusiveMaximum }}); err != nil {
{{- end }}
{{- else }}
{{- if and (eq .UnderlyingType "float64") (not .IsAliased) }}
if err := validate.Maximum({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.Maximum }}, {{.ExclusiveMaximum }}); err != nil {
{{- else }}
if err := validate.Maximum({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, float64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.Maximum }}, {{.ExclusiveMaximum }}); err != nil {
{{- end }}
{{- end }}
{{- end }}
return err
}

View file

@ -0,0 +1,23 @@
{{- if hasPrefix .UnderlyingType "int" }}
{{- if and (hasPrefix .UnderlyingType "int64") (not .IsAliased) }}
if err := validate.MinimumInt({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.Minimum }}, {{.ExclusiveMinimum }}); err != nil {
{{- else }}
if err := validate.MinimumInt({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, int64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.Minimum }}, {{.ExclusiveMinimum }}); err != nil {
{{- end }}
{{- else }}
{{- if hasPrefix .UnderlyingType "uint" }}
{{- if and (hasPrefix .UnderlyingType "uint64") (not .IsAliased) }}
if err := validate.MinimumUint({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.Minimum }}, {{.ExclusiveMinimum }}); err != nil {
{{- else }}
if err := validate.MinimumUint({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, uint64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.Minimum }}, {{.ExclusiveMinimum }}); err != nil {
{{- end }}
{{- else }}
{{- if and (eq .UnderlyingType "float64") (not .IsAliased) }}
if err := validate.Minimum({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.Minimum }}, {{.ExclusiveMinimum }}); err != nil {
{{- else }}
if err := validate.Minimum({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, float64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.Minimum }}, {{.ExclusiveMinimum }}); err != nil {
{{- end }}
{{- end }}
{{- end }}
return err
}

View file

@ -0,0 +1,23 @@
{{- if and (hasPrefix .UnderlyingType "int") (isInteger .MultipleOf) }}{{/* if the type is an integer, but the multiple factor is not, fall back to the float64 version of the validator */}}
{{- if and (hasPrefix .UnderlyingType "int64") (not .IsAliased) }}
if err := validate.MultipleOfInt({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.MultipleOf }}); err != nil {
{{- else }}
if err := validate.MultipleOfInt({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, int64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.MultipleOf }}); err != nil {
{{- end }}
{{- else }}
{{- if and (hasPrefix .UnderlyingType "uint") (isInteger .MultipleOf) }}
{{- if and (hasPrefix .UnderlyingType "uint64") (not .IsAliased) }}
if err := validate.MultipleOfUint({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.MultipleOf }}); err != nil {
{{- else }}
if err := validate.MultipleOfUint({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, uint64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.MultipleOf }}); err != nil {
{{- end }}
{{- else }}
{{- if and (eq .UnderlyingType "float64") (not .IsAliased) }}
if err := validate.MultipleOf({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, {{ if .IsNullable }}*{{ end }}{{.ValueExpression }}, {{.MultipleOf }}); err != nil {
{{- else }}
if err := validate.MultipleOf({{ if .Path }}{{ .Path }}{{ else }}""{{ end }}, {{ printf "%q" .Location }}, float64({{ if .IsNullable }}*{{ end }}{{.ValueExpression }}), {{.MultipleOf }}); err != nil {
{{- end }}
{{- end }}
{{- end }}
return err
}

View file

@ -0,0 +1,29 @@
{{if .MinLength}}
if err := validate.MinLength({{ if .Path }}{{ .Path }}{{else}}""{{end}}, {{ printf "%q" .Location }}, {{ .ToString }}, {{.MinLength}}); err != nil {
return err
}
{{end}}
{{if .MaxLength}}
if err := validate.MaxLength({{ if .Path }}{{ .Path }}{{else}}""{{end}}, {{ printf "%q" .Location }}, {{ .ToString }}, {{.MaxLength}}); err != nil {
return err
}
{{end}}
{{if .Pattern}}
if err := validate.Pattern({{ if .Path }}{{ .Path }}{{else}}""{{end}}, {{ printf "%q" .Location }}, {{ .ToString }}, `{{escapeBackticks .Pattern}}`); err != nil {
return err
}
{{end}}
{{if .Minimum}}
{{ template "validationMinimum" . }}
{{end}}
{{if .Maximum}}
{{ template "validationMaximum" . }}
{{end}}
{{if .MultipleOf}}
{{ template "validationMultipleOf" . }}
{{end}}
{{if .Enum}}
if err := validate.EnumCase({{ if .Path }}{{ .Path }}{{else}}""{{end}}, {{ printf "%q" .Location }}, {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) .IsNullable }}*{{ end }}{{.ValueExpression}}{{ if .IsCustomFormatter }}.String(){{ end }}, {{ printf "%#v" .Enum}}, {{ if .IsEnumCI }}false{{ else }}true{{ end }}); err != nil {
return err
}
{{end}}

View file

@ -0,0 +1,62 @@
{{ define "propertyValidationDocString" }}
{{- if .Required }}
// Required: true
{{- end }}
{{- if .ReadOnly }}
// Read Only: true
{{- end }}
{{- if .Maximum }}
// Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}
{{- end }}
{{- if .Minimum }}
// Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}
{{- end }}
{{- if .MultipleOf }}
// Multiple Of: {{ .MultipleOf }}
{{- end }}
{{- if .MaxLength }}
// Max Length: {{ .MaxLength }}
{{- end }}
{{- if .MinLength }}
// Min Length: {{ .MinLength }}
{{- end }}
{{- if .Pattern }}
// Pattern: {{ .Pattern }}
{{- end }}
{{- if .MaxItems }}
// Max Items: {{ .MaxItems }}
{{- end }}
{{- if .MinItems }}
// Min Items: {{ .MinItems }}
{{- end }}
{{- if .MinProperties }}
// Min Properties: {{ .MinProperties }}
{{- end }}
{{- if .MaxProperties }}
// Max Properties: {{ .MaxProperties }}
{{- end }}
{{- if .UniqueItems }}
// Unique: true
{{- end }}
{{- if .IsCustomFormatter }}
// Format: {{ .SwaggerFormat }}
{{- end }}
{{- if .Enum }}
// Enum: {{ printf "%v" .Enum }}
{{- end }}
{{- end}}

File diff suppressed because it is too large Load diff