Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 merge #1907

Merged
merged 88 commits into from
Oct 19, 2024
Merged
Changes from 1 commit
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
3c5e486
Update README_zh-CN.md (#1545)
tzxdtc Apr 11, 2023
01d20cc
Add option to set template delimiters (#1499)
leosunmo Apr 17, 2023
c14cc8e
fix bug: enums of explicit type conversion (#1556)
sdghchj Apr 17, 2023
21d34e2
add retract to fix proxy cache caused by accidentally pushed tags (#1…
Nerzal Apr 19, 2023
5774b7b
docs: doc to pt Add option to set template delims. (#1563)
Paulo-Lopes-Estevao May 3, 2023
e749ad5
fix: lint error for generated docs.go (#1583)
lowang-bh May 10, 2023
ea35767
fix bug: enums of underscored number (#1581)
sdghchj May 12, 2023
e73a0d0
fix using tab (\t) as separator for custom type names (#1594)
nitram509 Jun 7, 2023
b2f325f
chore(deps): bump github.com/gin-gonic/gin (#1598)
dependabot[bot] Jun 25, 2023
8e5b314
chore(deps): bump github.com/gin-gonic/gin in /example/celler (#1599)
dependabot[bot] Jun 25, 2023
c8372f6
chore(deps): bump github.com/gin-gonic/gin in /example/go-module-supp…
dependabot[bot] Jun 25, 2023
0cee1c5
fix required params parsing for routes with multiple paths and multip…
Phenix66 Jul 7, 2023
fe971d2
parser: if all tags negate return true on no hits (#1624)
rsmarples Jul 17, 2023
4536bf2
fix: enums in body got parse incorrectly (#1625)
hohobilly Jul 18, 2023
575963e
parse binary literal const (#1593)
sdghchj Jul 18, 2023
1bf0078
feat: global security (#1620)
nameoffnv Jul 18, 2023
7534a13
add cli flag --pdl to determine whether parse operations in dependenc…
sdghchj Jul 18, 2023
d0f9dc5
feat: add --packagePrefix=P for only parse packages matched by prefix…
SilverRainZ Jul 19, 2023
27b27bd
enchancement: report which property is triggering a parsing error (#1…
sakishrist Jul 20, 2023
f05ccdc
add byte check before and after file is formatted (#1637)
hohobilly Aug 9, 2023
9f128b4
feat: preserve file permission when write formatted files (#1636)
hohobilly Aug 9, 2023
8ebf32f
docs(readme): fix param brace (#1647)
danielmoncada Aug 15, 2023
23c9b5c
chore(deps): bump gopkg.in/yaml.v3 (#1663)
dependabot[bot] Aug 30, 2023
e9d0aa5
yaml.v3 security patch (#1664)
ubogdan Aug 30, 2023
1460377
test: remove redundant `filepath.Clean` call (#1675)
wholesome-ghoul Sep 25, 2023
2e5d352
chore(deps): bump golang.org/x/net from 0.8.0 to 0.17.0 (#1686)
dependabot[bot] Oct 12, 2023
d23a84a
chore(deps): bump golang.org/x/net in /example/markdown (#1685)
dependabot[bot] Oct 12, 2023
2da9651
When the return value defined by the @Success tag is equal to a null …
Shimizu1111 Oct 12, 2023
d7e961a
chore(deps): bump golang.org/x/net in /example/go-module-support (#1682)
dependabot[bot] Oct 12, 2023
c00103a
chore(deps): bump golang.org/x/net in /example/object-map-example (#1…
dependabot[bot] Oct 12, 2023
2b5852a
chore(deps): bump golang.org/x/net in /example/celler (#1683)
dependabot[bot] Oct 12, 2023
bab7aac
docs: add PT and EN examples for Go generic types (#1697)
rpedrodasilva10 Nov 7, 2023
19b9c00
Update README.md (#1698)
saurabhchatterjee23 Nov 7, 2023
c7c63fc
update gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 to 3.0.0 (…
chncaption Nov 7, 2023
0ade78c
improve docker container usage (#1704)
ngehrsitz Nov 16, 2023
6cdaaf5
fix issue #1662: find definitions from external packages first (#1666)
sdghchj Nov 24, 2023
744a58e
Drop support for go v1.17.x (#1723)
sdghchj Dec 18, 2023
7603121
Add flag state #1628 (#1629)
ivolkoff Dec 18, 2023
9fdba3e
fix deps (#1724)
sdghchj Dec 19, 2023
43fec8e
chore(deps): bump golang.org/x/crypto in /example/celler (#1727)
dependabot[bot] Dec 19, 2023
fb5890c
chore(deps): bump golang.org/x/crypto in /example/go-module-support (…
dependabot[bot] Dec 19, 2023
33da992
chore(deps): bump golang.org/x/crypto in /example/object-map-example …
dependabot[bot] Dec 20, 2023
0fb6820
deprecate some parts of routers in an operation (#1735)
sdghchj Jan 11, 2024
76695ca
bug: array form filed name should not contains bracket which led to i…
Jinof Jan 16, 2024
d4218f2
Struct fields supported for header and path param types (#1740)
tanenbaum Jan 22, 2024
ae7e404
fix #1742 (#1744)
sdghchj Jan 22, 2024
7147984
Feat: Support generic with map params (#1746)
sdghchj Jan 29, 2024
98ed434
Update version.go (#1751)
sdghchj Feb 1, 2024
56fde5c
Update operation.go (#1753)
mathieu-chauvet Feb 6, 2024
928264c
fix: remove dropped tags from general infos (#1764)
sdghchj Feb 20, 2024
87e7d9c
Update docker go build version to 1.21 (#1758)
ngehrsitz Feb 20, 2024
91624ad
add support for "title" tag (#1762)
matteobassan Feb 20, 2024
90aa46f
chore: fix some typos in comments (#1788)
camcui Apr 20, 2024
8a47dcb
bump go version (#1797)
ubogdan Apr 20, 2024
0834357
chore(deps): bump golang.org/x/net from 0.17.0 to 0.23.0 (#1793)
dependabot[bot] Apr 20, 2024
d5af957
chore(deps): bump golang.org/x/net in /example/markdown (#1792)
dependabot[bot] Apr 20, 2024
1bb1445
chore(deps): bump golang.org/x/net in /example/celler (#1794)
dependabot[bot] Apr 20, 2024
6aa6613
chore(deps): bump golang.org/x/net in /example/go-module-support (#1795)
dependabot[bot] Apr 20, 2024
0368d7d
chore(deps): bump golang.org/x/net in /example/object-map-example (#1…
dependabot[bot] Apr 20, 2024
4c2f8dd
Handle case of empty GOROOT (#1798)
evan-goode Apr 21, 2024
85254b4
Added multiline support for @description attribute for securityDefini…
golandprog Apr 22, 2024
15dae35
Feat: multi-arch docker image (#1756)
tnaroska Apr 23, 2024
b8662de
chore(deps): bump google.golang.org/protobuf (#1773)
dependabot[bot] Apr 23, 2024
4a11e23
chore(deps): bump google.golang.org/protobuf (#1774)
dependabot[bot] Apr 23, 2024
937c239
chore(deps): bump google.golang.org/protobuf in /example/celler (#1775)
dependabot[bot] Apr 23, 2024
fd2fa83
fix issue: #1780: filter $GOROOT path (#1827)
bobsongplus Jun 21, 2024
e55c557
feat: read from stdin, write to stdout (#1831) (#1832)
bfbonatto Jun 21, 2024
7204462
Added suport for parsing comments inside of function bodies (#1824)
j-d-ha Jun 21, 2024
f32d4d3
adds support for complex types with function scope (#1813)
KristofferFJ Jun 28, 2024
807dd1f
[Issue 1812] fix misalignment in expected.json and api.go messing wit…
KristofferFJ Jul 1, 2024
697572a
Fixes Issue 1829 (#1830)
Kafkalasch Jul 1, 2024
ff50cd6
Fix global overrides for any/interface ref types (#1835)
ezequiel Jul 3, 2024
c7f1cd8
adds support for pointer function scoped fields (#1841)
KristofferFJ Jul 11, 2024
10030b0
fix parse nested structs and aliases (#1866)
zdon0 Aug 20, 2024
83fe3ca
Fix generics used with function scoped types (#1883)
berk-karaal Sep 5, 2024
1d730c5
Fix param comment escaping issue (#1890)
yukiomoto Sep 20, 2024
a3c6d12
support markdown description for declaration (#1893)
nicoxb Sep 25, 2024
9069105
update README (#1856)
sdghchj Oct 17, 2024
4fd8a36
Update docs for request and response headers (#1825)
eksrha Oct 17, 2024
a74d34c
fix:parse all field names declared in a row (#1872)
sdghchj Oct 17, 2024
28de14c
Flags to parse internal and dependency package (#1894)
bytesByHarsh Oct 17, 2024
7159b0f
fix: failing assert in enums test on 32bit (#1634)
leso-kn Oct 17, 2024
d323b48
Feat: Add support for parenthesis in router patterns (#1859)
alifemove Oct 17, 2024
103ac42
chore: Update ci.yml (#1902)
ubogdan Oct 17, 2024
0b9e347
new release (#1901)
ubogdan Oct 18, 2024
5b930d4
Merge branch 'v2' into v2-merge
ubogdan Oct 18, 2024
36c14a4
fix some issues
ubogdan Oct 18, 2024
6106783
fix unit tests
ubogdan Oct 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add flag state #1628 (#1629)
* add state flag
ivolkoff authored Dec 18, 2023
commit 76031216231b1c6a0869f15c944394a3013767bb
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -115,6 +115,7 @@ OPTIONS:
--tags value, -t value A comma-separated list of tags to filter the APIs for which the documentation is generated.Special case if the tag is prefixed with the '!' character then the APIs with that tag will be excluded
--templateDelims value, --td value Provide custom delimeters for Go template generation. The format is leftDelim,rightDelim. For example: "[[,]]"
--collectionFormat value, --cf value Set default collection format (default: "csv")
--state value Initial state for the state machine (default: ""), @HostState in root file, @State in other files
--help, -h show help (default: false)
```

7 changes: 7 additions & 0 deletions cmd/swag/main.go
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ const (
packageName = "packageName"
collectionFormatFlag = "collectionFormat"
packagePrefixFlag = "packagePrefix"
stateFlag = "state"
)

var initFlags = []cli.Flag{
@@ -173,6 +174,11 @@ var initFlags = []cli.Flag{
Value: "",
Usage: "Parse only packages whose import path match the given prefix, comma separated",
},
&cli.StringFlag{
Name: stateFlag,
Value: "",
Usage: "Set host state for swagger.json",
},
}

func initAction(ctx *cli.Context) error {
@@ -242,6 +248,7 @@ func initAction(ctx *cli.Context) error {
Debugger: logger,
CollectionFormat: collectionFormat,
PackagePrefix: ctx.String(packagePrefixFlag),
State: ctx.String(stateFlag),
})
}

35 changes: 30 additions & 5 deletions gen/gen.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@ import (

"github.com/go-openapi/spec"
"github.com/swaggo/swag"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"sigs.k8s.io/yaml"
)

@@ -141,6 +143,9 @@ type Config struct {

// Parse only packages whose import path match the given prefix, comma separated
PackagePrefix string

// State set host state
State string
}

// Build builds swagger json file for given searchDir and mainAPIFile. Returns json.
@@ -207,6 +212,7 @@ func (g *Gen) Build(config *Config) error {
p.ParseVendor = config.ParseVendor
p.ParseInternal = config.ParseInternal
p.RequiredByDefault = config.RequiredByDefault
p.HostState = config.State

if err := p.ParseAPIMultiSearchDir(searchDirs, config.MainAPIFile, config.ParseDepth); err != nil {
return err
@@ -235,6 +241,10 @@ func (g *Gen) Build(config *Config) error {
func (g *Gen) writeDocSwagger(config *Config, swagger *spec.Swagger) error {
var filename = "docs.go"

if config.State != "" {
filename = config.State + "_" + filename
}

if config.InstanceName != swag.Name {
filename = config.InstanceName + "_" + filename
}
@@ -274,6 +284,10 @@ func (g *Gen) writeDocSwagger(config *Config, swagger *spec.Swagger) error {
func (g *Gen) writeJSONSwagger(config *Config, swagger *spec.Swagger) error {
var filename = "swagger.json"

if config.State != "" {
filename = config.State + "_" + filename
}

if config.InstanceName != swag.Name {
filename = config.InstanceName + "_" + filename
}
@@ -298,6 +312,10 @@ func (g *Gen) writeJSONSwagger(config *Config, swagger *spec.Swagger) error {
func (g *Gen) writeYAMLSwagger(config *Config, swagger *spec.Swagger) error {
var filename = "swagger.yaml"

if config.State != "" {
filename = config.State + "_" + filename
}

if config.InstanceName != swag.Name {
filename = config.InstanceName + "_" + filename
}
@@ -441,6 +459,11 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa
return err
}

state := ""
if len(config.State) > 0 {
state = cases.Title(language.English).String(strings.ToLower(config.State))
}

buffer := &bytes.Buffer{}

err = generator.Execute(buffer, struct {
@@ -452,6 +475,7 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa
Title string
Description string
Version string
State string
InstanceName string
Schemes []string
GeneratedTime bool
@@ -468,6 +492,7 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa
Title: swagger.Info.Title,
Description: swagger.Info.Description,
Version: swagger.Info.Version,
State: state,
InstanceName: config.InstanceName,
LeftTemplateDelim: config.LeftTemplateDelim,
RightTemplateDelim: config.RightTemplateDelim,
@@ -489,23 +514,23 @@ package {{.PackageName}}

import "github.com/swaggo/swag"

const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = ` + "`{{ printDoc .Doc}}`" + `
const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}{{ .State }} = ` + "`{{ printDoc .Doc}}`" + `

// SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} holds exported Swagger Info so clients can modify it
var SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = &swag.Spec{
// Swagger{{ .State }}Info{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} holds exported Swagger Info so clients can modify it
var Swagger{{ .State }}Info{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = &swag.Spec{
Version: {{ printf "%q" .Version}},
Host: {{ printf "%q" .Host}},
BasePath: {{ printf "%q" .BasePath}},
Schemes: []string{ {{ range $index, $schema := .Schemes}}{{if gt $index 0}},{{end}}{{printf "%q" $schema}}{{end}} },
Title: {{ printf "%q" .Title}},
Description: {{ printf "%q" .Description}},
InfoInstanceName: {{ printf "%q" .InstanceName }},
SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }},
SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}{{ .State }},
LeftDelim: {{ printf "%q" .LeftTemplateDelim}},
RightDelim: {{ printf "%q" .RightTemplateDelim}},
}

func init() {
swag.Register(SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }})
swag.Register(Swagger{{ .State }}Info{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), Swagger{{ .State }}Info{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }})
}
`
78 changes: 78 additions & 0 deletions gen/gen_test.go
Original file line number Diff line number Diff line change
@@ -895,3 +895,81 @@ func TestGen_ErrorAndInterface(t *testing.T) {

assert.JSONEq(t, string(expectedJSON), string(jsonOutput))
}

func TestGen_StateAdmin(t *testing.T) {
config := &Config{
SearchDir: "../testdata/state",
MainAPIFile: "./main.go",
OutputDir: "../testdata/state/docs",
OutputTypes: outputTypes,
PropNamingStrategy: "",
State: "admin",
}

assert.NoError(t, New().Build(config))

expectedFiles := []string{
filepath.Join(config.OutputDir, "admin_docs.go"),
filepath.Join(config.OutputDir, "admin_swagger.json"),
filepath.Join(config.OutputDir, "admin_swagger.yaml"),
}
t.Cleanup(func() {
for _, expectedFile := range expectedFiles {
_ = os.Remove(expectedFile)
}
})

// check files
for _, expectedFile := range expectedFiles {
if _, err := os.Stat(expectedFile); os.IsNotExist(err) {
require.NoError(t, err)
}
}

// check content
jsonOutput, err := os.ReadFile(filepath.Join(config.OutputDir, "admin_swagger.json"))
require.NoError(t, err)
expectedJSON, err := os.ReadFile(filepath.Join(config.SearchDir, "admin_expected.json"))
require.NoError(t, err)

assert.JSONEq(t, string(expectedJSON), string(jsonOutput))
}

func TestGen_StateUser(t *testing.T) {
config := &Config{
SearchDir: "../testdata/state",
MainAPIFile: "./main.go",
OutputDir: "../testdata/state/docs",
OutputTypes: outputTypes,
PropNamingStrategy: "",
State: "user",
}

assert.NoError(t, New().Build(config))

expectedFiles := []string{
filepath.Join(config.OutputDir, "user_docs.go"),
filepath.Join(config.OutputDir, "user_swagger.json"),
filepath.Join(config.OutputDir, "user_swagger.yaml"),
}
t.Cleanup(func() {
for _, expectedFile := range expectedFiles {
_ = os.Remove(expectedFile)
}
})

// check files
for _, expectedFile := range expectedFiles {
if _, err := os.Stat(expectedFile); os.IsNotExist(err) {
require.NoError(t, err)
}
}

// check content
jsonOutput, err := os.ReadFile(filepath.Join(config.OutputDir, "user_swagger.json"))
require.NoError(t, err)
expectedJSON, err := os.ReadFile(filepath.Join(config.SearchDir, "user_expected.json"))
require.NoError(t, err)

assert.JSONEq(t, string(expectedJSON), string(jsonOutput))
}
8 changes: 8 additions & 0 deletions operation.go
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
codeExampleFilesDir string
spec.Operation
RouterProperties []RouteProperties
State string
}

var mimeTypeAliases = map[string]string{
@@ -118,6 +119,8 @@
lineRemainder = fields[1]
}
switch lowerAttribute {
case stateAttr:
operation.ParseStateComment(lineRemainder)
case descriptionAttr:
operation.ParseDescriptionComment(lineRemainder)
case descriptionMarkdownAttr:
@@ -183,6 +186,11 @@
return operation.ParseMetadata(attribute, strings.ToLower(attribute), lineRemainder)
}

// ParseDescriptionComment godoc.

Check failure on line 189 in operation.go

GitHub Actions / test (1.18.x, ubuntu-latest)

comment on exported method Operation.ParseStateComment should be of the form "ParseStateComment ..."

Check failure on line 189 in operation.go

GitHub Actions / test (1.19.x, ubuntu-latest)

comment on exported method Operation.ParseStateComment should be of the form "ParseStateComment ..."

Check failure on line 189 in operation.go

GitHub Actions / test (1.20.x, ubuntu-latest)

comment on exported method Operation.ParseStateComment should be of the form "ParseStateComment ..."

Check failure on line 189 in operation.go

GitHub Actions / test (1.21.x, ubuntu-latest)

comment on exported method Operation.ParseStateComment should be of the form "ParseStateComment ..."
func (operation *Operation) ParseStateComment(lineRemainder string) {
operation.State = lineRemainder
}

// ParseDescriptionComment godoc.
func (operation *Operation) ParseDescriptionComment(lineRemainder string) {
if operation.Description == "" {
16 changes: 16 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
@@ -66,6 +66,7 @@ const (
extDocsURLAttr = "@externaldocs.url"
xCodeSamplesAttr = "@x-codesamples"
scopeAttrPrefix = "@scope."
stateAttr = "@state"
)

// ParseFlag determine what to parse
@@ -174,6 +175,9 @@ type Parser struct {

// tags to filter the APIs after
tags map[string]struct{}

// HostState is the state of the host
HostState string
}

// FieldParserFactory create FieldParser.
@@ -541,6 +545,14 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error {

case "@host":
parser.swagger.Host = value
case "@hoststate":
fields = FieldsByAnySpace(commentLine, 3)
if len(fields) != 3 {
return fmt.Errorf("%s needs 3 arguments", attribute)
}
if parser.HostState == fields[1] {
parser.swagger.Host = fields[2]
}
case "@basepath":
parser.swagger.BasePath = value

@@ -977,6 +989,7 @@ func matchExtension(extensionToMatch string, comments []*ast.Comment) (match boo

// ParseRouterAPIInfo parses router api info for given astFile.
func (parser *Parser) ParseRouterAPIInfo(fileInfo *AstFileInfo) error {
DeclsLoop:
for _, astDescription := range fileInfo.File.Decls {
if (fileInfo.ParseFlag & ParseOperations) == ParseNone {
continue
@@ -992,6 +1005,9 @@ func (parser *Parser) ParseRouterAPIInfo(fileInfo *AstFileInfo) error {
if err != nil {
return fmt.Errorf("ParseComment error in file %s :%+v", fileInfo.Path, err)
}
if operation.State != "" && operation.State != parser.HostState {
continue DeclsLoop
}
}
err := processRouterOperation(parser, operation)
if err != nil {
396 changes: 396 additions & 0 deletions testdata/state/admin_expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,396 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server.",
"title": "Swagger Example API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"host": "petstore-admin.swagger.io",
"basePath": "/v3",
"paths": {
"/admin/file/upload": {
"post": {
"description": "Upload file",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "Upload file",
"operationId": "admin.file.upload",
"parameters": [
{
"type": "file",
"description": "this is a test file",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
},
"/admin/testapi/get-string-by-int/{some_id}": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Add a new pet to the store",
"operationId": "admin.get-string-by-int",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Some ID",
"name": "some_id",
"in": "path",
"required": true
},
{
"description": "Some ID",
"name": "some_id",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/web.Pet"
}
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
},
"/admin/testapi/get-struct-array-by-string/{some_id}": {
"get": {
"security": [
{
"ApiKeyAuth": []
},
{
"BasicAuth": []
},
{
"OAuth2Application": [
"write"
]
},
{
"OAuth2Implicit": [
"read",
"admin"
]
},
{
"OAuth2AccessCode": [
"read"
]
},
{
"OAuth2Password": [
"admin"
]
}
],
"description": "get struct array by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"operationId": "admin.get-struct-array-by-string",
"parameters": [
{
"type": "string",
"description": "Some ID",
"name": "some_id",
"in": "path",
"required": true
},
{
"enum": [
1,
2,
3
],
"type": "integer",
"description": "Category",
"name": "category",
"in": "query",
"required": true
},
{
"minimum": 0,
"type": "integer",
"default": 0,
"description": "Offset",
"name": "offset",
"in": "query",
"required": true
},
{
"maximum": 50,
"type": "integer",
"default": 10,
"description": "Limit",
"name": "limit",
"in": "query",
"required": true
},
{
"maxLength": 50,
"minLength": 1,
"type": "string",
"default": "\"\"",
"description": "q",
"name": "q",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
}
},
"definitions": {
"web.APIError": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"errorCode": {
"type": "integer"
},
"errorMessage": {
"type": "string"
}
}
},
"web.Pet": {
"type": "object",
"properties": {
"category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "category_name"
},
"photoURLs": {
"type": "array",
"items": {
"type": "string",
"format": "url"
},
"example": [
"http://test/image/1.jpg",
"http://test/image/2.jpg"
]
},
"smallCategory": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "detail_category_name"
},
"photoURLs": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"http://test/image/1.jpg",
"http://test/image/2.jpg"
]
}
}
}
}
},
"data": {},
"decimal": {
"type": "number"
},
"id": {
"type": "integer",
"format": "int64",
"example": 1
},
"isAlive": {
"type": "boolean",
"example": true
},
"name": {
"type": "string",
"example": "poti"
},
"pets": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Pet2"
}
},
"pets2": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Pet2"
}
},
"photoURLs": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"http://test/image/1.jpg",
"http://test/image/2.jpg"
]
},
"price": {
"type": "number",
"multipleOf": 0.01,
"example": 3.25
},
"status": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Tag"
}
},
"uuid": {
"type": "string"
}
}
},
"web.Pet2": {
"type": "object",
"properties": {
"deletedAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"middleName": {
"type": "string"
}
}
},
"web.RevValue": {
"type": "object",
"properties": {
"data": {
"type": "integer"
},
"err": {
"type": "integer"
},
"status": {
"type": "boolean"
}
}
},
"web.Tag": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"pets": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Pet"
}
}
}
}
}
}
76 changes: 76 additions & 0 deletions testdata/state/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package api

import "net/http"

// @State admin
// @Summary Add a new pet to the store
// @Description get string by ID
// @ID admin.get-string-by-int
// @Accept json
// @Produce json
// @Param some_id path int true "Some ID" Format(int64)
// @Param some_id body web.Pet true "Some ID"
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Router /admin/testapi/get-string-by-int/{some_id} [get]
func GetStringByInt(w http.ResponseWriter, r *http.Request) {
//write your code
}

// @State admin
// @Description get struct array by ID
// @ID admin.get-struct-array-by-string
// @Accept json
// @Produce json
// @Param some_id path string true "Some ID"
// @Param category query int true "Category" Enums(1, 2, 3)
// @Param offset query int true "Offset" Minimum(0) default(0)
// @Param limit query int true "Limit" Maximum(50) default(10)
// @Param q query string true "q" Minlength(1) Maxlength(50) default("")
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Security ApiKeyAuth
// @Security BasicAuth
// @Security OAuth2Application[write]
// @Security OAuth2Implicit[read, admin]
// @Security OAuth2AccessCode[read]
// @Security OAuth2Password[admin]
// @Router /admin/testapi/get-struct-array-by-string/{some_id} [get]
func GetStructArrayByString(w http.ResponseWriter, r *http.Request) {
//write your code
}

// @State admin
// @Summary Upload file
// @Description Upload file
// @ID admin.file.upload
// @Accept multipart/form-data
// @Produce json
// @Param file formData file true "this is a test file"
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Router /admin/file/upload [post]
func Upload(w http.ResponseWriter, r *http.Request) {
//write your code
}

// @State admin
// @Summary use Anonymous field
// @Success 200 {object} web.RevValue "ok"
func AnonymousField() {

}

// @State admin
// @Summary use pet2
// @Success 200 {object} web.Pet2 "ok"
func Pet2() {

}

type Pet3 struct {
ID int `json:"id"`
}
76 changes: 76 additions & 0 deletions testdata/state/api/api_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package api

import "net/http"

// @State user
// @Summary Add a new pet to the store
// @Description get string by ID
// @ID get-string-by-int
// @Accept json
// @Produce json
// @Param some_id path int true "Some ID" Format(int64)
// @Param some_id body web.Pet true "Some ID"
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Router /testapi/get-string-by-int/{some_id} [get]
func GetStringByIntUser(w http.ResponseWriter, r *http.Request) {
//write your code
}

// @State user
// @Description get struct array by ID
// @ID get-struct-array-by-string
// @Accept json
// @Produce json
// @Param some_id path string true "Some ID"
// @Param category query int true "Category" Enums(1, 2, 3)
// @Param offset query int true "Offset" Minimum(0) default(0)
// @Param limit query int true "Limit" Maximum(50) default(10)
// @Param q query string true "q" Minlength(1) Maxlength(50) default("")
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Security ApiKeyAuth
// @Security BasicAuth
// @Security OAuth2Application[write]
// @Security OAuth2Implicit[read, admin]
// @Security OAuth2AccessCode[read]
// @Security OAuth2Password[admin]
// @Router /testapi/get-struct-array-by-string/{some_id} [get]
func GetStructArrayByStringUser(w http.ResponseWriter, r *http.Request) {
//write your code
}

// @State user
// @Summary Upload file
// @Description Upload file
// @ID file.upload
// @Accept multipart/form-data
// @Produce json
// @Param file formData file true "this is a test file"
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Router /file/upload [post]
func UploadUser(w http.ResponseWriter, r *http.Request) {
//write your code
}

// @State user
// @Summary use Anonymous field
// @Success 200 {object} web.RevValue "ok"
func AnonymousFieldUser() {

}

// @State user
// @Summary use pet2
// @Success 200 {object} web.Pet2 "ok"
func Pet2User() {

}

type Pet3User struct {
ID int `json:"id"`
}
38 changes: 38 additions & 0 deletions testdata/state/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

import (
"net/http"

"github.com/swaggo/swag/testdata/state/api"
)

// @title Swagger Example API
// @version 1.0
// @description This is a sample server Petstore server.
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @hostState admin petstore-admin.swagger.io
// @hostState user petstore-user.swagger.io
// @BasePath /v3
func main() {
state := "admin" // "admin" or "user"
switch state {
case "admin":
http.HandleFunc("/admin/testapi/get-string-by-int/", api.GetStringByInt)
http.HandleFunc("/admin/testapi/get-struct-array-by-string/", api.GetStructArrayByString)
http.HandleFunc("/admin/testapi/upload", api.Upload)
http.ListenAndServe(":8080", nil)
case "user":
http.HandleFunc("/testapi/get-string-by-int/", api.GetStringByIntUser)
http.HandleFunc("/testapi/get-struct-array-by-string/", api.GetStructArrayByStringUser)
http.HandleFunc("/testapi/upload", api.UploadUser)
http.ListenAndServe(":8080", nil)
}
}
396 changes: 396 additions & 0 deletions testdata/state/user_expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,396 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server.",
"title": "Swagger Example API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"host": "petstore-user.swagger.io",
"basePath": "/v3",
"paths": {
"/file/upload": {
"post": {
"description": "Upload file",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "Upload file",
"operationId": "file.upload",
"parameters": [
{
"type": "file",
"description": "this is a test file",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
},
"/testapi/get-string-by-int/{some_id}": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Add a new pet to the store",
"operationId": "get-string-by-int",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Some ID",
"name": "some_id",
"in": "path",
"required": true
},
{
"description": "Some ID",
"name": "some_id",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/web.Pet"
}
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
},
"/testapi/get-struct-array-by-string/{some_id}": {
"get": {
"security": [
{
"ApiKeyAuth": []
},
{
"BasicAuth": []
},
{
"OAuth2Application": [
"write"
]
},
{
"OAuth2Implicit": [
"read",
"admin"
]
},
{
"OAuth2AccessCode": [
"read"
]
},
{
"OAuth2Password": [
"admin"
]
}
],
"description": "get struct array by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"operationId": "get-struct-array-by-string",
"parameters": [
{
"type": "string",
"description": "Some ID",
"name": "some_id",
"in": "path",
"required": true
},
{
"enum": [
1,
2,
3
],
"type": "integer",
"description": "Category",
"name": "category",
"in": "query",
"required": true
},
{
"minimum": 0,
"type": "integer",
"default": 0,
"description": "Offset",
"name": "offset",
"in": "query",
"required": true
},
{
"maximum": 50,
"type": "integer",
"default": 10,
"description": "Limit",
"name": "limit",
"in": "query",
"required": true
},
{
"maxLength": 50,
"minLength": 1,
"type": "string",
"default": "\"\"",
"description": "q",
"name": "q",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
}
},
"definitions": {
"web.APIError": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"errorCode": {
"type": "integer"
},
"errorMessage": {
"type": "string"
}
}
},
"web.Pet": {
"type": "object",
"properties": {
"category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "category_name"
},
"photoURLs": {
"type": "array",
"items": {
"type": "string",
"format": "url"
},
"example": [
"http://test/image/1.jpg",
"http://test/image/2.jpg"
]
},
"smallCategory": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "detail_category_name"
},
"photoURLs": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"http://test/image/1.jpg",
"http://test/image/2.jpg"
]
}
}
}
}
},
"data": {},
"decimal": {
"type": "number"
},
"id": {
"type": "integer",
"format": "int64",
"example": 1
},
"isAlive": {
"type": "boolean",
"example": true
},
"name": {
"type": "string",
"example": "poti"
},
"pets": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Pet2"
}
},
"pets2": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Pet2"
}
},
"photoURLs": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"http://test/image/1.jpg",
"http://test/image/2.jpg"
]
},
"price": {
"type": "number",
"multipleOf": 0.01,
"example": 3.25
},
"status": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Tag"
}
},
"uuid": {
"type": "string"
}
}
},
"web.Pet2": {
"type": "object",
"properties": {
"deletedAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"middleName": {
"type": "string"
}
}
},
"web.RevValue": {
"type": "object",
"properties": {
"data": {
"type": "integer"
},
"err": {
"type": "integer"
},
"status": {
"type": "boolean"
}
}
},
"web.Tag": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"pets": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Pet"
}
}
}
}
}
}
64 changes: 64 additions & 0 deletions testdata/state/web/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package web

import (
"time"

uuid "github.com/gofrs/uuid"
"github.com/shopspring/decimal"
)

type Pet struct {
ID int `example:"1" format:"int64"`
Category struct {
ID int `example:"1"`
Name string `example:"category_name"`
PhotoURLs []string `example:"http://test/image/1.jpg,http://test/image/2.jpg" format:"url"`
SmallCategory struct {
ID int `example:"1"`
Name string `example:"detail_category_name"`
PhotoURLs []string `example:"http://test/image/1.jpg,http://test/image/2.jpg"`
}
}
Name string `example:"poti"`
PhotoURLs []string `example:"http://test/image/1.jpg,http://test/image/2.jpg"`
Tags []Tag
Pets *[]Pet2
Pets2 []*Pet2
Status string
Price float32 `example:"3.25" multipleOf:"0.01"`
IsAlive bool `example:"true"`
Data interface{}
Hidden string `json:"-"`
UUID uuid.UUID
Decimal decimal.Decimal
Function func()
}

type Tag struct {
ID int `format:"int64"`
Name string
Pets []Pet
}

type Pet2 struct {
ID int
MiddleName *string
DeletedAt *time.Time
}

type APIError struct {
ErrorCode int
ErrorMessage string
CreatedAt time.Time
}

type RevValueBase struct {
Status bool

Err int32
}
type RevValue struct {
RevValueBase

Data int
}