From b852cf935f514e03deb46e9e711bbe0c18787c99 Mon Sep 17 00:00:00 2001 From: luowei Date: Fri, 30 Aug 2024 20:03:10 +0800 Subject: [PATCH] use lower swagger version to avoid swagger error --- build/swag/main.go | 4 +- go.mod | 11 +- go.sum | 20 +- sqle/docs/docs.go | 4 +- sqle/docs/swagger.json | 4 +- sqle/docs/swagger.yaml | 4 +- vendor/github.com/ghodss/yaml/.travis.yml | 8 + .../ghodss}/yaml/LICENSE | 0 .../ghodss}/yaml/README.md | 16 +- .../ghodss}/yaml/fields.go | 1 - .../ghodss}/yaml/yaml.go | 98 +- .../ghodss}/yaml/yaml_go110.go | 0 vendor/github.com/swaggo/swag/.gitignore | 8 +- vendor/github.com/swaggo/swag/.goreleaser.yml | 25 +- vendor/github.com/swaggo/swag/.travis.yml | 18 + vendor/github.com/swaggo/swag/Dockerfile | 7 +- vendor/github.com/swaggo/swag/Makefile | 19 +- vendor/github.com/swaggo/swag/README.md | 574 ++-- vendor/github.com/swaggo/swag/README_pt.md | 968 ------- vendor/github.com/swaggo/swag/README_zh-CN.md | 296 +- vendor/github.com/swaggo/swag/const.go | 567 ---- vendor/github.com/swaggo/swag/debug.go | 30 + vendor/github.com/swaggo/swag/enums.go | 13 - vendor/github.com/swaggo/swag/field_parser.go | 680 ----- vendor/github.com/swaggo/swag/formatter.go | 182 -- vendor/github.com/swaggo/swag/gen/gen.go | 454 +--- vendor/github.com/swaggo/swag/generics.go | 432 --- vendor/github.com/swaggo/swag/golist.go | 78 - vendor/github.com/swaggo/swag/operation.go | 1264 ++++----- vendor/github.com/swaggo/swag/package.go | 187 -- vendor/github.com/swaggo/swag/packages.go | 574 ---- vendor/github.com/swaggo/swag/parser.go | 2413 ++++++++--------- vendor/github.com/swaggo/swag/property.go | 139 + vendor/github.com/swaggo/swag/schema.go | 262 +- vendor/github.com/swaggo/swag/spec.go | 64 - vendor/github.com/swaggo/swag/swagger.go | 49 +- vendor/github.com/swaggo/swag/types.go | 105 - vendor/github.com/swaggo/swag/utils.go | 55 - vendor/github.com/swaggo/swag/version.go | 4 +- vendor/modules.txt | 11 +- vendor/sigs.k8s.io/yaml/.travis.yml | 12 - vendor/sigs.k8s.io/yaml/CONTRIBUTING.md | 31 - vendor/sigs.k8s.io/yaml/OWNERS | 27 - vendor/sigs.k8s.io/yaml/RELEASE.md | 9 - vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS | 17 - vendor/sigs.k8s.io/yaml/code-of-conduct.md | 3 - 46 files changed, 2299 insertions(+), 7448 deletions(-) create mode 100644 vendor/github.com/ghodss/yaml/.travis.yml rename vendor/{sigs.k8s.io => github.com/ghodss}/yaml/LICENSE (100%) rename vendor/{sigs.k8s.io => github.com/ghodss}/yaml/README.md (86%) rename vendor/{sigs.k8s.io => github.com/ghodss}/yaml/fields.go (99%) rename vendor/{sigs.k8s.io => github.com/ghodss}/yaml/yaml.go (78%) rename vendor/{sigs.k8s.io => github.com/ghodss}/yaml/yaml_go110.go (100%) create mode 100644 vendor/github.com/swaggo/swag/.travis.yml delete mode 100644 vendor/github.com/swaggo/swag/README_pt.md delete mode 100644 vendor/github.com/swaggo/swag/const.go create mode 100644 vendor/github.com/swaggo/swag/debug.go delete mode 100644 vendor/github.com/swaggo/swag/enums.go delete mode 100644 vendor/github.com/swaggo/swag/field_parser.go delete mode 100644 vendor/github.com/swaggo/swag/formatter.go delete mode 100644 vendor/github.com/swaggo/swag/generics.go delete mode 100644 vendor/github.com/swaggo/swag/golist.go delete mode 100644 vendor/github.com/swaggo/swag/package.go delete mode 100644 vendor/github.com/swaggo/swag/packages.go create mode 100644 vendor/github.com/swaggo/swag/property.go delete mode 100644 vendor/github.com/swaggo/swag/spec.go delete mode 100644 vendor/github.com/swaggo/swag/types.go delete mode 100644 vendor/github.com/swaggo/swag/utils.go delete mode 100644 vendor/sigs.k8s.io/yaml/.travis.yml delete mode 100644 vendor/sigs.k8s.io/yaml/CONTRIBUTING.md delete mode 100644 vendor/sigs.k8s.io/yaml/OWNERS delete mode 100644 vendor/sigs.k8s.io/yaml/RELEASE.md delete mode 100644 vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS delete mode 100644 vendor/sigs.k8s.io/yaml/code-of-conduct.md diff --git a/build/swag/main.go b/build/swag/main.go index f994e44d0d..17366b688f 100644 --- a/build/swag/main.go +++ b/build/swag/main.go @@ -56,7 +56,7 @@ var initFlags = []cli.Flag{ Name: parseVendorFlag, Usage: "Parse go files in 'vendor' folder, disabled by default", }, - &cli.IntFlag{ + &cli.BoolFlag{ Name: parseDependencyFlag, Usage: "ParseDependencies whether swag should be parse outside dependency folder: 0 none, 1 models, 2 operations, 3 all. 0 by default", }, @@ -92,7 +92,7 @@ func initAction(c *cli.Context) error { PropNamingStrategy: strategy, OutputDir: c.String(outputFlag), ParseVendor: c.Bool(parseVendorFlag), - ParseDependency: c.Int(parseDependencyFlag), + ParseDependency: c.Bool(parseDependencyFlag), MarkdownFilesDir: c.String(markdownFilesFlag), ParseInternal: c.Bool(parseInternal), GeneratedTime: c.Bool(generatedTimeFlag), diff --git a/go.mod b/go.mod index 7da9092b30..46df732814 100644 --- a/go.mod +++ b/go.mod @@ -30,9 +30,6 @@ require ( github.com/github/gh-ost v1.1.3-0.20210727153850-e484824bbd68 github.com/go-git/go-git/v5 v5.9.0 github.com/go-ini/ini v1.63.2 - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.14.1 @@ -68,6 +65,7 @@ require ( ) require ( + github.com/aliyun/credentials-go v1.1.2 github.com/hashicorp/go-version v1.7.0 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.69 golang.org/x/text v0.13.0 @@ -89,7 +87,6 @@ require ( github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect github.com/alibabacloud-go/openapi-util v0.1.0 // indirect github.com/alibabacloud-go/tea-xml v1.1.2 // indirect - github.com/aliyun/credentials-go v1.1.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cloudflare/circl v1.3.3 // indirect @@ -101,13 +98,17 @@ require ( github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-mysql-org/go-mysql v1.3.0 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/go-openapi/errors v0.20.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect @@ -175,7 +176,6 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) replace ( @@ -183,5 +183,6 @@ replace ( github.com/labstack/echo/v4 => github.com/labstack/echo/v4 v4.6.1 github.com/pingcap/log => github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9 github.com/pingcap/parser => github.com/sjjian/parser v0.0.0-20240305095250-688ad439ef31 + github.com/swaggo/swag => github.com/swaggo/swag v1.6.7 google.golang.org/grpc => google.golang.org/grpc v1.29.0 ) diff --git a/go.sum b/go.sum index cf16c4304b..2aa0230b6c 100644 --- a/go.sum +++ b/go.sum @@ -35,7 +35,6 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= @@ -227,6 +226,7 @@ github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0 github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= @@ -265,25 +265,20 @@ github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= @@ -558,7 +553,6 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= @@ -842,12 +836,8 @@ github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuI github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= github.com/swaggo/http-swagger v0.0.0-20200103000832-0e9263c4b516/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= -github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= -github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio= -github.com/swaggo/swag v1.6.5/go.mod h1:Y7ZLSS0d0DdxhWGVhQdu+Bu1QhaF5k0RD7FKdiAykeY= -github.com/swaggo/swag v1.6.6-0.20200323071853-8e21f4cefeea/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= -github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= -github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +github.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s= +github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= github.com/syndtr/goleveldb v0.0.0-20180815032940-ae2bd5eed72d/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190625010220-02440ea7a285 h1:uSDYjYejelKyceA6DiCsngFof9jAyeaSyX9XC5a1a7Q= github.com/syndtr/goleveldb v1.0.1-0.20190625010220-02440ea7a285/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= @@ -880,7 +870,6 @@ github.com/ungerik/go-dry v0.0.0-20210209114055-a3e162a9e62e h1:1oi3J06qNU9zsDsX github.com/ungerik/go-dry v0.0.0-20210209114055-a3e162a9e62e/go.mod h1:g61b/Pvp64yQ4oYVbcdA7qqzn1RcQIHZQuhWOVG1VHk= github.com/unrolled/render v0.0.0-20171102162132-65450fb6b2d3/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= @@ -1171,7 +1160,6 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -1312,8 +1300,6 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 h1:VO9oZbbkvTwqLimlQt15QNdOOBArT2dw/bvzsMZBiqQ= sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/sqle/docs/docs.go b/sqle/docs/docs.go index 8e1990fecc..e3707eb89f 100644 --- a/sqle/docs/docs.go +++ b/sqle/docs/docs.go @@ -15265,7 +15265,7 @@ var doc = `{ "description": "节点信息", "type": "array", "items": { - "$ref": "#/definitions/v1.pipelineNodeToBeUpdated" + "$ref": "#/definitions/v1.updatePipelineNode" } } } @@ -16060,7 +16060,7 @@ var doc = `{ } } }, - "v1.pipelineNodeToBeUpdated": { + "v1.updatePipelineNode": { "type": "object", "properties": { "audit_method": { diff --git a/sqle/docs/swagger.json b/sqle/docs/swagger.json index 5f5bbeda36..5efe6701c8 100644 --- a/sqle/docs/swagger.json +++ b/sqle/docs/swagger.json @@ -15249,7 +15249,7 @@ "description": "节点信息", "type": "array", "items": { - "$ref": "#/definitions/v1.pipelineNodeToBeUpdated" + "$ref": "#/definitions/v1.updatePipelineNode" } } } @@ -16044,7 +16044,7 @@ } } }, - "v1.pipelineNodeToBeUpdated": { + "v1.updatePipelineNode": { "type": "object", "properties": { "audit_method": { diff --git a/sqle/docs/swagger.yaml b/sqle/docs/swagger.yaml index f58b0ddba7..03c32eaaa9 100644 --- a/sqle/docs/swagger.yaml +++ b/sqle/docs/swagger.yaml @@ -3746,7 +3746,7 @@ definitions: nodes: description: 节点信息 items: - $ref: '#/definitions/v1.pipelineNodeToBeUpdated' + $ref: '#/definitions/v1.updatePipelineNode' type: array type: object v1.UpdateProjectRuleTemplateReqV1: @@ -4286,7 +4286,7 @@ definitions: - release type: string type: object - v1.pipelineNodeToBeUpdated: + v1.updatePipelineNode: properties: audit_method: description: 审核方式,必选,可选项为离线审核、在线审核 diff --git a/vendor/github.com/ghodss/yaml/.travis.yml b/vendor/github.com/ghodss/yaml/.travis.yml new file mode 100644 index 0000000000..98ad417e22 --- /dev/null +++ b/vendor/github.com/ghodss/yaml/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - "1.9" + - "1.10" + - "1.11" +script: + - go test + - go build diff --git a/vendor/sigs.k8s.io/yaml/LICENSE b/vendor/github.com/ghodss/yaml/LICENSE similarity index 100% rename from vendor/sigs.k8s.io/yaml/LICENSE rename to vendor/github.com/ghodss/yaml/LICENSE diff --git a/vendor/sigs.k8s.io/yaml/README.md b/vendor/github.com/ghodss/yaml/README.md similarity index 86% rename from vendor/sigs.k8s.io/yaml/README.md rename to vendor/github.com/ghodss/yaml/README.md index e81cc426be..0200f75b4d 100644 --- a/vendor/sigs.k8s.io/yaml/README.md +++ b/vendor/github.com/ghodss/yaml/README.md @@ -1,14 +1,12 @@ # YAML marshaling and unmarshaling support for Go -[![Build Status](https://travis-ci.org/kubernetes-sigs/yaml.svg)](https://travis-ci.org/kubernetes-sigs/yaml) - -kubernetes-sigs/yaml is a permanent fork of [ghodss/yaml](https://github.com/ghodss/yaml). +[![Build Status](https://travis-ci.org/ghodss/yaml.svg)](https://travis-ci.org/ghodss/yaml) ## Introduction A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs. -In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://web.archive.org/web/20190603050330/http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/). +In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/). ## Compatibility @@ -34,13 +32,13 @@ GOOD: To install, run: ``` -$ go get sigs.k8s.io/yaml +$ go get github.com/ghodss/yaml ``` And import using: ``` -import "sigs.k8s.io/yaml" +import "github.com/ghodss/yaml" ``` Usage is very similar to the JSON library: @@ -51,7 +49,7 @@ package main import ( "fmt" - "sigs.k8s.io/yaml" + "github.com/ghodss/yaml" ) type Person struct { @@ -95,7 +93,7 @@ package main import ( "fmt" - "sigs.k8s.io/yaml" + "github.com/ghodss/yaml" ) func main() { @@ -107,8 +105,8 @@ func main() { } fmt.Println(string(y)) /* Output: - age: 30 name: John + age: 30 */ j2, err := yaml.YAMLToJSON(y) if err != nil { diff --git a/vendor/sigs.k8s.io/yaml/fields.go b/vendor/github.com/ghodss/yaml/fields.go similarity index 99% rename from vendor/sigs.k8s.io/yaml/fields.go rename to vendor/github.com/ghodss/yaml/fields.go index 235b7f2cf6..5860074026 100644 --- a/vendor/sigs.k8s.io/yaml/fields.go +++ b/vendor/github.com/ghodss/yaml/fields.go @@ -1,7 +1,6 @@ // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. - package yaml import ( diff --git a/vendor/sigs.k8s.io/yaml/yaml.go b/vendor/github.com/ghodss/yaml/yaml.go similarity index 78% rename from vendor/sigs.k8s.io/yaml/yaml.go rename to vendor/github.com/ghodss/yaml/yaml.go index efbc535d41..dfd264d6c5 100644 --- a/vendor/sigs.k8s.io/yaml/yaml.go +++ b/vendor/github.com/ghodss/yaml/yaml.go @@ -1,4 +1,14 @@ -package yaml +// Package yaml provides a wrapper around go-yaml designed to enable a better +// way of handling YAML when marshaling to and from structs. +// +// In short, this package first converts YAML to JSON using go-yaml and then +// uses json.Marshal and json.Unmarshal to convert to or from the struct. This +// means that it effectively reuses the JSON struct tags as well as the custom +// JSON methods MarshalJSON and UnmarshalJSON unlike go-yaml. +// +// See also http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang +// +package yaml // import "github.com/ghodss/yaml" import ( "bytes" @@ -11,7 +21,7 @@ import ( "gopkg.in/yaml.v2" ) -// Marshal marshals the object into JSON then converts JSON to YAML and returns the +// Marshals the object into JSON then converts JSON to YAML and returns the // YAML. func Marshal(o interface{}) ([]byte, error) { j, err := json.Marshal(o) @@ -33,24 +43,19 @@ type JSONOpt func(*json.Decoder) *json.Decoder // Unmarshal converts YAML to JSON then uses JSON to unmarshal into an object, // optionally configuring the behavior of the JSON unmarshal. func Unmarshal(y []byte, o interface{}, opts ...JSONOpt) error { - return yamlUnmarshal(y, o, false, opts...) + return unmarshal(yaml.Unmarshal, y, o, opts) } -// UnmarshalStrict strictly converts YAML to JSON then uses JSON to unmarshal -// into an object, optionally configuring the behavior of the JSON unmarshal. +// UnmarshalStrict is like Unmarshal except that any mapping keys that are +// duplicates will result in an error. +// To also be strict about unknown fields, add the DisallowUnknownFields option. func UnmarshalStrict(y []byte, o interface{}, opts ...JSONOpt) error { - return yamlUnmarshal(y, o, true, append(opts, DisallowUnknownFields)...) + return unmarshal(yaml.UnmarshalStrict, y, o, opts) } -// yamlUnmarshal unmarshals the given YAML byte stream into the given interface, -// optionally performing the unmarshalling strictly -func yamlUnmarshal(y []byte, o interface{}, strict bool, opts ...JSONOpt) error { +func unmarshal(f func(in []byte, out interface{}) (err error), y []byte, o interface{}, opts []JSONOpt) error { vo := reflect.ValueOf(o) - unmarshalFn := yaml.Unmarshal - if strict { - unmarshalFn = yaml.UnmarshalStrict - } - j, err := yamlToJSON(y, &vo, unmarshalFn) + j, err := yamlToJSON(y, &vo, f) if err != nil { return fmt.Errorf("error converting YAML to JSON: %v", err) } @@ -78,7 +83,7 @@ func jsonUnmarshal(r io.Reader, o interface{}, opts ...JSONOpt) error { return nil } -// JSONToYAML Converts JSON to YAML. +// Convert JSON to YAML. func JSONToYAML(j []byte) ([]byte, error) { // Convert the JSON to an object. var jsonObj interface{} @@ -129,7 +134,7 @@ func yamlToJSON(y []byte, jsonTarget *reflect.Value, yamlUnmarshal func([]byte, // YAML objects are not completely compatible with JSON objects (e.g. you // can have non-string keys in YAML). So, convert the YAML-compatible object // to a JSON-compatible object, failing with an error if irrecoverable - // incompatibilties happen along the way. + // incompatibilities happen along the way. jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget) if err != nil { return nil, err @@ -316,65 +321,6 @@ func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (in } return yamlObj, nil } -} - -// JSONObjectToYAMLObject converts an in-memory JSON object into a YAML in-memory MapSlice, -// without going through a byte representation. A nil or empty map[string]interface{} input is -// converted to an empty map, i.e. yaml.MapSlice(nil). -// -// interface{} slices stay interface{} slices. map[string]interface{} becomes yaml.MapSlice. -// -// int64 and float64 are down casted following the logic of github.com/go-yaml/yaml: -// - float64s are down-casted as far as possible without data-loss to int, int64, uint64. -// - int64s are down-casted to int if possible without data-loss. -// -// Big int/int64/uint64 do not lose precision as in the json-yaml roundtripping case. -// -// string, bool and any other types are unchanged. -func JSONObjectToYAMLObject(j map[string]interface{}) yaml.MapSlice { - if len(j) == 0 { - return nil - } - ret := make(yaml.MapSlice, 0, len(j)) - for k, v := range j { - ret = append(ret, yaml.MapItem{Key: k, Value: jsonToYAMLValue(v)}) - } - return ret -} -func jsonToYAMLValue(j interface{}) interface{} { - switch j := j.(type) { - case map[string]interface{}: - if j == nil { - return interface{}(nil) - } - return JSONObjectToYAMLObject(j) - case []interface{}: - if j == nil { - return interface{}(nil) - } - ret := make([]interface{}, len(j)) - for i := range j { - ret[i] = jsonToYAMLValue(j[i]) - } - return ret - case float64: - // replicate the logic in https://github.com/go-yaml/yaml/blob/51d6538a90f86fe93ac480b35f37b2be17fef232/resolve.go#L151 - if i64 := int64(j); j == float64(i64) { - if i := int(i64); i64 == int64(i) { - return i - } - return i64 - } - if ui64 := uint64(j); j == float64(ui64) { - return ui64 - } - return j - case int64: - if i := int(j); j == int64(i) { - return i - } - return j - } - return j + return nil, nil } diff --git a/vendor/sigs.k8s.io/yaml/yaml_go110.go b/vendor/github.com/ghodss/yaml/yaml_go110.go similarity index 100% rename from vendor/sigs.k8s.io/yaml/yaml_go110.go rename to vendor/github.com/ghodss/yaml/yaml_go110.go diff --git a/vendor/github.com/swaggo/swag/.gitignore b/vendor/github.com/swaggo/swag/.gitignore index 865a6f357f..bea8db6819 100644 --- a/vendor/github.com/swaggo/swag/.gitignore +++ b/vendor/github.com/swaggo/swag/.gitignore @@ -1,9 +1,5 @@ dist testdata/simple*/docs -testdata/quotes/docs -testdata/quotes/quotes.so -testdata/delims/docs -testdata/delims/delims.so example/basic/docs/* example/celler/docs/* cover.out @@ -20,5 +16,5 @@ cover.out # Etc .DS_Store -/swag -/swag.exe +swag +swag.exe diff --git a/vendor/github.com/swaggo/swag/.goreleaser.yml b/vendor/github.com/swaggo/swag/.goreleaser.yml index a431d5db6c..d0b576ab1d 100644 --- a/vendor/github.com/swaggo/swag/.goreleaser.yml +++ b/vendor/github.com/swaggo/swag/.goreleaser.yml @@ -1,23 +1,12 @@ build: main: cmd/swag/main.go - goos: - - linux - - darwin - goarch: - - amd64 - - arm64 - - 386 - env: - - CGO_ENABLED=0 - -archives: - - id: foo - name_template: >- - {{ .ProjectName }}_ - {{- .Version }}_ - {{- if eq .Os "linux"}}Linux{{ else if eq .Os "darwin"}}Darwin{{ else }}{{ .Os }}{{ end }}_ - {{- if eq .Arch "386" }}i386{{ else if eq .Arch "amd64" }}x86_64{{ else }}{{ .Arch }}{{ end }} - +archive: + replacements: + darwin: Darwin + linux: Linux + windows: Windows + 386: i386 + amd64: x86_64 checksum: name_template: 'checksums.txt' snapshot: diff --git a/vendor/github.com/swaggo/swag/.travis.yml b/vendor/github.com/swaggo/swag/.travis.yml new file mode 100644 index 0000000000..36a51fd720 --- /dev/null +++ b/vendor/github.com/swaggo/swag/.travis.yml @@ -0,0 +1,18 @@ +language: go + +go: + - 1.13.x + - 1.14.x + +install: + - make deps + +script: + - make fmt-check + - make lint + - make vet + - make build + - make test + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/swaggo/swag/Dockerfile b/vendor/github.com/swaggo/swag/Dockerfile index 410fe31efc..0a5d9b8db9 100644 --- a/vendor/github.com/swaggo/swag/Dockerfile +++ b/vendor/github.com/swaggo/swag/Dockerfile @@ -1,7 +1,7 @@ # Dockerfile References: https://docs.docker.com/engine/reference/builder/ # Start from the latest golang base image -FROM golang:1.20-alpine as builder +FROM golang:1.14-alpine as builder # Set the Current Working Directory inside the container WORKDIR /app @@ -22,9 +22,8 @@ RUN CGO_ENABLED=0 GOOS=linux go build -v -a -installsuffix cgo -o swag cmd/swag/ ######## Start a new stage from scratch ####### FROM scratch -WORKDIR /code/ +WORKDIR /root/ # Copy the Pre-built binary file from the previous stage -COPY --from=builder /app/swag /bin/swag +COPY --from=builder /app/swag . -ENTRYPOINT ["/bin/swag"] diff --git a/vendor/github.com/swaggo/swag/Makefile b/vendor/github.com/swaggo/swag/Makefile index 0d8175da78..f78299612d 100644 --- a/vendor/github.com/swaggo/swag/Makefile +++ b/vendor/github.com/swaggo/swag/Makefile @@ -6,7 +6,6 @@ GOBUILD:=$(GOCMD) build GOINSTALL:=$(GOCMD) install GOCLEAN:=$(GOCMD) clean GOTEST:=$(GOCMD) test -GOMODTIDY:=$(GOCMD) mod tidy GOGET:=$(GOCMD) get GOLIST:=$(GOCMD) list GOVET:=$(GOCMD) vet @@ -14,7 +13,7 @@ GOPATH:=$(shell $(GOCMD) env GOPATH) u := $(if $(update),-u) BINARY_NAME:=swag -PACKAGES:=$(shell $(GOLIST) github.com/swaggo/swag github.com/swaggo/swag/cmd/swag github.com/swaggo/swag/gen github.com/swaggo/swag/format) +PACKAGES:=$(shell $(GOLIST) github.com/swaggo/swag github.com/swaggo/swag/cmd/swag github.com/swaggo/swag/gen) GOFILES:=$(shell find . -name "*.go" -type f) export GO111MODULE := on @@ -55,12 +54,20 @@ clean: .PHONY: deps deps: - $(GOMODTIDY) + $(GOGET) github.com/swaggo/cli + $(GOGET) github.com/ghodss/yaml + $(GOGET) github.com/gin-gonic/gin + $(GOGET) github.com/KyleBanks/depth + $(GOGET) github.com/go-openapi/jsonreference + $(GOGET) github.com/go-openapi/spec + $(GOGET) github.com/stretchr/testify/assert + $(GOGET) github.com/alecthomas/template + $(GOGET) golang.org/x/tools/go/loader .PHONY: devel-deps -devel-deps: +devel-deps: GO111MODULE=off $(GOGET) -v -u \ - golang.org/x/lint/golint + golang.org/x/lint/golint .PHONY: lint lint: devel-deps @@ -86,4 +93,4 @@ fmt-check: .PHONY: view-covered view-covered: $(GOTEST) -coverprofile=cover.out $(TARGET) - $(GOCMD) tool cover -html=cover.out + $(GOCMD) tool cover -html=cover.out \ No newline at end of file diff --git a/vendor/github.com/swaggo/swag/README.md b/vendor/github.com/swaggo/swag/README.md index 054e36ec6c..5edbd5d5aa 100644 --- a/vendor/github.com/swaggo/swag/README.md +++ b/vendor/github.com/swaggo/swag/README.md @@ -1,15 +1,15 @@ # swag -🌍 *[English](README.md) ∙ [简体中文](README_zh-CN.md) ∙ [Português](README_pt.md)* +🌍 *[English](README.md) ∙ [简体中文](README_zh-CN.md)* -[![Build Status](https://github.com/swaggo/swag/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/features/actions) +[![Travis Status](https://img.shields.io/travis/swaggo/swag/master.svg)](https://travis-ci.org/swaggo/swag) [![Coverage Status](https://img.shields.io/codecov/c/github/swaggo/swag/master.svg)](https://codecov.io/gh/swaggo/swag) [![Go Report Card](https://goreportcard.com/badge/github.com/swaggo/swag)](https://goreportcard.com/report/github.com/swaggo/swag) [![codebeat badge](https://codebeat.co/badges/71e2f5e5-9e6b-405d-baf9-7cc8b5037330)](https://codebeat.co/projects/github-com-swaggo-swag-master) [![Go Doc](https://godoc.org/github.com/swaggo/swagg?status.svg)](https://godoc.org/github.com/swaggo/swag) -[![Backers on Open Collective](https://opencollective.com/swag/backers/badge.svg)](#backers) +[![Backers on Open Collective](https://opencollective.com/swag/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/swag/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fswaggo%2Fswag.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fswaggo%2Fswag?ref=badge_shield) [![Release](https://img.shields.io/github/release/swaggo/swag.svg?style=flat-square)](https://github.com/swaggo/swag/releases) @@ -20,7 +20,6 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie - [Getting started](#getting-started) - [Supported Web Frameworks](#supported-web-frameworks) - [How to use it with Gin](#how-to-use-it-with-gin) - - [The swag formatter](#the-swag-formatter) - [Implementation Status](#implementation-status) - [Declarative Comments Format](#declarative-comments-format) - [General API Info](#general-api-info) @@ -29,64 +28,44 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie - [Examples](#examples) - [Descriptions over multiple lines](#descriptions-over-multiple-lines) - [User defined structure with an array type](#user-defined-structure-with-an-array-type) - - [Function scoped struct declaration](#function-scoped-struct-declaration) - [Model composition in response](#model-composition-in-response) - - [Add a headers in response](#add-a-headers-in-response) + - [Add a headers in response](#add-a-headers-in-response) - [Use multiple path params](#use-multiple-path-params) - [Example value of struct](#example-value-of-struct) - - [SchemaExample of body](#schemaexample-of-body) - [Description of struct](#description-of-struct) - [Use swaggertype tag to supported custom type](#use-swaggertype-tag-to-supported-custom-type) - - [Use global overrides to support a custom type](#use-global-overrides-to-support-a-custom-type) - [Use swaggerignore tag to exclude a field](#use-swaggerignore-tag-to-exclude-a-field) - [Add extension info to struct field](#add-extension-info-to-struct-field) - [Rename model to display](#rename-model-to-display) - - [How to use security annotations](#how-to-use-security-annotations) - - [Add a description for enum items](#add-a-description-for-enum-items) - - [Generate only specific docs file types](#generate-only-specific-docs-file-types) - - [How to use Go generic types](#how-to-use-generics) + - [How to using security annotations](#how-to-using-security-annotations) - [About the Project](#about-the-project) ## Getting started 1. Add comments to your API source code, See [Declarative Comments Format](#declarative-comments-format). -2. Install swag by using: +2. Download swag by using: ```sh -go install github.com/swaggo/swag/cmd/swag@latest -``` -To build from source you need [Go](https://golang.org/dl/) (1.18 or newer). - -Alternatively you can run the docker image: -```sh -docker run --rm -v $(pwd):/code ghcr.io/swaggo/swag:latest +$ go get -u github.com/swaggo/swag/cmd/swag ``` +To build from source you need [Go](https://golang.org/dl/) (1.9 or newer). Or download a pre-compiled binary from the [release page](https://github.com/swaggo/swag/releases). 3. Run `swag init` in the project's root folder which contains the `main.go` file. This will parse your comments and generate the required files (`docs` folder and `docs/docs.go`). ```sh -swag init +$ swag init ``` Make sure to import the generated `docs/docs.go` so that your specific configuration gets `init`'ed. If your General API annotations do not live in `main.go`, you can let swag know with `-g` flag. - ```go - import _ "example-module-name/docs" - ``` ```sh swag init -g http/api.go ``` -4. (optional) Use `swag fmt` format the SWAG comment. (Please upgrade to the latest version) - - ```sh - swag fmt - ``` - ## swag cli ```sh -swag init -h +$ swag init -h NAME: swag init - Create docs.go @@ -94,45 +73,14 @@ USAGE: swag init [command options] [arguments...] OPTIONS: - --quiet, -q Make the logger quiet. (default: false) - --generalInfo value, -g value Go file path in which 'swagger general API Info' is written (default: "main.go") - --dir value, -d value Directories you want to parse,comma separated and general-info file must be in the first one (default: "./") - --exclude value Exclude directories and files when searching, comma separated - --propertyStrategy value, -p value Property Naming Strategy like snakecase,camelcase,pascalcase (default: "camelcase") - --output value, -o value Output directory for all the generated files(swagger.json, swagger.yaml and docs.go) (default: "./docs") - --outputTypes value, --ot value Output types of generated files (docs.go, swagger.json, swagger.yaml) like go,json,yaml (default: "go,json,yaml") - --parseVendor Parse go files in 'vendor' folder, disabled by default (default: false) - --parseDependency, --pd Parse go files inside dependency folder, disabled by default (default: false) - --markdownFiles value, --md value Parse folder containing markdown files to use as description, disabled by default - --codeExampleFiles value, --cef value Parse folder containing code example files to use for the x-codeSamples extension, disabled by default - --parseInternal Parse go files in internal packages, disabled by default (default: false) - --generatedTime Generate timestamp at the top of docs.go, disabled by default (default: false) - --parseDepth value Dependency parse depth (default: 100) - --requiredByDefault Set validation required for all fields by default (default: false) - --instanceName value This parameter can be used to name different swagger document instances. It is optional. - --overridesFile value File to read global type overrides from. (default: ".swaggo") - --parseGoList Parse dependency via 'go list' (default: true) - --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) -``` - -```bash -swag fmt -h -NAME: - swag fmt - format swag comments - -USAGE: - swag fmt [command options] [arguments...] - -OPTIONS: - --dir value, -d value Directories you want to parse,comma separated and general-info file must be in the first one (default: "./") - --exclude value Exclude directories and files when searching, comma separated - --generalInfo value, -g value Go file path in which 'swagger general API Info' is written (default: "main.go") - --help, -h show help (default: false) - + --generalInfo value, -g value Go file path in which 'swagger general API Info' is written (default: "main.go") + --dir value, -d value Directory you want to parse (default: "./") + --exclude value Exclude directoies and files, comma separated + --propertyStrategy value, -p value Property Naming Strategy like snakecase,camelcase,pascalcase (default: "camelcase") + --output value, -o value Output directory for all the generated files(swagger.json, swagger.yaml and doc.go) (default: "./docs") + --parseVendor Parse go files in 'vendor' folder, disabled by default + --parseDependency Parse go files in outside dependency folder, disabled by default + --parseInternal Parse go files in internal packages, disabled by default ``` ## Supported Web Frameworks @@ -141,18 +89,13 @@ OPTIONS: - [echo](http://github.com/swaggo/echo-swagger) - [buffalo](https://github.com/swaggo/buffalo-swagger) - [net/http](https://github.com/swaggo/http-swagger) -- [gorilla/mux](https://github.com/swaggo/http-swagger) -- [go-chi/chi](https://github.com/swaggo/http-swagger) - [flamingo](https://github.com/i-love-flamingo/swagger) -- [fiber](https://github.com/gofiber/swagger) -- [atreugo](https://github.com/Nerzal/atreugo-swagger) -- [hertz](https://github.com/hertz-contrib/swagger) +- [fiber](https://github.com/arsmn/fiber-swagger) ## How to use it with Gin Find the example source code [here](https://github.com/swaggo/swag/tree/master/example/celler). -Finish the steps in [Getting started](#getting-started) 1. After using `swag init` to generate Swagger 2.0 docs, import the following packages: ```go import "github.com/swaggo/gin-swagger" // gin-swagger middleware @@ -162,25 +105,51 @@ import "github.com/swaggo/files" // swagger embed files 2. Add [General API](#general-api-info) annotations in `main.go` code: ```go -// @title Swagger Example API -// @version 1.0 -// @description This is a sample server celler server. -// @termsOfService http://swagger.io/terms/ +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server celler 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 + +// @host localhost:8080 +// @BasePath /api/v1 +// @query.collection.format multi -// @contact.name API Support -// @contact.url http://www.swagger.io/support -// @contact.email support@swagger.io +// @securityDefinitions.basic BasicAuth -// @license.name Apache 2.0 -// @license.url http://www.apache.org/licenses/LICENSE-2.0.html +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name Authorization -// @host localhost:8080 -// @BasePath /api/v1 +// @securitydefinitions.oauth2.application OAuth2Application +// @tokenUrl https://example.com/oauth/token +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information -// @securityDefinitions.basic BasicAuth +// @securitydefinitions.oauth2.implicit OAuth2Implicit +// @authorizationurl https://example.com/oauth/authorize +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information + +// @securitydefinitions.oauth2.password OAuth2Password +// @tokenUrl https://example.com/oauth/token +// @scope.read Grants read access +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information + +// @securitydefinitions.oauth2.accessCode OAuth2AccessCode +// @tokenUrl https://example.com/oauth/token +// @authorizationurl https://example.com/oauth/authorize +// @scope.admin Grants read and write access to administrative information + +// @x-extension-openapi {"example": "value on a json format"} -// @externalDocs.description OpenAPI -// @externalDocs.url https://swagger.io/resources/open-api/ func main() { r := gin.Default() @@ -205,7 +174,7 @@ func main() { //... ``` -Additionally some general API info can be set dynamically. The generated code package `docs` exports `SwaggerInfo` variable which we can use to set the title, description, version, host and base path programmatically. Example using Gin: +Additionally some general API info can be set dynamically. The generated code package `docs` exports `SwaggerInfo` variable which we can use to set the title, description, version, host and base path programatically. Example using Gin: ```go package main @@ -214,26 +183,29 @@ import ( "github.com/gin-gonic/gin" "github.com/swaggo/files" "github.com/swaggo/gin-swagger" - + "./docs" // docs is generated by Swag CLI, you have to import it. ) -// @contact.name API Support -// @contact.url http://www.swagger.io/support -// @contact.email support@swagger.io +// @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 + +// @termsOfService http://swagger.io/terms/ -// @license.name Apache 2.0 -// @license.url http://www.apache.org/licenses/LICENSE-2.0.html func main() { - // programmatically set swagger info + // programatically set swagger info docs.SwaggerInfo.Title = "Swagger Example API" docs.SwaggerInfo.Description = "This is a sample server Petstore server." docs.SwaggerInfo.Version = "1.0" docs.SwaggerInfo.Host = "petstore.swagger.io" docs.SwaggerInfo.BasePath = "/v2" docs.SwaggerInfo.Schemes = []string{"http", "https"} - + r := gin.New() // use ginSwagger middleware to serve the API docs @@ -249,111 +221,76 @@ func main() { package controller import ( - "fmt" - "net/http" - "strconv" + "fmt" + "net/http" + "strconv" - "github.com/gin-gonic/gin" - "github.com/swaggo/swag/example/celler/httputil" - "github.com/swaggo/swag/example/celler/model" + "github.com/gin-gonic/gin" + "github.com/swaggo/swag/example/celler/httputil" + "github.com/swaggo/swag/example/celler/model" ) // ShowAccount godoc -// @Summary Show an account -// @Description get string by ID -// @Tags accounts -// @Accept json -// @Produce json -// @Param id path int true "Account ID" -// @Success 200 {object} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts/{id} [get] +// @Summary Show a account +// @Description get string by ID +// @ID get-string-by-int +// @Accept json +// @Produce json +// @Param id path int true "Account ID" +// @Success 200 {object} model.Account +// @Header 200 {string} Token "qwerty" +// @Failure 400 {object} httputil.HTTPError +// @Failure 404 {object} httputil.HTTPError +// @Failure 500 {object} httputil.HTTPError +// @Router /accounts/{id} [get] func (c *Controller) ShowAccount(ctx *gin.Context) { - id := ctx.Param("id") - aid, err := strconv.Atoi(id) - if err != nil { - httputil.NewError(ctx, http.StatusBadRequest, err) - return - } - account, err := model.AccountOne(aid) - if err != nil { - httputil.NewError(ctx, http.StatusNotFound, err) - return - } - ctx.JSON(http.StatusOK, account) + id := ctx.Param("id") + aid, err := strconv.Atoi(id) + if err != nil { + httputil.NewError(ctx, http.StatusBadRequest, err) + return + } + account, err := model.AccountOne(aid) + if err != nil { + httputil.NewError(ctx, http.StatusNotFound, err) + return + } + ctx.JSON(http.StatusOK, account) } // ListAccounts godoc -// @Summary List accounts -// @Description get accounts -// @Tags accounts -// @Accept json -// @Produce json -// @Param q query string false "name search by q" Format(email) -// @Success 200 {array} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts [get] +// @Summary List accounts +// @Description get accounts +// @Accept json +// @Produce json +// @Param q query string false "name search by q" +// @Success 200 {array} model.Account +// @Header 200 {string} Token "qwerty" +// @Failure 400 {object} httputil.HTTPError +// @Failure 404 {object} httputil.HTTPError +// @Failure 500 {object} httputil.HTTPError +// @Router /accounts [get] func (c *Controller) ListAccounts(ctx *gin.Context) { - q := ctx.Request.URL.Query().Get("q") - accounts, err := model.AccountsAll(q) - if err != nil { - httputil.NewError(ctx, http.StatusNotFound, err) - return - } - ctx.JSON(http.StatusOK, accounts) + q := ctx.Request.URL.Query().Get("q") + accounts, err := model.AccountsAll(q) + if err != nil { + httputil.NewError(ctx, http.StatusNotFound, err) + return + } + ctx.JSON(http.StatusOK, accounts) } + //... ``` ```console -swag init +$ swag init ``` 4. Run your app, and browse to http://localhost:8080/swagger/index.html. You will see Swagger 2.0 Api documents as shown below: ![swagger_index.html](https://raw.githubusercontent.com/swaggo/swag/master/assets/swagger-image.png) -## The swag formatter - -The Swag Comments can be automatically formatted, just like 'go fmt'. -Find the result of formatting [here](https://github.com/swaggo/swag/tree/master/example/celler). - -Usage: -```shell -swag fmt -``` - -Exclude folder: -```shell -swag fmt -d ./ --exclude ./internal -``` - -When using `swag fmt`, you need to ensure that you have a doc comment for the function to ensure correct formatting. -This is due to `swag fmt` indenting swag comments with tabs, which is only allowed *after* a standard doc comment. - -For example, use - -```go -// ListAccounts lists all existing accounts -// -// @Summary List accounts -// @Description get accounts -// @Tags accounts -// @Accept json -// @Produce json -// @Param q query string false "name search by q" Format(email) -// @Success 200 {array} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts [get] -func (c *Controller) ListAccounts(ctx *gin.Context) { -``` - ## Implementation Status [Swagger 2.0 document](https://swagger.io/docs/specification/2-0/basic-structure/) @@ -398,12 +335,8 @@ func (c *Controller) ListAccounts(ctx *gin.Context) { | license.url | A URL to the license used for the API. MUST be in the format of a URL. | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html | | host | The host (name or ip) serving the API. | // @host localhost:8080 | | BasePath | The base path on which the API is served. | // @BasePath /api/v1 | -| accept | A list of MIME types the APIs can consume. Note that Accept only affects operations with a request body, such as POST, PUT and PATCH. Value MUST be as described under [Mime Types](#mime-types). | // @accept json | -| produce | A list of MIME types the APIs can produce. Value MUST be as described under [Mime Types](#mime-types). | // @produce json | | query.collection.format | The default collection(array) param format in query,enums:csv,multi,pipes,tsv,ssv. If not set, csv is the default.| // @query.collection.format multi | schemes | The transfer protocol for the operation that separated by spaces. | // @schemes http https | -| externalDocs.description | Description of the external document. | // @externalDocs.description OpenAPI | -| externalDocs.url | URL of the external document. | // @externalDocs.url https://swagger.io/resources/open-api/ | | x-name | The extension key, must be start by x- and take only json value | // @x-example-key {"key": "value"} | ### Using markdown descriptions @@ -425,26 +358,22 @@ When a short string in your documentation is insufficient, or you need images, c [celler/controller](https://github.com/swaggo/swag/tree/master/example/celler/controller) -| annotation | description | -|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| description | A verbose explanation of the operation behavior. | -| description.markdown | A short description of the application. The description will be read from a file. E.g. `@description.markdown details` will load `details.md` | // @description.file endpoint.description.markdown | -| id | A unique string used to identify the operation. Must be unique among all API operations. | -| tags | A list of tags to each API operation that separated by commas. | -| summary | A short summary of what the operation does. | -| accept | A list of MIME types the APIs can consume. Note that Accept only affects operations with a request body, such as POST, PUT and PATCH. Value MUST be as described under [Mime Types](#mime-types). | -| produce | A list of MIME types the APIs can produce. Value MUST be as described under [Mime Types](#mime-types). | -| param | Parameters that separated by spaces. `param name`,`param type`,`data type`,`is mandatory?`,`comment` `attribute(optional)` | -| security | [Security](#security) to each API operation. | -| success | Success response that separated by spaces. `return code or default`,`{param type}`,`data type`,`comment` | -| failure | Failure response that separated by spaces. `return code or default`,`{param type}`,`data type`,`comment` | -| response | As same as `success` and `failure` | -| header | Header in response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` | -| router | Path definition that separated by spaces. `path`,`[httpMethod]` | -| deprecatedrouter | As same as router, but deprecated. | -| x-name | The extension key, must be start by x- and take only json value. | -| x-codeSample | Optional Markdown usage. take `file` as parameter. This will then search for a file named like the summary in the given folder. | -| deprecated | Mark endpoint as deprecated. | +| annotation | description | +|-------------|----------------------------------------------------------------------------------------------------------------------------| +| description | A verbose explanation of the operation behavior. | +| description.markdown | A short description of the application. The description will be read from a file named like endpointname.md| // @description.file endpoint.description.markdown | +| id | A unique string used to identify the operation. Must be unique among all API operations. | +| tags | A list of tags to each API operation that separated by commas. | +| summary | A short summary of what the operation does. | +| accept | A list of MIME types the APIs can consume. Value MUST be as described under [Mime Types](#mime-types). | +| produce | A list of MIME types the APIs can produce. Value MUST be as described under [Mime Types](#mime-types). | +| param | Parameters that separated by spaces. `param name`,`param type`,`data type`,`is mandatory?`,`comment` `attribute(optional)` | +| security | [Security](#security) to each API operation. | +| success | Success response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` | +| failure | Failure response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` | +| header | Header in response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` | +| router | Path definition that separated by spaces. `path`,`[httpMethod]` | +| x-name | The extension key, must be start by x- and take only json value. | @@ -484,48 +413,45 @@ Besides that, `swag` also accepts aliases for some MIME Types as follows: - integer (int, uint, uint32, uint64) - number (float32) - boolean (bool) -- file (param data type when uploading) - user defined struct ## Security | annotation | description | parameters | example | |------------|-------------|------------|---------| | securitydefinitions.basic | [Basic](https://swagger.io/docs/specification/2-0/authentication/basic-authentication/) auth. | | // @securityDefinitions.basic BasicAuth | -| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, description | // @securityDefinitions.apikey ApiKeyAuth | -| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, description | // @securitydefinitions.oauth2.application OAuth2Application | -| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | -| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, description | // @securitydefinitions.oauth2.password OAuth2Password | -| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | - - -| parameters annotation | example | -|---------------------------------|-------------------------------------------------------------------------| -| in | // @in header | -| name | // @name Authorization | -| tokenUrl | // @tokenUrl https://example.com/oauth/token | -| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | -| scope.hoge | // @scope.write Grants write access | -| description | // @description OAuth protects our entity endpoints | +| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name | // @securityDefinitions.apikey ApiKeyAuth | +| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope | // @securitydefinitions.oauth2.application OAuth2Application | +| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope | // @securitydefinitions.oauth2.implicit OAuth2Implicit | +| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope | // @securitydefinitions.oauth2.password OAuth2Password | +| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | + + +| parameters annotation | example | +|-----------------------|----------------------------------------------------------| +| in | // @in header | +| name | // @name Authorization | +| tokenUrl | // @tokenUrl https://example.com/oauth/token | +| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | +| scope.hoge | // @scope.write Grants write access | + ## Attribute ```go -// @Param enumstring query string false "string enums" Enums(A, B, C) -// @Param enumint query int false "int enums" Enums(1, 2, 3) -// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3) -// @Param string query string false "string valid" minlength(5) maxlength(10) -// @Param int query int false "int valid" minimum(1) maximum(10) -// @Param default query string false "string default" default(A) -// @Param example query string false "string example" example(string) -// @Param collection query []string false "string collection" collectionFormat(multi) -// @Param extensions query []string false "string collection" extensions(x-example=test,x-nullable) +// @Param enumstring query string false "string enums" Enums(A, B, C) +// @Param enumint query int false "int enums" Enums(1, 2, 3) +// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3) +// @Param string query string false "string valid" minlength(5) maxlength(10) +// @Param int query int false "int valid" mininum(1) maxinum(10) +// @Param default query string false "string default" default(A) +// @Param collection query []string false "string collection" collectionFormat(multi) ``` It also works for the struct fields: ```go type Foo struct { - Bar string `minLength:"4" maxLength:"16" example:"random string"` + Bar string `minLength:"4" maxLength:"16"` Baz int `minimum:"10" maximum:"20" default:"15"` Qux []string `enums:"foo,bar,baz"` } @@ -535,23 +461,21 @@ type Foo struct { Field Name | Type | Description ---|:---:|--- -validate | `string` | Determines the validation for the parameter. Possible values are: `required,optional`. +validate | `string` | Determines the validation for the parameter. Possible values are: `required`. default | * | Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request. (Note: "default" has no meaning for required parameters.) See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. Unlike JSON Schema this value MUST conform to the defined [`type`](#parameterType) for this parameter. maximum | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.2. minimum | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.3. -multipleOf | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.1. maxLength | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.1. minLength | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.2. enums | [\*] | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1. format | `string` | The extending format for the previously mentioned [`type`](#parameterType). See [Data Type Formats](https://swagger.io/specification/v2/#dataTypeFormat) for further details. collectionFormat | `string` |Determines the format of the array if type array is used. Possible values are: Default value is `csv`. -example | * | Declares the example for the parameter value -extensions | `string` | Add extension to parameters. ### Future Field Name | Type | Description ---|:---:|--- +multipleOf | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.1. pattern | `string` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3. maxItems | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.2. minItems | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.3. @@ -561,7 +485,7 @@ Field Name | Type | Description ### Descriptions over multiple lines -You can add descriptions spanning multiple lines in either the general api description or routes definitions like so: +You can add descriptions spanning multiple lines in either the general api description or routes definitions like so: ```go // @description This is the first line @@ -584,30 +508,6 @@ type Account struct { } ``` - -### Function scoped struct declaration - -You can declare your request response structs inside a function body. -You must have to follow the naming convention `.. `. - -```go -package main - -// @Param request body main.MyHandler.request true "query params" -// @Success 200 {object} main.MyHandler.response -// @Router /test [post] -func MyHandler() { - type request struct { - RequestField string - } - - type response struct { - ResponseField string - } -} -``` - - ### Model composition in response ```go // JSONResult's data field will be overridden by the specific type proto.Order @@ -634,7 +534,7 @@ type Order struct { //in `proto` package @success 200 {object} jsonresult.JSONResult{data=[]string} "desc" ``` -- overriding multiple fields. field will be added if not exists +- overriding multiple fields. field will be added if not exists ```go @success 200 {object} jsonresult.JSONResult{data1=string,data2=[]string,data3=proto.Order,data4=[]proto.Order} "desc" ``` @@ -648,35 +548,21 @@ type DeepObject struct { //in `proto` package ### Add a headers in response ```go -// @Success 200 {string} string "ok" -// @failure 400 {string} string "error" -// @response default {string} string "other error" -// @Header 200 {string} Location "/entity/1" -// @Header 200,400,default {string} Token "token" -// @Header all {string} Token2 "token2" +// @Success 200 {string} string "ok" +// @Header 200 {string} Location "/entity/1" +// @Header 200 {string} Token "qwerty" ``` ### Use multiple path params ```go /// ... -// @Param group_id path int true "Group ID" +// @Param group_id path int true "Group ID" // @Param account_id path int true "Account ID" // ... // @Router /examples/groups/{group_id}/accounts/{account_id} [get] ``` -### Add multiple paths - -```go -/// ... -// @Param group_id path int true "Group ID" -// @Param user_id path int true "User ID" -// ... -// @Router /examples/groups/{group_id}/user/{user_id}/address [put] -// @Router /examples/user/{user_id}/address [put] -``` - ### Example value of struct ```go @@ -687,18 +573,9 @@ type Account struct { } ``` -### SchemaExample of body - -```go -// @Param email body string true "message/rfc822" SchemaExample(Subject: Testmail\r\n\r\nBody Message\r\n) -``` - ### Description of struct ```go -// Account model info -// @Description User account information -// @Description with user id and username type Account struct { // ID this is userid ID int `json:"id"` @@ -706,27 +583,6 @@ type Account struct { } ``` -[#708](https://github.com/swaggo/swag/issues/708) The parser handles only struct comments starting with `@Description` attribute. -But it writes all struct field comments as is. - -So, generated swagger doc as follows: -```json -"Account": { - "type":"object", - "description": "User account information with user id and username" - "properties": { - "id": { - "type": "integer", - "description": "ID this is userid" - }, - "name": { - "type":"string", - "description": "This is Name" - } - } -} -``` - ### Use swaggertype tag to supported custom type [#201](https://github.com/swaggo/swag/issues/201#issuecomment-475479409) @@ -791,40 +647,6 @@ generated swagger doc as follows: ``` -### Use global overrides to support a custom type - -If you are using generated files, the [`swaggertype`](#use-swaggertype-tag-to-supported-custom-type) or `swaggerignore` tags may not be possible. - -By passing a mapping to swag with `--overridesFile` you can tell swag to use one type in place of another wherever it appears. By default, if a `.swaggo` file is present in the current directory it will be used. - -Go code: -```go -type MyStruct struct { - ID sql.NullInt64 `json:"id"` - Name sql.NullString `json:"name"` -} -``` - -`.swaggo`: -``` -// Replace all NullInt64 with int -replace database/sql.NullInt64 int - -// Don't include any fields of type database/sql.NullString in the swagger docs -skip database/sql.NullString -``` - -Possible directives are comments (beginning with `//`), `replace path/to/a.type path/to/b.type`, and `skip path/to/a.type`. - -(Note that the full paths to any named types must be provided to prevent problems when multiple packages define a type with the same name) - -Rendered: -```go -"types.MyStruct": { - "id": "integer" -} -``` - ### Use swaggerignore tag to exclude a field @@ -840,7 +662,7 @@ type Account struct { ```go type Account struct { - ID string `json:"id" extensions:"x-nullable,x-abc=def,!x-omitempty"` // extensions fields must start with "x-" + ID string `json:"id" extensions:"x-nullable,x-abc=def"` // extensions fields must start with "x-" } ``` @@ -853,8 +675,7 @@ generate swagger doc as follows: "id": { "type": "string", "x-nullable": true, - "x-abc": "def", - "x-omitempty": false + "x-abc": "def" } } } @@ -867,7 +688,7 @@ type Resp struct { }//@name Response ``` -### How to use security annotations +### How to using security annotations General API info. @@ -893,59 +714,6 @@ Make it AND condition // @Security OAuth2Application[write, admin] ``` -Make it OR condition - -```go -// @Security ApiKeyAuth || firebase -// @Security OAuth2Application[write, admin] || APIKeyAuth -``` - - -### Add a description for enum items - -```go -type Example struct { - // Sort order: - // * asc - Ascending, from A to Z. - // * desc - Descending, from Z to A. - Order string `enums:"asc,desc"` -} -``` - -### Generate only specific docs file types - -By default `swag` command generates Swagger specification in three different files/file types: -- docs.go -- swagger.json -- swagger.yaml - -If you would like to limit a set of file types which should be generated you can use `--outputTypes` (short `-ot`) flag. Default value is `go,json,yaml` - output types separated with comma. To limit output only to `go` and `yaml` files, you would write `go,yaml`. With complete command that would be `swag init --outputTypes go,yaml`. - -### How to use Generics - -```go -// @Success 200 {object} web.GenericNestedResponse[types.Post] -// @Success 204 {object} web.GenericNestedResponse[types.Post, Types.AnotherOne] -// @Success 201 {object} web.GenericNestedResponse[web.GenericInnerType[types.Post]] -func GetPosts(w http.ResponseWriter, r *http.Request) { - _ = web.GenericNestedResponse[types.Post]{} -} -``` -See [this file](https://github.com/swaggo/swag/blob/master/testdata/generics_nested/api/api.go) for more details -and other examples. - -### Change the default Go Template action delimiters -[#980](https://github.com/swaggo/swag/issues/980) -[#1177](https://github.com/swaggo/swag/issues/1177) - -If your swagger annotations or struct fields contain "{{" or "}}", the template generation will most likely fail, as these are the default delimiters for [go templates](https://pkg.go.dev/text/template#Template.Delims). - -To make the generation work properly, you can change the default delimiters with `-td`. For example: -```console -swag init -g http/api.go -td "[[,]]" -``` -The new delimiter is a string with the format "``,``". - ## About the Project This project was inspired by [yvasiyarov/swagger](https://github.com/yvasiyarov/swagger) but we simplified the usage and added support a variety of [web frameworks](#supported-web-frameworks). Gopher image source is [tenntenn/gopher-stickers](https://github.com/tenntenn/gopher-stickers). It has licenses [creative commons licensing](http://creativecommons.org/licenses/by/3.0/deed.en). ## Contributors diff --git a/vendor/github.com/swaggo/swag/README_pt.md b/vendor/github.com/swaggo/swag/README_pt.md deleted file mode 100644 index 7f95066bba..0000000000 --- a/vendor/github.com/swaggo/swag/README_pt.md +++ /dev/null @@ -1,968 +0,0 @@ -# swag - -🌍 *[English](README.md) ∙ [简体中文](README_zh-CN.md) ∙ [Português](README_pt.md)* - - - -[![Build Status](https://github.com/swaggo/swag/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/features/actions) -[![Coverage Status](https://img.shields.io/codecov/c/github/swaggo/swag/master.svg)](https://codecov.io/gh/swaggo/swag) -[![Go Report Card](https://goreportcard.com/badge/github.com/swaggo/swag)](https://goreportcard.com/report/github.com/swaggo/swag) -[![codebeat badge](https://codebeat.co/badges/71e2f5e5-9e6b-405d-baf9-7cc8b5037330)](https://codebeat.co/projects/github-com-swaggo-swag-master) -[![Go Doc](https://godoc.org/github.com/swaggo/swagg?status.svg)](https://godoc.org/github.com/swaggo/swag) -[![Backers on Open Collective](https://opencollective.com/swag/backers/badge.svg)](#backers) -[![Sponsors on Open Collective](https://opencollective.com/swag/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fswaggo%2Fswag.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fswaggo%2Fswag?ref=badge_shield) -[![Release](https://img.shields.io/github/release/swaggo/swag.svg?style=flat-square)](https://github.com/swaggo/swag/releases) - -Swag converte anotações Go para Documentação Swagger 2.0. Criámos uma variedade de plugins para populares [Go web frameworks](#supported-web-frameworks). Isto permite uma integração rápida com um projecto Go existente (utilizando a Swagger UI). - -## Conteúdo -- [Começando](#começando) - - [Estruturas Web Suportadas](#estruturas-web-suportadas) - - [Como utilizá-lo com Gin](#como-como-ser-como-gin) - - [O formatador de swag](#a-formatação-de-swag) - - [Estado de Implementação](#implementação-estado) - - [Formato dos comentários declarativos](#formato-dos-comentarios-declarativos) - - [Informações Gerais API](#informações-gerais-api) - - [Operação API](#api-operacao) - - [Segurança](#seguranca) - - [Exemplos](#exemplos) - - [Descrições em múltiplas linhas](#descricoes-sobre-múltiplas-linhas) - - [Estrutura definida pelo utilizador com um tipo de matriz](#-estrutura-definida-pelo-utilizador-com-um-um-tipo) - - [Declaração de estruturação de funções](#function-scoped-struct-declaration) - - [Composição do modelo em resposta](#model-composição-em-resposta) - - [Adicionar um cabeçalho em resposta](#add-a-headers-in-response) - - [Utilizar parâmetros de caminhos múltiplos](#use-multiple-path-params) - - [Exemplo de valor de estrutura](#exemplo-do-valor-de-estrutura) - - [Schema Exemplo do corpo](#schemaexample-of-body) - - [Descrição da estrutura](#descrição-da-estrutura) - - [Usar etiqueta do tipo swaggertype para suportar o tipo personalizado](#use-swaggertype-tag-to-supported-custom-type) - - [Utilizar anulações globais para suportar um tipo personalizado](#use-global-overrides-to-support-a-custom-type) - - [Use swaggerignore tag para excluir um campo](#use-swaggerignore-tag-to-excluir-um-campo) - - [Adicionar informações de extensão ao campo de estruturação](#add-extension-info-to-struct-field) - - [Renomear modelo a expor](#renome-modelo-a-exibir) - - [Como utilizar as anotações de segurança](#como-utilizar-as-anotações-de-segurança) - - [Adicionar uma descrição para enumerar artigos](#add-a-description-for-enum-items) - - [Gerar apenas tipos de ficheiros de documentos específicos](#generate-only-specific-docs-file-file-types) - - [Como usar tipos genéricos](#como-usar-tipos-genéricos) -- [Sobre o projecto](#sobre-o-projecto) - -## Começando - -1. Adicione comentários ao código-fonte da API, consulte [Formato dos comentários declarativos](#declarative-comments-format). - -2. Descarregue o swag utilizando: -```sh -go install github.com/swaggo/swag/cmd/swag@latest -``` -Para construir a partir da fonte é necessário [Go](https://golang.org/dl/) (1.18 ou mais recente). - -Ou descarregar um binário pré-compilado a partir da [página de lançamento](https://github.com/swaggo/swag/releases). - -3. Executar `swag init` na pasta raiz do projecto que contém o ficheiro `main.go`. Isto irá analisar os seus comentários e gerar os ficheiros necessários (pasta `docs` e `docs/docs.go`). -```sh -swag init -``` - -Certifique-se de importar os `docs/docs.go` gerados para que a sua configuração específica fique "init" ed. Se as suas anotações API gerais não viverem em `main.go`, pode avisar a swag com a bandeira `-g`. -```sh -swag init -g http/api.go -``` - -4. (opcional) Utilizar o formato `swag fmt` no comentário SWAG. (Por favor, actualizar para a versão mais recente) - -```sh -swag fmt -``` - -## swag cli - -```sh -swag init -h -NOME: - swag init - Criar docs.go - -UTILIZAÇÃO: - swag init [opções de comando] [argumentos...] - -OPÇÕES: - --quiet, -q Fazer o logger ficar quiet (por padrão: falso) - --generalInfo valor, -g valor Go caminho do ficheiro em que 'swagger general API Info' está escrito (por padrão: "main.go") - --dir valor, -d valor Os directórios que deseja analisar, separados por vírgulas e de informação geral devem estar no primeiro (por padrão: "./") - --exclude valor Excluir directórios e ficheiros ao pesquisar, separados por vírgulas - -propertyStrategy da estratégia, -p valor da propriedadeEstratégia de nomeação de propriedades como snakecase,camelcase,pascalcase (por padrão: "camelcase") - --output de saída, -o valor directório de saída para todos os ficheiros gerados(swagger.json, swagger.yaml e docs.go) (por padrão: "./docs") - --outputTypes valor de saídaTypes, -- valor de saída Tipos de ficheiros gerados (docs.go, swagger.json, swagger.yaml) como go,json,yaml (por padrão: "go,json,yaml") - --parseVendor ParseVendor Parse go files na pasta 'vendor', desactivado por padrão (padrão: falso) - --parseInternal Parse go ficheiros em pacotes internos, desactivados por padrão (padrão: falso) - --generatedTime Gerar timestamp no topo dos docs.go, desactivado por padrão (padrão: falso) - --parteDepth value Dependência profundidade parse (por padrão: 100) - --templateDelims value, --td value fornecem delimitadores personalizados para a geração de modelos Go. O formato é leftDelim,rightDelim. Por exemplo: "[[,]]" - ... - - --help, -h mostrar ajuda (por padrão: falso) -``` - -```bash -swag fmt -h -NOME: - swag fmt - formato swag comentários - -UTILIZAÇÃO: - swag fmt [opções de comando] [argumentos...] - -OPÇÕES: - --dir valor, -d valor Os directórios que pretende analisar, separados por vírgulas e de informação geral devem estar no primeiro (por padrão: "./") - --excluir valor Excluir directórios e ficheiros ao pesquisar, separados por vírgulas - --generalInfo value, -g value Go file path in which 'swagger general API Info' is written (por padrão: "main.go") - --ajuda, -h mostrar ajuda (por padrão: falso) - -``` - -## Estruturas Web Suportadas - -- [gin](http://github.com/swaggo/gin-swagger) -- [echo](http://github.com/swaggo/echo-swagger) -- [buffalo](https://github.com/swaggo/buffalo-swagger) -- [net/http](https://github.com/swaggo/http-swagger) -- [gorilla/mux](https://github.com/swaggo/http-swagger) -- [go-chi/chi](https://github.com/swaggo/http-swagger) -- [flamingo](https://github.com/i-love-flamingo/swagger) -- [fiber](https://github.com/gofiber/swagger) -- [atreugo](https://github.com/Nerzal/atreugo-swagger) -- [hertz](https://github.com/hertz-contrib/swagger) - -## Como utilizá-lo com Gin - -Encontrar o código fonte de exemplo [aqui](https://github.com/swaggo/swag/tree/master/example/celler). - -1. Depois de utilizar `swag init` para gerar os documentos Swagger 2.0, importar os seguintes pacotes: -```go -import "github.com/swaggo/gin-swagger" // gin-swagger middleware -import "github.com/swaggo/files" // swagger embed files -``` - -2. Adicionar [Informações Gerais API](#general-api-info) anotações em código `main.go`: - - -```go -// @title Swagger Example API -// @version 1.0 -// @description This is a sample server celler 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 - -// @host localhost:8080 -// @BasePath /api/v1 - -// @securityDefinitions.basic BasicAuth - -// @externalDocs.description OpenAPI -// @externalDocs.url https://swagger.io/resources/open-api/ -func main() { - r := gin.Default() - - c := controller.NewController() - - v1 := r.Group("/api/v1") - { - accounts := v1.Group("/accounts") - { - accounts.GET(":id", c.ShowAccount) - accounts.GET("", c.ListAccounts) - accounts.POST("", c.AddAccount) - accounts.DELETE(":id", c.DeleteAccount) - accounts.PATCH(":id", c.UpdateAccount) - accounts.POST(":id/images", c.UploadAccountImage) - } - //... - } - r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - r.Run(":8080") -} -//... -``` - -Além disso, algumas informações API gerais podem ser definidas de forma dinâmica. O pacote de código gerado `docs` exporta a variável `SwaggerInfo` que podemos utilizar para definir programticamente o título, descrição, versão, hospedeiro e caminho base. Exemplo utilizando Gin: - -```go -package main - -import ( - "github.com/gin-gonic/gin" - "github.com/swaggo/files" - "github.com/swaggo/gin-swagger" - - "./docs" // docs is generated by Swag CLI, you have to import it. -) - -// @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 -func main() { - - // programmatically set swagger info - docs.SwaggerInfo.Title = "Swagger Example API" - docs.SwaggerInfo.Description = "This is a sample server Petstore server." - docs.SwaggerInfo.Version = "1.0" - docs.SwaggerInfo.Host = "petstore.swagger.io" - docs.SwaggerInfo.BasePath = "/v2" - docs.SwaggerInfo.Schemes = []string{"http", "https"} - - r := gin.New() - - // use ginSwagger middleware to serve the API docs - r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - - r.Run() -} -``` - -3. Adicionar [Operação API](#api-operacao) anotações em código `controller` - -```go -package controller - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - "github.com/swaggo/swag/example/celler/httputil" - "github.com/swaggo/swag/example/celler/model" -) - -// ShowAccount godoc -// @Summary Show an account -// @Description get string by ID -// @Tags accounts -// @Accept json -// @Produce json -// @Param id path int true "Account ID" -// @Success 200 {object} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts/{id} [get] -func (c *Controller) ShowAccount(ctx *gin.Context) { - id := ctx.Param("id") - aid, err := strconv.Atoi(id) - if err != nil { - httputil.NewError(ctx, http.StatusBadRequest, err) - return - } - account, err := model.AccountOne(aid) - if err != nil { - httputil.NewError(ctx, http.StatusNotFound, err) - return - } - ctx.JSON(http.StatusOK, account) -} - -// ListAccounts godoc -// @Summary List accounts -// @Description get accounts -// @Tags accounts -// @Accept json -// @Produce json -// @Param q query string false "name search by q" Format(email) -// @Success 200 {array} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts [get] -func (c *Controller) ListAccounts(ctx *gin.Context) { - q := ctx.Request.URL.Query().Get("q") - accounts, err := model.AccountsAll(q) - if err != nil { - httputil.NewError(ctx, http.StatusNotFound, err) - return - } - ctx.JSON(http.StatusOK, accounts) -} -//... -``` - -```console -swag init -``` - -4. Execute a sua aplicação, e navegue para http://localhost:8080/swagger/index.html. Verá os documentos Swagger 2.0 Api, como mostrado abaixo: - -![swagger_index.html](https://raw.githubusercontent.com/swaggo/swag/master/assets/swagger-image.png) - -## O formatador de swag - -Os Swag Comments podem ser formatados automaticamente, assim como 'go fmt'. -Encontre o resultado da formatação [aqui](https://github.com/swaggo/swag/tree/master/example/celler). - -Usage: -```shell -swag fmt -``` - -Exclude folder: -```shell -swag fmt -d ./ --exclude ./internal -``` - -Ao utilizar `swag fmt`, é necessário assegurar-se de que tem um comentário doc para a função a fim de assegurar uma formatação correcta. -Isto deve-se ao `swag fmt` que traça comentários swag com separadores, o que só é permitido *após* um comentário doc padrão. - -Por exemplo, utilizar - -```go -// ListAccounts lists all existing accounts -// -// @Summary List accounts -// @Description get accounts -// @Tags accounts -// @Accept json -// @Produce json -// @Param q query string false "name search by q" Format(email) -// @Success 200 {array} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts [get] -func (c *Controller) ListAccounts(ctx *gin.Context) { -``` - -## Estado de Implementação - -[Documento Swagger 2.0](https://swagger.io/docs/specification/2-0/basic-structure/) - -- [x] Estrutura básica -- [x] Hospedeiro API e Caminho Base -- [x] Caminhos e operações -- [x] Descrição dos parâmetros -- [x] Descrever o corpo do pedido -- [x] Descrição das respostas -- [x] Tipos MIME -- [x] Autenticação - - [x] Autenticação básica - - [x] Chaves API -- [x] Acrescentar exemplos -- [x] Carregamento de ficheiros -- [x] Enums -- [x] Operações de Agrupamento com Etiquetas -- Extensões Swagger - -## Formato dos comentários declarativos - -## Informações Gerais API - -**Exemplo** -[celler/main.go](https://github.com/swaggo/swag/blob/master/example/celler/main.go) - -| anotação | descrição | exemplo | -|-------------|--------------------------------------------|---------------------------------| -| title | **Obrigatório.** O título da aplicação.| // @title Swagger Example API | -| version | **Obrigatório.** Fornece a versão da aplicação API.| // @version 1.0 | -| description | Uma breve descrição da candidatura. |// @descrição Este é um servidor servidor de celas de amostra. | -| tag.name | Nome de uma tag.| // @tag.name Este é o nome da tag | -| tag.description | Descrição da tag | // @tag.description Cool Description | -| tag.docs.url | Url da Documentação externa da tag | // @tag.docs.url https://example.com| -| tag.docs.description | Descrição da documentação externa da tag| // @tag.docs.description Melhor exemplo de documentação | -| TermsOfService | Os Termos de Serviço para o API.| // @termsOfService http://swagger.io/terms/ | -| contact.name | A informação de contacto para a API exposta.| // @contacto.name Suporte API | -| contact.url | O URL que aponta para as informações de contacto. DEVE estar no formato de um URL. | // @contact.url http://www.swagger.io/support| -| contact.email| O endereço de email da pessoa/organização de contacto. DEVE estar no formato de um endereço de correio electrónico.| // @contact.email support@swagger.io | -| license.name | **Obrigatório.** O nome da licença utilizada para a API.|// @licença.name Apache 2.0| -| license.url | Um URL para a licença utilizada para a API. DEVE estar no formato de um URL. | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html | -| host | O anfitrião (nome ou ip) que serve o API. | // @host localhost:8080 | -| BasePath | O caminho de base sobre o qual o API é servido. | // @BasePath /api/v1 | -| accept | Uma lista de tipos de MIME que os APIs podem consumir. Note que accept só afecta operações com um organismo de pedido, tais como POST, PUT e PATCH. O valor DEVE ser o descrito em [Tipos de Mime](#mime-types). | // @accept json | -| produce | Uma lista de tipos de MIME que os APIs podem produce. O valor DEVE ser o descrito em [Tipos de Mime](#mime-types). | // @produce json | -| query.collection.format | O formato padrão de param de colecção(array) em query,enums:csv,multi,pipes,tsv,ssv. Se não definido, csv é o padrão.| // @query.collection.format multi -| schemes | O protocolo de transferência para a operação que separou por espaços. | // @schemes http https | -| externalDocs.description | Descrição do documento externo. | // @externalDocs.description OpenAPI | -| externalDocs.url | URL do documento externo. | // @externalDocs.url https://swagger.io/resources/open-api/ | -| x-name | A chave de extensão, deve ser iniciada por x- e tomar apenas o valor json | // @x-example-key {"chave": "valor"} | - -### Usando descrições de remarcação para baixo -Quando uma pequena sequência na sua documentação é insuficiente, ou precisa de imagens, exemplos de códigos e coisas do género, pode querer usar descrições de marcação. Para utilizar as descrições markdown, utilize as seguintes anotações. - -| anotação | descrição | exemplo | -|-------------|--------------------------------------------|---------------------------------| -| title | **Obrigatório.** O título da aplicação.| // @title Swagger Example API | -| version | **Obrigatório.** Fornece a versão da aplicação API.| // @versão 1.0 | -| description.markdown | Uma breve descrição da candidatura. Parsed a partir do ficheiro api.md. Esta é uma alternativa a @description |// @description.markdown Sem valor necessário, isto analisa a descrição do ficheiro api.md |. -| tag.name | Nome de uma tag.| // @tag.name Este é o nome da tag | -| tag.description.markdown | Descrição da tag esta é uma alternativa à tag.description. A descrição será lida a partir de um ficheiro nomeado como tagname.md | // @tag.description.markdown | - -## Operação API - -**Exemplo** -[celler/controller](https://github.com/swaggo/swag/tree/master/example/celler/controller) - -| anotação | descrição | -|-------------|----------------------------------------------------------------------------------------------------------------------------| -| descrição | Uma explicação verbosa do comportamento da operação. | -| description.markdown | Uma breve descrição da candidatura. A descrição será lida a partir de um ficheiro. Por exemplo, `@description.markdown details` irá carregar `details.md`| // @description.file endpoint.description.markdown | -| id | Um fio único utilizado para identificar a operação. Deve ser única entre todas as operações API. | -| tags | Uma lista de tags para cada operação API que separou por vírgulas. | -| summary | Um breve resumo do que a operação faz. | -| accept | Uma lista de tipos de MIME que os APIs podem consumir. Note que accept só afecta operações com um organismo de pedido, tais como POST, PUT e PATCH. O valor DEVE ser o descrito em [Tipos de Mime](#mime-types). | -| produce | Uma lista de tipos de MIME que os APIs podem produce. O valor DEVE ser o descrito em [Tipos de Mime](#mime-types). | -| param | Parâmetros que se separaram por espaços. `param name`,`param type`,`data type`,`is mandatory?`,`comment` `attribute(optional)` | -| security | [Segurança](#security) para cada operação API. | -| success | resposta de sucesso que separou por espaços. `return code or default`,`{param type}`,`data type`,`comment` |. -| failure | Resposta de falha que separou por espaços. `return code or default`,`{param type}`,`data type`,`comment` | -| response | Igual ao `sucesso` e `falha` | -| header | Cabeçalho em resposta que separou por espaços. `código de retorno`,`{tipo de parâmetro}`,`tipo de dados`,`comentário` |. -| router | Definição do caminho que separou por espaços. caminho",`path`,`[httpMethod]` |[httpMethod]` | -| x-name | A chave de extensão, deve ser iniciada por x- e tomar apenas o valor json. | -| x-codeSample | Optional Markdown use. tomar `file` como parâmetro. Isto irá então procurar um ficheiro nomeado como o resumo na pasta dada. | -| deprecated | Marcar o ponto final como depreciado. | - -## Mime Types - -`swag` aceita todos os tipos MIME que estão no formato correcto, ou seja, correspondem `*/*`. -Além disso, `swag` também aceita pseudónimos para alguns tipos de MIME, como se segue: - - -| Alias | MIME Type | -|-----------------------|-----------------------------------| -| json | application/json | -| xml | text/xml | -| plain | text/plain | -| html | text/html | -| mpfd | multipart/form-data | -| x-www-form-urlencoded | application/x-www-form-urlencoded | -| json-api | application/vnd.api+json | -| json-stream | application/x-json-stream | -| octet-stream | application/octet-stream | -| png | image/png | -| jpeg | image/jpeg | -| gif | image/gif | - - - -## Tipo de parâmetro - -- query -- path -- header -- body -- formData - -## Tipo de dados - -- string (string) -- integer (int, uint, uint32, uint64) -- number (float32) -- boolean (bool) -- file (param data type when uploading) -- user defined struct - -## Segurança -| anotação | descrição | parâmetros | exemplo | -|------------|-------------|------------|---------| -| securitydefinitions.basic | [Basic](https://swagger.io/docs/specification/2-0/authentication/basic-authentication/) auth. | | // @securityDefinitions.basicAuth | [Básico]() -| securitydefinitions.apikey | [chave API](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, description | // @securityDefinitions.apikey ApiKeyAuth | -| securitydefinitions.oauth2.application | [Aplicação OAuth2](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, description | // @securitydefinitions.oauth2.application OAuth2Application | -| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | [OAuth2Implicit]() -| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, description | // @securitydefinitions.oauth2.password OAuth2Password | -| securitydefinitions.oauth2.accessCode | [código de acesso OAuth2](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | [código de acesso OAuth2.accessCode]() - - -| anotação de parâmetros | exemplo | -|---------------------------------|-------------------------------------------------------------------------| -| in | // @in header | -| name | // @name Authorization | -| tokenUrl | // @tokenUrl https://example.com/oauth/token | -| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | -| scope.hoge | // @scope.write Grants write access | -| description | // @descrição OAuth protege os pontos finais da nossa entidade | - -## Atributo - -```go -// @Param enumstring query string false "string enums" Enums(A, B, C) -// @Param enumint query int false "int enums" Enums(1, 2, 3) -// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3) -// @Param string query string false "string valid" minlength(5) maxlength(10) -// @Param int query int false "int valid" minimum(1) maximum(10) -// @Param default query string false "string default" default(A) -// @Param example query string false "string example" example(string) -// @Param collection query []string false "string collection" collectionFormat(multi) -// @Param extensions query []string false "string collection" extensions(x-example=test,x-nullable) -``` - -It also works for the struct fields: - -```go -type Foo struct { - Bar string `minLength:"4" maxLength:"16" example:"random string"` - Baz int `minimum:"10" maximum:"20" default:"15"` - Qux []string `enums:"foo,bar,baz"` -} -``` - -### Disponível - -Nome do campo | Tipo | Descrição ----|:---:|--- -validate | `string` | Determina a validação para o parâmetro. Os valores possíveis são: `required,optional`. -default | * | Declara o valor do parâmetro que o servidor utilizará se nenhum for fornecido, por exemplo, uma "contagem" para controlar o número de resultados por página poderá ser por defeito de 100 se não for fornecido pelo cliente no pedido. (Nota: "por defeito" não tem significado para os parâmetros requeridos). -See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. Ao contrário do esquema JSON, este valor DEVE estar em conformidade com o definido [`type`](#parameterType) para este parâmetro. -maximum | `number` | Ver https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.2. -minimum | `number` | Ver https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.3. -multipleOf | `number` | Ver https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.1. -maxLength | `integer` | Ver https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.1. -minLength | `integer` | Ver https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.2. -enums | [\*] | Ver https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1. -format | `string` | O formato de extensão para o anteriormente mencionado [`type`](#parameterType). Ver [Data Type Formats](https://swagger.io/specification/v2/#dataTypeFormat) para mais detalhes. -collectionFormat | `string` |Determina o formato da matriz se for utilizada uma matriz de tipos. Os valores possíveis são:
  • `csv` - valores separados por vírgulas `foo,bar`.
  • `ssv` - valores separados por espaço `foo bar`.
  • `tsv` - valores separados por tabulação `foo\tbar`.
  • `pipes` - valores separados por tubo foo|bar.
  • `multi` - corresponde a múltiplas instâncias de parâmetros em vez de múltiplos valores para uma única instância `foo=bar&foo=baz`. This is valid only for parameters [`in`](#parameterIn) "query" or "formData".
Default value is `csv`. -example | * | Declara o exemplo para o valor do parâmetro -extensions | `string` | Acrescentar extensão aos parâmetros. - -### Futuro - -Nome do campo | Tipo | Description ----|:---:|--- -pattern | `string` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3. -maxItems | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.2. -minItems | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.3. -uniqueItems | `boolean` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.4. - -## Exemplos - - -### Descrições em múltiplas linhas - -É possível acrescentar descrições que abranjam várias linhas tanto na descrição geral da api como em definições de rotas como esta: - -```go -// @description This is the first line -// @description This is the second line -// @description And so forth. -``` - -### Estrutura definida pelo utilizador com um tipo de matriz - -```go -// @Success 200 {array} model.Account <-- This is a user defined struct. -``` - -```go -package model - -type Account struct { - ID int `json:"id" example:"1"` - Name string `json:"name" example:"account name"` -} -``` - - -### Declaração de estruturação de funções - -Pode declarar as estruturas de resposta do seu pedido dentro de um corpo funcional. -Deve ter de seguir a convenção de nomeação -`.. `. - -```go -package main - -// @Param request body main.MyHandler.request true "query params" -// @Success 200 {object} main.MyHandler.response -// @Router /test [post] -func MyHandler() { - type request struct { - RequestField string - } - - type response struct { - ResponseField string - } -} -``` - - -### Composição do modelo em resposta -```go -// JSONResult's data field will be overridden by the specific type proto.Order -@success 200 {object} jsonresult.JSONResult{data=proto.Order} "desc" -``` - -```go -type JSONResult struct { - Code int `json:"code" ` - Message string `json:"message"` - Data interface{} `json:"data"` -} - -type Order struct { //in `proto` package - Id uint `json:"id"` - Data interface{} `json:"data"` -} -``` - -- também suportam uma variedade de objectos e tipos primitivos como resposta aninhada -```go -@success 200 {object} jsonresult.JSONResult{data=[]proto.Order} "desc" -@success 200 {object} jsonresult.JSONResult{data=string} "desc" -@success 200 {object} jsonresult.JSONResult{data=[]string} "desc" -``` - -- campos múltiplos que se sobrepõem. campo será adicionado se não existir -```go -@success 200 {object} jsonresult.JSONResult{data1=string,data2=[]string,data3=proto.Order,data4=[]proto.Order} "desc" -``` -- overriding deep-level fields -```go -type DeepObject struct { //in `proto` package - ... -} -@success 200 {object} jsonresult.JSONResult{data1=proto.Order{data=proto.DeepObject},data2=[]proto.Order{data=[]proto.DeepObject}} "desc" -``` - -### Adicionar um cabeçalho em resposta - -```go -// @Success 200 {string} string "ok" -// @failure 400 {string} string "error" -// @response default {string} string "other error" -// @Header 200 {string} Location "/entity/1" -// @Header 200,400,default {string} Token "token" -// @Header all {string} Token2 "token2" -``` - - -### Utilizar parâmetros de caminhos múltiplos - -```go -/// ... -// @Param group_id path int true "Group ID" -// @Param account_id path int true "Account ID" -// ... -// @Router /examples/groups/{group_id}/accounts/{account_id} [get] -``` - -### Adicionar múltiplos caminhos - -```go -/// ... -// @Param group_id path int true "Group ID" -// @Param user_id path int true "User ID" -// ... -// @Router /examples/groups/{group_id}/user/{user_id}/address [put] -// @Router /examples/user/{user_id}/address [put] -``` - -### Exemplo de valor de estrutura - -```go -type Account struct { - ID int `json:"id" example:"1"` - Name string `json:"name" example:"account name"` - PhotoUrls []string `json:"photo_urls" example:"http://test/image/1.jpg,http://test/image/2.jpg"` -} -``` - -### Schema Exemplo do corpo - -```go -// @Param email body string true "message/rfc822" SchemaExample(Subject: Testmail\r\n\r\nBody Message\r\n) -``` - -### Descrição da estrutura - -```go -// Account model info -// @Description User account information -// @Description with user id and username -type Account struct { - // ID this is userid - ID int `json:"id"` - Name string `json:"name"` // This is Name -} -``` - -[#708](https://github.com/swaggo/swag/issues/708) O analisador trata apenas de comentários estruturais a partir de `@Description` attribute. - -Assim, gerou o doc. de swagger como se segue: -```json -"Account": { - "type":"object", - "description": "User account information with user id and username" - "properties": { - "id": { - "type": "integer", - "description": "ID this is userid" - }, - "name": { - "type":"string", - "description": "This is Name" - } - } -} -``` - -### Usar etiqueta do tipo swaggertype para suportar o tipo personalizado -[#201](https://github.com/swaggo/swag/issues/201#issuecomment-475479409) - -```go -type TimestampTime struct { - time.Time -} - -///implement encoding.JSON.Marshaler interface -func (t *TimestampTime) MarshalJSON() ([]byte, error) { - bin := make([]byte, 16) - bin = strconv.AppendInt(bin[:0], t.Time.Unix(), 10) - return bin, nil -} - -func (t *TimestampTime) UnmarshalJSON(bin []byte) error { - v, err := strconv.ParseInt(string(bin), 10, 64) - if err != nil { - return err - } - t.Time = time.Unix(v, 0) - return nil -} -/// - -type Account struct { - // Override primitive type by simply specifying it via `swaggertype` tag - ID sql.NullInt64 `json:"id" swaggertype:"integer"` - - // Override struct type to a primitive type 'integer' by specifying it via `swaggertype` tag - RegisterTime TimestampTime `json:"register_time" swaggertype:"primitive,integer"` - - // Array types can be overridden using "array," format - Coeffs []big.Float `json:"coeffs" swaggertype:"array,number"` -} -``` - -[#379](https://github.com/swaggo/swag/issues/379) -```go -type CerticateKeyPair struct { - Crt []byte `json:"crt" swaggertype:"string" format:"base64" example:"U3dhZ2dlciByb2Nrcw=="` - Key []byte `json:"key" swaggertype:"string" format:"base64" example:"U3dhZ2dlciByb2Nrcw=="` -} -``` -generated swagger doc as follows: -```go -"api.MyBinding": { - "type":"object", - "properties":{ - "crt":{ - "type":"string", - "format":"base64", - "example":"U3dhZ2dlciByb2Nrcw==" - }, - "key":{ - "type":"string", - "format":"base64", - "example":"U3dhZ2dlciByb2Nrcw==" - } - } -} - -``` - -### Utilizar anulações globais para suportar um tipo personalizado - -Se estiver a utilizar ficheiros gerados, as etiquetas [`swaggertype`](#use-swaggertype-tag-to-supported-custom-type) ou `swaggerignore` podem não ser possíveis. - -Ao passar um mapeamento para swag com `--overridesFile` pode dizer swag para utilizar um tipo no lugar de outro onde quer que apareça. Por defeito, se um ficheiro `.swaggo` estiver presente no directório actual, será utilizado. - -Go code: -```go -type MyStruct struct { - ID sql.NullInt64 `json:"id"` - Name sql.NullString `json:"name"` -} -``` - -`.swaggo`: -``` -// Substituir todos os NullInt64 por int -replace database/sql.NullInt64 int - -// Não inclua quaisquer campos do tipo base de database/sql. -NullString no swagger docs -skip database/sql.NullString -``` - -As directivas possíveis são comentários (começando por `//`), `replace path/to/a.type path/to/b.type`, e `skip path/to/a.type`. - -(Note que os caminhos completos para qualquer tipo nomeado devem ser fornecidos para evitar problemas quando vários pacotes definem um tipo com o mesmo nome) - -Entregue em: -```go -"types.MyStruct": { - "id": "integer" -} - -### Use swaggerignore tag para excluir um campo - -```go -type Account struct { - ID string `json:"id"` - Name string `json:"name"` - Ignored int `swaggerignore:"true"` -} -``` - - -### Adicionar informações de extensão ao campo de estruturação - -```go -type Account struct { - ID string `json:"id" extensions:"x-nullable,x-abc=def,!x-omitempty"` // extensions fields must start with "x-" -} -``` - -gerar doc. de swagger como se segue: - -```go -"Account": { - "type": "object", - "properties": { - "id": { - "type": "string", - "x-nullable": true, - "x-abc": "def", - "x-omitempty": false - } - } -} -``` - - -### Renomear modelo a expor - -```golang -type Resp struct { - Code int -}//@name Response -``` - -### Como utilizar as anotações de segurança - -Informações API gerais. - -```go -// @securityDefinitions.basic BasicAuth - -// @securitydefinitions.oauth2.application OAuth2Application -// @tokenUrl https://example.com/oauth/token -// @scope.write Grants write access -// @scope.admin Grants read and write access to administrative information -``` - -Cada operação API. - -```go -// @Security ApiKeyAuth -``` - -Faça-o AND condicione-o - -```go -// @Security ApiKeyAuth -// @Security OAuth2Application[write, admin] -``` - -Faça-o OR condição - -```go -// @Security ApiKeyAuth || firebase -// @Security OAuth2Application[write, admin] || APIKeyAuth -``` - - - -### Adicionar uma descrição para enumerar artigos - -```go -type Example struct { - // Sort order: - // * asc - Ascending, from A to Z. - // * desc - Descending, from Z to A. - Order string `enums:"asc,desc"` -} -``` - -### Gerar apenas tipos de ficheiros de documentos específicos - -Por defeito, o comando `swag` gera especificação Swagger em três tipos diferentes de ficheiros/arquivos: -- docs.go -- swagger.json -- swagger.yaml - -Se desejar limitar um conjunto de tipos de ficheiros que devem ser gerados pode utilizar a bandeira `--outputTypes` (short `-ot`). O valor por defeito é `go,json,yaml` - tipos de saída separados por vírgula. Para limitar a saída apenas a ficheiros `go` e `yaml`, escrever-se-ia `go,yaml'. Com comando completo que seria `swag init --outputTypes go,yaml`. - -### Como usar tipos genéricos - -```go -// @Success 200 {object} web.GenericNestedResponse[types.Post] -// @Success 204 {object} web.GenericNestedResponse[types.Post, Types.AnotherOne] -// @Success 201 {object} web.GenericNestedResponse[web.GenericInnerType[types.Post]] -func GetPosts(w http.ResponseWriter, r *http.Request) { - _ = web.GenericNestedResponse[types.Post]{} -} -``` -Para mais detalhes e outros exemplos, veja [esse arquivo](https://github.com/swaggo/swag/blob/master/testdata/generics_nested/api/api.go) - -### Alterar os delimitadores de acção padrão Go Template -[#980](https://github.com/swaggo/swag/issues/980) -[#1177](https://github.com/swaggo/swag/issues/1177) - -Se as suas anotações ou campos estruturantes contêm "{{" or "}}", a geração de modelos irá muito provavelmente falhar, uma vez que estes são os delimitadores por defeito para [go templates](https://pkg.go.dev/text/template#Template.Delims). - -Para que a geração funcione correctamente, pode alterar os delimitadores por defeito com `-td'. Por exemplo: -``console -swag init -g http/api.go -td "[[,]" -``` - -O novo delimitador é um fio com o formato "``,``". - -## Sobre o projecto -Este projecto foi inspirado por [yvasiyarov/swagger](https://github.com/yvasiyarov/swagger) mas simplificámos a utilização e acrescentámos apoio a uma variedade de [frameworks web](#estruturas-web-suportadas). A fonte de imagem Gopher é [tenntenn/gopher-stickers](https://github.com/tenntenn/gopher-stickers). Tem licenças [creative commons licensing](http://creativecommons.org/licenses/by/3.0/deed.en). - -## Contribuidores - -Este projecto existe graças a todas as pessoas que contribuem. [[Contribute](CONTRIBUTING.md)]. - - - -## Apoios - -Obrigado a todos os nossos apoiantes! 🙏 [[Become a backer](https://opencollective.com/swag#backer)] - - - - -## Patrocinadores - -Apoiar este projecto tornando-se um patrocinador. O seu logótipo aparecerá aqui com um link para o seu website. [[Become a sponsor](https://opencollective.com/swag#sponsor)] - - - - - - - - - - - - - - -## Licença -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fswaggo%2Fswag.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fswaggo%2Fswag?ref=badge_large) diff --git a/vendor/github.com/swaggo/swag/README_zh-CN.md b/vendor/github.com/swaggo/swag/README_zh-CN.md index 87d600b522..d14f49851a 100644 --- a/vendor/github.com/swaggo/swag/README_zh-CN.md +++ b/vendor/github.com/swaggo/swag/README_zh-CN.md @@ -20,7 +20,6 @@ Swag将Go的注释转换为Swagger2.0文档。我们为流行的 [Go Web Framewo - [快速开始](#快速开始) - [支持的Web框架](#支持的web框架) - [如何与Gin集成](#如何与gin集成) -- [格式化说明](#格式化说明) - [开发现状](#开发现状) - [声明式注释格式](#声明式注释格式) - [通用API信息](#通用api信息) @@ -47,10 +46,10 @@ Swag将Go的注释转换为Swagger2.0文档。我们为流行的 [Go Web Framewo 2. 使用如下命令下载swag: ```bash -go install github.com/swaggo/swag/cmd/swag@latest +go get -u github.com/swaggo/swag/cmd/swag ``` -从源码开始构建的话,需要有Go环境(1.18及以上版本)。 +从源码开始构建的话,需要有Go环境(1.9及以上版本)。 或者从github的release页面下载预编译好的二进制文件。 @@ -60,18 +59,12 @@ go install github.com/swaggo/swag/cmd/swag@latest swag init ``` -确保导入了生成的`docs/docs.go`文件,这样特定的配置文件才会被初始化。如果通用API注释没有写在`main.go`中,可以使用`-g`标识符来告知swag。 +确保导入了生成的`docs/docs.go`文件,这样特定的配置文件才会被初始化。如果通用API指数没有写在`main.go`中,可以使用`-g`标识符来告知swag。 ```bash swag init -g http/api.go ``` -4. (可选) 使用`fmt`格式化 SWAG 注释。(请先升级到最新版本) - -```bash -swag fmt -``` - ## swag cli ```bash @@ -83,35 +76,14 @@ USAGE: swag init [command options] [arguments...] OPTIONS: - --generalInfo value, -g value API通用信息所在的go源文件路径,如果是相对路径则基于API解析目录 (默认: "main.go") - --dir value, -d value API解析目录 (默认: "./") - --exclude value 解析扫描时排除的目录,多个目录可用逗号分隔(默认:空) - --propertyStrategy value, -p value 结构体字段命名规则,三种:snakecase,camelcase,pascalcase (默认: "camelcase") - --output value, -o value 文件(swagger.json, swagger.yaml and doc.go)输出目录 (默认: "./docs") - --parseVendor 是否解析vendor目录里的go源文件,默认不 - --parseDependency 是否解析依赖目录中的go源文件,默认不 - --markdownFiles value, --md value 指定API的描述信息所使用的markdown文件所在的目录 - --generatedTime 是否输出时间到输出文件docs.go的顶部,默认是 - --codeExampleFiles value, --cef value 解析包含用于 x-codeSamples 扩展的代码示例文件的文件夹,默认禁用 - --parseInternal 解析 internal 包中的go文件,默认禁用 - --parseDepth value 依赖解析深度 (默认: 100) - --instanceName value 设置文档实例名 (默认: "swagger") -``` - -```bash -swag fmt -h -NAME: - swag fmt - format swag comments - -USAGE: - swag fmt [command options] [arguments...] - -OPTIONS: - --dir value, -d value API解析目录 (默认: "./") - --exclude value 解析扫描时排除的目录,多个目录可用逗号分隔(默认:空) - --generalInfo value, -g value API通用信息所在的go源文件路径,如果是相对路径则基于API解析目录 (默认: "main.go") - --help, -h show help (default: false) - + --generalInfo value, -g value API通用信息所在的go源文件路径,如果是相对路径则基于API解析目录 (默认: "main.go") + --dir value, -d value API解析目录 (默认: "./") + --propertyStrategy value, -p value 结构体字段命名规则,三种:snakecase,camelcase,pascalcase (默认: "camelcase") + --output value, -o value 文件(swagger.json, swagger.yaml and doc.go)输出目录 (默认: "./docs") + --parseVendor 是否解析vendor目录里的go源文件,默认不 + --parseDependency 是否解析依赖目录中的go源文件,默认不 + --markdownFiles value, --md value 指定API的描述信息所使用的markdown文件所在的目录 + --generatedTime 是否输出时间到输出文件docs.go的顶部,默认是 ``` ## 支持的Web框架 @@ -120,12 +92,6 @@ OPTIONS: - [echo](http://github.com/swaggo/echo-swagger) - [buffalo](https://github.com/swaggo/buffalo-swagger) - [net/http](https://github.com/swaggo/http-swagger) -- [gorilla/mux](https://github.com/swaggo/http-swagger) -- [go-chi/chi](https://github.com/swaggo/http-swagger) -- [flamingo](https://github.com/i-love-flamingo/swagger) -- [fiber](https://github.com/gofiber/swagger) -- [atreugo](https://github.com/Nerzal/atreugo-swagger) -- [hertz](https://github.com/hertz-contrib/swagger) ## 如何与Gin集成 @@ -140,26 +106,52 @@ import "github.com/swaggo/files" // swagger embed files 2. 在`main.go`源代码中添加通用的API注释: -```go -// @title Swagger Example API -// @version 1.0 -// @description This is a sample server celler server. -// @termsOfService http://swagger.io/terms/ +```bash +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server celler server. +// @termsOfService http://swagger.io/terms/ -// @contact.name API Support -// @contact.url http://www.swagger.io/support -// @contact.email support@swagger.io +// @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 +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html -// @host localhost:8080 -// @BasePath /api/v1 +// @host localhost:8080 +// @BasePath /api/v1 +// @query.collection.format multi + +// @securityDefinitions.basic BasicAuth -// @securityDefinitions.basic BasicAuth +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name Authorization + +// @securitydefinitions.oauth2.application OAuth2Application +// @tokenUrl https://example.com/oauth/token +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information + +// @securitydefinitions.oauth2.implicit OAuth2Implicit +// @authorizationurl https://example.com/oauth/authorize +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information + +// @securitydefinitions.oauth2.password OAuth2Password +// @tokenUrl https://example.com/oauth/token +// @scope.read Grants read access +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information + +// @securitydefinitions.oauth2.accessCode OAuth2AccessCode +// @tokenUrl https://example.com/oauth/token +// @authorizationurl https://example.com/oauth/authorize +// @scope.admin Grants read and write access to administrative information + +// @x-extension-openapi {"example": "value on a json format"} -// @externalDocs.description OpenAPI -// @externalDocs.url https://swagger.io/resources/open-api/ func main() { r := gin.Default() @@ -197,12 +189,15 @@ import ( "./docs" // docs is generated by Swag CLI, you have to import it. ) -// @contact.name API Support -// @contact.url http://www.swagger.io/support -// @contact.email support@swagger.io +// @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 + +// @termsOfService http://swagger.io/terms/ -// @license.name Apache 2.0 -// @license.url http://www.apache.org/licenses/LICENSE-2.0.html func main() { // programatically set swagger info @@ -238,53 +233,55 @@ import ( ) // ShowAccount godoc -// @Summary Show an account -// @Description get string by ID -// @Tags accounts -// @Accept json -// @Produce json -// @Param id path int true "Account ID" -// @Success 200 {object} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts/{id} [get] +// @Summary Show a account +// @Description get string by ID +// @ID get-string-by-int +// @Accept json +// @Produce json +// @Param id path int true "Account ID" +// @Success 200 {object} model.Account +// @Header 200 {string} Token "qwerty" +// @Failure 400 {object} httputil.HTTPError +// @Failure 404 {object} httputil.HTTPError +// @Failure 500 {object} httputil.HTTPError +// @Router /accounts/{id} [get] func (c *Controller) ShowAccount(ctx *gin.Context) { - id := ctx.Param("id") - aid, err := strconv.Atoi(id) - if err != nil { - httputil.NewError(ctx, http.StatusBadRequest, err) - return - } - account, err := model.AccountOne(aid) - if err != nil { - httputil.NewError(ctx, http.StatusNotFound, err) - return - } - ctx.JSON(http.StatusOK, account) + id := ctx.Param("id") + aid, err := strconv.Atoi(id) + if err != nil { + httputil.NewError(ctx, http.StatusBadRequest, err) + return + } + account, err := model.AccountOne(aid) + if err != nil { + httputil.NewError(ctx, http.StatusNotFound, err) + return + } + ctx.JSON(http.StatusOK, account) } // ListAccounts godoc -// @Summary List accounts -// @Description get accounts -// @Tags accounts -// @Accept json -// @Produce json -// @Param q query string false "name search by q" Format(email) -// @Success 200 {array} model.Account -// @Failure 400 {object} httputil.HTTPError -// @Failure 404 {object} httputil.HTTPError -// @Failure 500 {object} httputil.HTTPError -// @Router /accounts [get] +// @Summary List accounts +// @Description get accounts +// @Accept json +// @Produce json +// @Param q query string false "name search by q" +// @Success 200 {array} model.Account +// @Header 200 {string} Token "qwerty" +// @Failure 400 {object} httputil.HTTPError +// @Failure 404 {object} httputil.HTTPError +// @Failure 500 {object} httputil.HTTPError +// @Router /accounts [get] func (c *Controller) ListAccounts(ctx *gin.Context) { - q := ctx.Request.URL.Query().Get("q") - accounts, err := model.AccountsAll(q) - if err != nil { - httputil.NewError(ctx, http.StatusNotFound, err) - return - } - ctx.JSON(http.StatusOK, accounts) + q := ctx.Request.URL.Query().Get("q") + accounts, err := model.AccountsAll(q) + if err != nil { + httputil.NewError(ctx, http.StatusNotFound, err) + return + } + ctx.JSON(http.StatusOK, accounts) } + //... ``` @@ -292,25 +289,10 @@ func (c *Controller) ListAccounts(ctx *gin.Context) { swag init ``` -4. 运行程序,然后在浏览器中访问 http://localhost:8080/swagger/index.html 。将看到Swagger 2.0 Api文档,如下所示: +4. 运行程序,然后在浏览器中访问 http://localhost:8080/swagger/index.html。将看到Swagger 2.0 Api文档,如下所示: ![swagger_index.html](https://raw.githubusercontent.com/swaggo/swag/master/assets/swagger-image.png) -## 格式化说明 - -可以针对Swag的注释自动格式化,就像`go fmt`。 -此处查看格式化结果 [here](https://github.com/swaggo/swag/tree/master/example/celler). - -示例: -```shell -swag fmt -``` - -排除目录(不扫描)示例: -```shell -swag fmt -d ./ --exclude ./internal -``` - ## 开发现状 [Swagger 2.0 文档](https://swagger.io/docs/specification/2-0/basic-structure/) @@ -354,12 +336,8 @@ swag fmt -d ./ --exclude ./internal | license.url | 用于API的许可证的URL。 必须采用网址格式。 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html | | host | 运行API的主机(主机名或IP地址)。 | // @host localhost:8080 | | BasePath | 运行API的基本路径。 | // @BasePath /api/v1 | -| accept | API 可以使用的 MIME 类型列表。 请注意,Accept 仅影响具有请求正文的操作,例如 POST、PUT 和 PATCH。 值必须如“[Mime类型](#mime类型)”中所述。 | // @accept json | -| produce | API可以生成的MIME类型的列表。值必须如“[Mime类型](#mime类型)”中所述。 | // @produce json | | query.collection.format | 请求URI query里数组参数的默认格式:csv,multi,pipes,tsv,ssv。 如果未设置,则默认为csv。 | // @query.collection.format multi | | schemes | 用空格分隔的请求的传输协议。 | // @schemes http https | -| externalDocs.description | Description of the external document. | // @externalDocs.description OpenAPI | -| externalDocs.url | URL of the external document. | // @externalDocs.url https://swagger.io/resources/open-api/ | | x-name | 扩展的键必须以x-开头,并且只能使用json值 | // @x-example-key {"key": "value"} | ### 使用Markdown描述 @@ -378,29 +356,26 @@ swag fmt -d ./ --exclude ./internal Example [celler/controller](https://github.com/swaggo/swag/tree/master/example/celler/controller) -| 注释 | 描述 | -|----------------------|------------------------------------------------------------------------------------------------| -| description | 操作行为的详细说明。 | -| description.markdown | 应用程序的简短描述。该描述将从名为`endpointname.md`的文件中读取。 | -| id | 用于标识操作的唯一字符串。在所有API操作中必须唯一。 | -| tags | 每个API操作的标签列表,以逗号分隔。 | +| 注释 | 描述 | | +| -------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | +| description | 操作行为的详细说明。 | +| description.markdown | 应用程序的简短描述。该描述将从名为`endpointname.md`的文件中读取。 | // @description.file endpoint.description.markdown | +| id | 用于标识操作的唯一字符串。在所有API操作中必须唯一。 | +| tags | 每个API操作的标签列表,以逗号分隔。 | | summary | 该操作的简短摘要。 | -| accept | API 可以使用的 MIME 类型列表。 请注意,Accept 仅影响具有请求正文的操作,例如 POST、PUT 和 PATCH。 值必须如“[Mime类型](#mime类型)”中所述。 | -| produce | API可以生成的MIME类型的列表。值必须如“[Mime类型](#mime类型)”中所述。 | +| accept | API可以使用的MIME类型的列表。值必须如“[Mime类型](#mime-types)”中所述。 | +| produce | API可以生成的MIME类型的列表。值必须如“[Mime类型](#mime-types)”中所述。 | | param | 用空格分隔的参数。`param name`,`param type`,`data type`,`is mandatory?`,`comment` `attribute(optional)` | -| security | 每个API操作的[安全性](#安全性)。 | -| success | 以空格分隔的成功响应。`return code`,`{param type}`,`data type`,`comment` | -| failure | 以空格分隔的故障响应。`return code`,`{param type}`,`data type`,`comment` | -| response | 与success、failure作用相同 | -| header | 以空格分隔的头字段。 `return code`,`{param type}`,`data type`,`comment` | -| router | 以空格分隔的路径定义。 `path`,`[httpMethod]` | -| deprecatedrouter | 与router相同,但是是deprecated的。 | -| x-name | 扩展字段必须以`x-`开头,并且只能使用json值。 | -| deprecated | 将当前API操作的所有路径设置为deprecated | +| security | 每个API操作的[安全性](#security)。 | +| success | 以空格分隔的成功响应。`return code`,`{param type}`,`data type`,`comment` | +| failure | 以空格分隔的故障响应。`return code`,`{param type}`,`data type`,`comment` | +| header | 以空格分隔的头字段。 `return code`,`{param type}`,`data type`,`comment` | +| router | 以空格分隔的路径定义。 `path`,`[httpMethod]` | +| x-name | 扩展字段必须以`x-`开头,并且只能使用json值。 | ## Mime类型 -`swag` 接受所有格式正确的MIME类型, 即使匹配 `*/*`。除此之外,`swag`还接受某些MIME类型的别名,如下所示: +`swag` g接受所有格式正确的MIME类型, 即使匹配 `*/*`。除此之外,`swag`还接受某些MIME类型的别名,如下所示: | Alias | MIME Type | | --------------------- | --------------------------------- | @@ -455,14 +430,13 @@ Example [celler/controller](https://github.com/swaggo/swag/tree/master/example/c ## 属性 ```go -// @Param enumstring query string false "string enums" Enums(A, B, C) -// @Param enumint query int false "int enums" Enums(1, 2, 3) -// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3) -// @Param string query string false "string valid" minlength(5) maxlength(10) -// @Param int query int false "int valid" minimum(1) maximum(10) -// @Param default query string false "string default" default(A) -// @Param collection query []string false "string collection" collectionFormat(multi) -// @Param extensions query []string false "string collection" extensions(x-example=test,x-nullable) +// @Param enumstring query string false "string enums" Enums(A, B, C) +// @Param enumint query int false "int enums" Enums(1, 2, 3) +// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3) +// @Param string query string false "string valid" minlength(5) maxlength(10) +// @Param int query int false "int valid" mininum(1) maxinum(10) +// @Param default query string false "string default" default(A) +// @Param collection query []string false "string collection" collectionFormat(multi) ``` 也适用于结构体字段: @@ -561,20 +535,17 @@ type Order struct { //in `proto` package ### 在响应中增加头字段 ```go -// @Success 200 {string} string "ok" -// @failure 400 {string} string "error" -// @response default {string} string "other error" -// @Header 200 {string} Location "/entity/1" -// @Header 200,400,default {string} Token "token" -// @Header all {string} Token2 "token2" +// @Success 200 {string} string "ok" +// @Header 200 {string} Location "/entity/1" +// @Header 200 {string} Token "qwerty" ``` ### 使用多路径参数 ```go /// ... -// @Param group_id path int true "Group ID" -// @Param account_id path int true "Account ID" +// @Param group_id path int true "Group ID" +// @Param account_id path int true "Account ID" // ... // @Router /examples/groups/{group_id}/accounts/{account_id} [get] ``` @@ -681,7 +652,7 @@ type Account struct { ```go type Account struct { - ID string `json:"id" extensions:"x-nullable,x-abc=def,!x-omitempty"` // 扩展字段必须以"x-"开头 + ID string `json:"id" extensions:"x-nullable,x-abc=def"` // 扩展字段必须以"x-"开头 } ``` @@ -694,8 +665,7 @@ type Account struct { "id": { "type": "string", "x-nullable": true, - "x-abc": "def", - "x-omitempty": false + "x-abc": "def" } } } diff --git a/vendor/github.com/swaggo/swag/const.go b/vendor/github.com/swaggo/swag/const.go deleted file mode 100644 index 83755103b7..0000000000 --- a/vendor/github.com/swaggo/swag/const.go +++ /dev/null @@ -1,567 +0,0 @@ -package swag - -import ( - "go/ast" - "go/token" - "reflect" - "strconv" - "strings" - "unicode/utf8" -) - -// ConstVariable a model to record a const variable -type ConstVariable struct { - Name *ast.Ident - Type ast.Expr - Value interface{} - Comment *ast.CommentGroup - File *ast.File - Pkg *PackageDefinitions -} - -var escapedChars = map[uint8]uint8{ - 'n': '\n', - 'r': '\r', - 't': '\t', - 'v': '\v', - '\\': '\\', - '"': '"', -} - -// EvaluateEscapedChar parse escaped character -func EvaluateEscapedChar(text string) rune { - if len(text) == 1 { - return rune(text[0]) - } - - if len(text) == 2 && text[0] == '\\' { - return rune(escapedChars[text[1]]) - } - - if len(text) == 6 && text[0:2] == "\\u" { - n, err := strconv.ParseInt(text[2:], 16, 32) - if err == nil { - return rune(n) - } - } - - return 0 -} - -// EvaluateEscapedString parse escaped characters in string -func EvaluateEscapedString(text string) string { - if !strings.ContainsRune(text, '\\') { - return text - } - result := make([]byte, 0, len(text)) - for i := 0; i < len(text); i++ { - if text[i] == '\\' { - i++ - if text[i] == 'u' { - i++ - char, err := strconv.ParseInt(text[i:i+4], 16, 32) - if err == nil { - result = utf8.AppendRune(result, rune(char)) - } - i += 3 - } else if c, ok := escapedChars[text[i]]; ok { - result = append(result, c) - } - } else { - result = append(result, text[i]) - } - } - return string(result) -} - -// EvaluateDataConversion evaluate the type a explicit type conversion -func EvaluateDataConversion(x interface{}, typeName string) interface{} { - switch value := x.(type) { - case int: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case uint: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case int8: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case uint8: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case int16: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case uint16: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case int32: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - case "string": - return string(value) - } - case uint32: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case int64: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case uint64: - switch typeName { - case "int": - return int(value) - case "byte": - return byte(value) - case "int8": - return int8(value) - case "int16": - return int16(value) - case "int32": - return int32(value) - case "int64": - return int64(value) - case "uint": - return uint(value) - case "uint8": - return uint8(value) - case "uint16": - return uint16(value) - case "uint32": - return uint32(value) - case "uint64": - return uint64(value) - case "rune": - return rune(value) - } - case string: - switch typeName { - case "string": - return value - } - } - return nil -} - -// EvaluateUnary evaluate the type and value of a unary expression -func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interface{}, ast.Expr) { - switch operator { - case token.SUB: - switch value := x.(type) { - case int: - return -value, xtype - case int8: - return -value, xtype - case int16: - return -value, xtype - case int32: - return -value, xtype - case int64: - return -value, xtype - } - case token.XOR: - switch value := x.(type) { - case int: - return ^value, xtype - case int8: - return ^value, xtype - case int16: - return ^value, xtype - case int32: - return ^value, xtype - case int64: - return ^value, xtype - case uint: - return ^value, xtype - case uint8: - return ^value, xtype - case uint16: - return ^value, xtype - case uint32: - return ^value, xtype - case uint64: - return ^value, xtype - } - } - return nil, nil -} - -// EvaluateBinary evaluate the type and value of a binary expression -func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Expr) (interface{}, ast.Expr) { - if operator == token.SHR || operator == token.SHL { - var rightOperand uint64 - yValue := reflect.ValueOf(y) - if yValue.CanUint() { - rightOperand = yValue.Uint() - } else if yValue.CanInt() { - rightOperand = uint64(yValue.Int()) - } - - switch operator { - case token.SHL: - switch xValue := x.(type) { - case int: - return xValue << rightOperand, xtype - case int8: - return xValue << rightOperand, xtype - case int16: - return xValue << rightOperand, xtype - case int32: - return xValue << rightOperand, xtype - case int64: - return xValue << rightOperand, xtype - case uint: - return xValue << rightOperand, xtype - case uint8: - return xValue << rightOperand, xtype - case uint16: - return xValue << rightOperand, xtype - case uint32: - return xValue << rightOperand, xtype - case uint64: - return xValue << rightOperand, xtype - } - case token.SHR: - switch xValue := x.(type) { - case int: - return xValue >> rightOperand, xtype - case int8: - return xValue >> rightOperand, xtype - case int16: - return xValue >> rightOperand, xtype - case int32: - return xValue >> rightOperand, xtype - case int64: - return xValue >> rightOperand, xtype - case uint: - return xValue >> rightOperand, xtype - case uint8: - return xValue >> rightOperand, xtype - case uint16: - return xValue >> rightOperand, xtype - case uint32: - return xValue >> rightOperand, xtype - case uint64: - return xValue >> rightOperand, xtype - } - } - return nil, nil - } - - evalType := xtype - if evalType == nil { - evalType = ytype - } - - xValue := reflect.ValueOf(x) - yValue := reflect.ValueOf(y) - if xValue.Kind() == reflect.String && yValue.Kind() == reflect.String { - return xValue.String() + yValue.String(), evalType - } - - var targetValue reflect.Value - if xValue.Kind() != reflect.Int { - targetValue = reflect.New(xValue.Type()).Elem() - } else { - targetValue = reflect.New(yValue.Type()).Elem() - } - - switch operator { - case token.ADD: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() + yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() + yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) + yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() + uint64(yValue.Int())) - } - case token.SUB: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() - yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() - yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) - yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() - uint64(yValue.Int())) - } - case token.MUL: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() * yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() * yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) * yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() * uint64(yValue.Int())) - } - case token.QUO: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() / yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() / yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) / yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() / uint64(yValue.Int())) - } - case token.REM: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() % yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() % yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) % yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() % uint64(yValue.Int())) - } - case token.AND: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() & yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() & yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) & yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() & uint64(yValue.Int())) - } - case token.OR: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() | yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() | yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) | yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() | uint64(yValue.Int())) - } - case token.XOR: - if xValue.CanInt() && yValue.CanInt() { - targetValue.SetInt(xValue.Int() ^ yValue.Int()) - } else if xValue.CanUint() && yValue.CanUint() { - targetValue.SetUint(xValue.Uint() ^ yValue.Uint()) - } else if xValue.CanInt() && yValue.CanUint() { - targetValue.SetUint(uint64(xValue.Int()) ^ yValue.Uint()) - } else if xValue.CanUint() && yValue.CanInt() { - targetValue.SetUint(xValue.Uint() ^ uint64(yValue.Int())) - } - } - return targetValue.Interface(), evalType -} diff --git a/vendor/github.com/swaggo/swag/debug.go b/vendor/github.com/swaggo/swag/debug.go new file mode 100644 index 0000000000..f28a8e653f --- /dev/null +++ b/vendor/github.com/swaggo/swag/debug.go @@ -0,0 +1,30 @@ +package swag + +import ( + "log" +) + +const ( + test = iota + release +) + +var swagMode = release + +func isRelease() bool { + return swagMode == release +} + +// Println calls Output to print to the standard logger when release mode. +func Println(v ...interface{}) { + if isRelease() { + log.Println(v...) + } +} + +// Printf calls Output to print to the standard logger when release mode. +func Printf(format string, v ...interface{}) { + if isRelease() { + log.Printf(format, v...) + } +} diff --git a/vendor/github.com/swaggo/swag/enums.go b/vendor/github.com/swaggo/swag/enums.go deleted file mode 100644 index 38658f20ad..0000000000 --- a/vendor/github.com/swaggo/swag/enums.go +++ /dev/null @@ -1,13 +0,0 @@ -package swag - -const ( - enumVarNamesExtension = "x-enum-varnames" - enumCommentsExtension = "x-enum-comments" -) - -// EnumValue a model to record an enum consts variable -type EnumValue struct { - key string - Value interface{} - Comment string -} diff --git a/vendor/github.com/swaggo/swag/field_parser.go b/vendor/github.com/swaggo/swag/field_parser.go deleted file mode 100644 index 9b24e7872a..0000000000 --- a/vendor/github.com/swaggo/swag/field_parser.go +++ /dev/null @@ -1,680 +0,0 @@ -package swag - -import ( - "fmt" - "go/ast" - "reflect" - "regexp" - "strconv" - "strings" - "sync" - "unicode" - - "github.com/go-openapi/spec" -) - -var _ FieldParser = &tagBaseFieldParser{p: nil, field: nil, tag: ""} - -const ( - requiredLabel = "required" - optionalLabel = "optional" - swaggerTypeTag = "swaggertype" - swaggerIgnoreTag = "swaggerignore" -) - -type tagBaseFieldParser struct { - p *Parser - field *ast.Field - tag reflect.StructTag -} - -func newTagBaseFieldParser(p *Parser, field *ast.Field) FieldParser { - fieldParser := tagBaseFieldParser{ - p: p, - field: field, - tag: "", - } - if fieldParser.field.Tag != nil { - fieldParser.tag = reflect.StructTag(strings.ReplaceAll(field.Tag.Value, "`", "")) - } - - return &fieldParser -} - -func (ps *tagBaseFieldParser) ShouldSkip() bool { - // Skip non-exported fields. - if ps.field.Names != nil && !ast.IsExported(ps.field.Names[0].Name) { - return true - } - - if ps.field.Tag == nil { - return false - } - - ignoreTag := ps.tag.Get(swaggerIgnoreTag) - if strings.EqualFold(ignoreTag, "true") { - return true - } - - // json:"tag,hoge" - name := strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0]) - if name == "-" { - return true - } - - return false -} - -func (ps *tagBaseFieldParser) FieldName() (string, error) { - var name string - - if ps.field.Tag != nil { - // json:"tag,hoge" - name = strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0]) - if name != "" { - return name, nil - } - - // use "form" tag over json tag - name = ps.FormName() - if name != "" { - return name, nil - } - } - - if ps.field.Names == nil { - return "", nil - } - - switch ps.p.PropNamingStrategy { - case SnakeCase: - return toSnakeCase(ps.field.Names[0].Name), nil - case PascalCase: - return ps.field.Names[0].Name, nil - default: - return toLowerCamelCase(ps.field.Names[0].Name), nil - } -} - -func (ps *tagBaseFieldParser) firstTagValue(tag string) string { - if ps.field.Tag != nil { - return strings.TrimRight(strings.TrimSpace(strings.Split(ps.tag.Get(tag), ",")[0]), "[]") - } - return "" -} - -func (ps *tagBaseFieldParser) FormName() string { - return ps.firstTagValue(formTag) -} - -func (ps *tagBaseFieldParser) HeaderName() string { - return ps.firstTagValue(headerTag) -} - -func (ps *tagBaseFieldParser) PathName() string { - return ps.firstTagValue(uriTag) -} - -func toSnakeCase(in string) string { - var ( - runes = []rune(in) - length = len(runes) - out []rune - ) - - for idx := 0; idx < length; idx++ { - if idx > 0 && unicode.IsUpper(runes[idx]) && - ((idx+1 < length && unicode.IsLower(runes[idx+1])) || unicode.IsLower(runes[idx-1])) { - out = append(out, '_') - } - - out = append(out, unicode.ToLower(runes[idx])) - } - - return string(out) -} - -func toLowerCamelCase(in string) string { - var flag bool - - out := make([]rune, len(in)) - - runes := []rune(in) - for i, curr := range runes { - if (i == 0 && unicode.IsUpper(curr)) || (flag && unicode.IsUpper(curr)) { - out[i] = unicode.ToLower(curr) - flag = true - - continue - } - - out[i] = curr - flag = false - } - - return string(out) -} - -func (ps *tagBaseFieldParser) CustomSchema() (*spec.Schema, error) { - if ps.field.Tag == nil { - return nil, nil - } - - typeTag := ps.tag.Get(swaggerTypeTag) - if typeTag != "" { - return BuildCustomSchema(strings.Split(typeTag, ",")) - } - - return nil, nil -} - -type structField struct { - schemaType string - arrayType string - formatType string - maximum *float64 - minimum *float64 - multipleOf *float64 - maxLength *int64 - minLength *int64 - maxItems *int64 - minItems *int64 - exampleValue interface{} - enums []interface{} - enumVarNames []interface{} - unique bool -} - -// splitNotWrapped slices s into all substrings separated by sep if sep is not -// wrapped by brackets and returns a slice of the substrings between those separators. -func splitNotWrapped(s string, sep rune) []string { - openCloseMap := map[rune]rune{ - '(': ')', - '[': ']', - '{': '}', - } - - var ( - result = make([]string, 0) - current = strings.Builder{} - openCount = 0 - openChar rune - ) - - for _, char := range s { - switch { - case openChar == 0 && openCloseMap[char] != 0: - openChar = char - - openCount++ - - current.WriteRune(char) - case char == openChar: - openCount++ - - current.WriteRune(char) - case openCount > 0 && char == openCloseMap[openChar]: - openCount-- - - current.WriteRune(char) - case openCount == 0 && char == sep: - result = append(result, current.String()) - - openChar = 0 - - current = strings.Builder{} - default: - current.WriteRune(char) - } - } - - if current.String() != "" { - result = append(result, current.String()) - } - - return result -} - -// ComplementSchema complement schema with field properties -func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error { - types := ps.p.GetSchemaTypePath(schema, 2) - if len(types) == 0 { - return fmt.Errorf("invalid type for field: %s", ps.field.Names[0]) - } - - if IsRefSchema(schema) { - var newSchema = spec.Schema{} - err := ps.complementSchema(&newSchema, types) - if err != nil { - return err - } - if !reflect.ValueOf(newSchema).IsZero() { - *schema = *(newSchema.WithAllOf(*schema)) - } - return nil - } - - return ps.complementSchema(schema, types) -} - -// complementSchema complement schema with field properties -func (ps *tagBaseFieldParser) complementSchema(schema *spec.Schema, types []string) error { - if ps.field.Tag == nil { - if ps.field.Doc != nil { - schema.Description = strings.TrimSpace(ps.field.Doc.Text()) - } - - if schema.Description == "" && ps.field.Comment != nil { - schema.Description = strings.TrimSpace(ps.field.Comment.Text()) - } - - return nil - } - - field := &structField{ - schemaType: types[0], - formatType: ps.tag.Get(formatTag), - } - - if len(types) > 1 && (types[0] == ARRAY || types[0] == OBJECT) { - field.arrayType = types[1] - } - - jsonTagValue := ps.tag.Get(jsonTag) - - bindingTagValue := ps.tag.Get(bindingTag) - if bindingTagValue != "" { - parseValidTags(bindingTagValue, field) - } - - validateTagValue := ps.tag.Get(validateTag) - if validateTagValue != "" { - parseValidTags(validateTagValue, field) - } - - enumsTagValue := ps.tag.Get(enumsTag) - if enumsTagValue != "" { - err := parseEnumTags(enumsTagValue, field) - if err != nil { - return err - } - } - - if IsNumericType(field.schemaType) || IsNumericType(field.arrayType) { - maximum, err := getFloatTag(ps.tag, maximumTag) - if err != nil { - return err - } - - if maximum != nil { - field.maximum = maximum - } - - minimum, err := getFloatTag(ps.tag, minimumTag) - if err != nil { - return err - } - - if minimum != nil { - field.minimum = minimum - } - - multipleOf, err := getFloatTag(ps.tag, multipleOfTag) - if err != nil { - return err - } - - if multipleOf != nil { - field.multipleOf = multipleOf - } - } - - if field.schemaType == STRING || field.arrayType == STRING { - maxLength, err := getIntTag(ps.tag, maxLengthTag) - if err != nil { - return err - } - - if maxLength != nil { - field.maxLength = maxLength - } - - minLength, err := getIntTag(ps.tag, minLengthTag) - if err != nil { - return err - } - - if minLength != nil { - field.minLength = minLength - } - } - - // json:"name,string" or json:",string" - exampleTagValue, ok := ps.tag.Lookup(exampleTag) - if ok { - field.exampleValue = exampleTagValue - - if !strings.Contains(jsonTagValue, ",string") { - example, err := defineTypeOfExample(field.schemaType, field.arrayType, exampleTagValue) - if err != nil { - return err - } - - field.exampleValue = example - } - } - - // perform this after setting everything else (min, max, etc...) - if strings.Contains(jsonTagValue, ",string") { - // @encoding/json: "It applies only to fields of string, floating point, integer, or boolean types." - defaultValues := map[string]string{ - // Zero Values as string - STRING: "", - INTEGER: "0", - BOOLEAN: "false", - NUMBER: "0", - } - - defaultValue, ok := defaultValues[field.schemaType] - if ok { - field.schemaType = STRING - *schema = *PrimitiveSchema(field.schemaType) - - if field.exampleValue == nil { - // if exampleValue is not defined by the user, - // we will force an example with a correct value - // (eg: int->"0", bool:"false") - field.exampleValue = defaultValue - } - } - } - - if ps.field.Doc != nil { - schema.Description = strings.TrimSpace(ps.field.Doc.Text()) - } - - if schema.Description == "" && ps.field.Comment != nil { - schema.Description = strings.TrimSpace(ps.field.Comment.Text()) - } - - schema.ReadOnly = ps.tag.Get(readOnlyTag) == "true" - - defaultTagValue := ps.tag.Get(defaultTag) - if defaultTagValue != "" { - value, err := defineType(field.schemaType, defaultTagValue) - if err != nil { - return err - } - - schema.Default = value - } - - schema.Example = field.exampleValue - - if field.schemaType != ARRAY { - schema.Format = field.formatType - } - - extensionsTagValue := ps.tag.Get(extensionsTag) - if extensionsTagValue != "" { - schema.Extensions = setExtensionParam(extensionsTagValue) - } - - varNamesTag := ps.tag.Get("x-enum-varnames") - if varNamesTag != "" { - varNames := strings.Split(varNamesTag, ",") - if len(varNames) != len(field.enums) { - return fmt.Errorf("invalid count of x-enum-varnames. expected %d, got %d", len(field.enums), len(varNames)) - } - - field.enumVarNames = nil - - for _, v := range varNames { - field.enumVarNames = append(field.enumVarNames, v) - } - - if field.schemaType == ARRAY { - // Add the var names in the items schema - if schema.Items.Schema.Extensions == nil { - schema.Items.Schema.Extensions = map[string]interface{}{} - } - schema.Items.Schema.Extensions[enumVarNamesExtension] = field.enumVarNames - } else { - // Add to top level schema - if schema.Extensions == nil { - schema.Extensions = map[string]interface{}{} - } - schema.Extensions[enumVarNamesExtension] = field.enumVarNames - } - } - - eleSchema := schema - - if field.schemaType == ARRAY { - // For Array only - schema.MaxItems = field.maxItems - schema.MinItems = field.minItems - schema.UniqueItems = field.unique - - eleSchema = schema.Items.Schema - eleSchema.Format = field.formatType - } - - eleSchema.Maximum = field.maximum - eleSchema.Minimum = field.minimum - eleSchema.MultipleOf = field.multipleOf - eleSchema.MaxLength = field.maxLength - eleSchema.MinLength = field.minLength - eleSchema.Enum = field.enums - - return nil -} - -func getFloatTag(structTag reflect.StructTag, tagName string) (*float64, error) { - strValue := structTag.Get(tagName) - if strValue == "" { - return nil, nil - } - - value, err := strconv.ParseFloat(strValue, 64) - if err != nil { - return nil, fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err) - } - - return &value, nil -} - -func getIntTag(structTag reflect.StructTag, tagName string) (*int64, error) { - strValue := structTag.Get(tagName) - if strValue == "" { - return nil, nil - } - - value, err := strconv.ParseInt(strValue, 10, 64) - if err != nil { - return nil, fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err) - } - - return &value, nil -} - -func (ps *tagBaseFieldParser) IsRequired() (bool, error) { - if ps.field.Tag == nil { - return false, nil - } - - bindingTag := ps.tag.Get(bindingTag) - if bindingTag != "" { - for _, val := range strings.Split(bindingTag, ",") { - switch val { - case requiredLabel: - return true, nil - case optionalLabel: - return false, nil - } - } - } - - validateTag := ps.tag.Get(validateTag) - if validateTag != "" { - for _, val := range strings.Split(validateTag, ",") { - switch val { - case requiredLabel: - return true, nil - case optionalLabel: - return false, nil - } - } - } - - return ps.p.RequiredByDefault, nil -} - -func parseValidTags(validTag string, sf *structField) { - // `validate:"required,max=10,min=1"` - // ps. required checked by IsRequired(). - for _, val := range strings.Split(validTag, ",") { - var ( - valValue string - keyVal = strings.Split(val, "=") - ) - - switch len(keyVal) { - case 1: - case 2: - valValue = strings.ReplaceAll(strings.ReplaceAll(keyVal[1], utf8HexComma, ","), utf8Pipe, "|") - default: - continue - } - - switch keyVal[0] { - case "max", "lte": - sf.setMax(valValue) - case "min", "gte": - sf.setMin(valValue) - case "oneof": - sf.setOneOf(valValue) - case "unique": - if sf.schemaType == ARRAY { - sf.unique = true - } - case "dive": - // ignore dive - return - default: - continue - } - } -} - -func parseEnumTags(enumTag string, field *structField) error { - enumType := field.schemaType - if field.schemaType == ARRAY { - enumType = field.arrayType - } - - field.enums = nil - - for _, e := range strings.Split(enumTag, ",") { - value, err := defineType(enumType, e) - if err != nil { - return err - } - - field.enums = append(field.enums, value) - } - - return nil -} - -func (sf *structField) setOneOf(valValue string) { - if len(sf.enums) != 0 { - return - } - - enumType := sf.schemaType - if sf.schemaType == ARRAY { - enumType = sf.arrayType - } - - valValues := parseOneOfParam2(valValue) - for i := range valValues { - value, err := defineType(enumType, valValues[i]) - if err != nil { - continue - } - - sf.enums = append(sf.enums, value) - } -} - -func (sf *structField) setMin(valValue string) { - value, err := strconv.ParseFloat(valValue, 64) - if err != nil { - return - } - - switch sf.schemaType { - case INTEGER, NUMBER: - sf.minimum = &value - case STRING: - intValue := int64(value) - sf.minLength = &intValue - case ARRAY: - intValue := int64(value) - sf.minItems = &intValue - } -} - -func (sf *structField) setMax(valValue string) { - value, err := strconv.ParseFloat(valValue, 64) - if err != nil { - return - } - - switch sf.schemaType { - case INTEGER, NUMBER: - sf.maximum = &value - case STRING: - intValue := int64(value) - sf.maxLength = &intValue - case ARRAY: - intValue := int64(value) - sf.maxItems = &intValue - } -} - -const ( - utf8HexComma = "0x2C" - utf8Pipe = "0x7C" -) - -// These code copy from -// https://github.com/go-playground/validator/blob/d4271985b44b735c6f76abc7a06532ee997f9476/baked_in.go#L207 -// ---. -var oneofValsCache = map[string][]string{} -var oneofValsCacheRWLock = sync.RWMutex{} -var splitParamsRegex = regexp.MustCompile(`'[^']*'|\S+`) - -func parseOneOfParam2(param string) []string { - oneofValsCacheRWLock.RLock() - values, ok := oneofValsCache[param] - oneofValsCacheRWLock.RUnlock() - - if !ok { - oneofValsCacheRWLock.Lock() - values = splitParamsRegex.FindAllString(param, -1) - - for i := 0; i < len(values); i++ { - values[i] = strings.ReplaceAll(values[i], "'", "") - } - - oneofValsCache[param] = values - - oneofValsCacheRWLock.Unlock() - } - - return values -} - -// ---. diff --git a/vendor/github.com/swaggo/swag/formatter.go b/vendor/github.com/swaggo/swag/formatter.go deleted file mode 100644 index 511e3a8224..0000000000 --- a/vendor/github.com/swaggo/swag/formatter.go +++ /dev/null @@ -1,182 +0,0 @@ -package swag - -import ( - "bytes" - "fmt" - "go/ast" - goparser "go/parser" - "go/token" - "log" - "os" - "regexp" - "sort" - "strings" - "text/tabwriter" -) - -// Check of @Param @Success @Failure @Response @Header -var specialTagForSplit = map[string]bool{ - paramAttr: true, - successAttr: true, - failureAttr: true, - responseAttr: true, - headerAttr: true, -} - -var skipChar = map[byte]byte{ - '"': '"', - '(': ')', - '{': '}', - '[': ']', -} - -// Formatter implements a formatter for Go source files. -type Formatter struct { - // debugging output goes here - debug Debugger -} - -// NewFormatter create a new formatter instance. -func NewFormatter() *Formatter { - formatter := &Formatter{ - debug: log.New(os.Stdout, "", log.LstdFlags), - } - return formatter -} - -// Format formats swag comments in contents. It uses fileName to report errors -// that happen during parsing of contents. -func (f *Formatter) Format(fileName string, contents []byte) ([]byte, error) { - fileSet := token.NewFileSet() - ast, err := goparser.ParseFile(fileSet, fileName, contents, goparser.ParseComments) - if err != nil { - return nil, err - } - - // Formatting changes are described as an edit list of byte range - // replacements. We make these content-level edits directly rather than - // changing the AST nodes and writing those out (via [go/printer] or - // [go/format]) so that we only change the formatting of Swag attribute - // comments. This won't touch the formatting of any other comments, or of - // functions, etc. - maxEdits := 0 - for _, comment := range ast.Comments { - maxEdits += len(comment.List) - } - edits := make(edits, 0, maxEdits) - - for _, comment := range ast.Comments { - formatFuncDoc(fileSet, comment.List, &edits) - } - - return edits.apply(contents), nil -} - -type edit struct { - begin int - end int - replacement []byte -} - -type edits []edit - -func (edits edits) apply(contents []byte) []byte { - // Apply the edits with the highest offset first, so that earlier edits - // don't affect the offsets of later edits. - sort.Slice(edits, func(i, j int) bool { - return edits[i].begin > edits[j].begin - }) - - for _, edit := range edits { - prefix := contents[:edit.begin] - suffix := contents[edit.end:] - contents = append(prefix, append(edit.replacement, suffix...)...) - } - - return contents -} - -// formatFuncDoc reformats the comment lines in commentList, and appends any -// changes to the edit list. -func formatFuncDoc(fileSet *token.FileSet, commentList []*ast.Comment, edits *edits) { - // Building the edit list to format a comment block is a two-step process. - // First, we iterate over each comment line looking for Swag attributes. In - // each one we find, we replace alignment whitespace with a tab character, - // then write the result into a tab writer. - - linesToComments := make(map[int]int, len(commentList)) - - buffer := &bytes.Buffer{} - w := tabwriter.NewWriter(buffer, 1, 4, 1, '\t', 0) - - for commentIndex, comment := range commentList { - text := comment.Text - if attr, body, found := swagComment(text); found { - formatted := "//\t" + attr - if body != "" { - formatted += "\t" + splitComment2(attr, body) - } - _, _ = fmt.Fprintln(w, formatted) - linesToComments[len(linesToComments)] = commentIndex - } - } - - // Once we've loaded all of the comment lines to be aligned into the tab - // writer, flushing it causes the aligned text to be written out to the - // backing buffer. - _ = w.Flush() - - // Now the second step: we iterate over the aligned comment lines that were - // written into the backing buffer, pair each one up to its original - // comment line, and use the combination to describe the edit that needs to - // be made to the original input. - formattedComments := bytes.Split(buffer.Bytes(), []byte("\n")) - for lineIndex, commentIndex := range linesToComments { - comment := commentList[commentIndex] - *edits = append(*edits, edit{ - begin: fileSet.Position(comment.Pos()).Offset, - end: fileSet.Position(comment.End()).Offset, - replacement: formattedComments[lineIndex], - }) - } -} - -func splitComment2(attr, body string) string { - if specialTagForSplit[strings.ToLower(attr)] { - for i := 0; i < len(body); i++ { - if skipEnd, ok := skipChar[body[i]]; ok { - skipStart, n := body[i], 1 - for i++; i < len(body); i++ { - if skipStart != skipEnd && body[i] == skipStart { - n++ - } else if body[i] == skipEnd { - n-- - if n == 0 { - break - } - } - } - } else if body[i] == ' ' || body[i] == '\t' { - j := i - for ; j < len(body) && (body[j] == ' ' || body[j] == '\t'); j++ { - } - body = replaceRange(body, i, j, "\t") - } - } - } - return body -} - -func replaceRange(s string, start, end int, new string) string { - return s[:start] + new + s[end:] -} - -var swagCommentLineExpression = regexp.MustCompile(`^\/\/\s+(@[\S.]+)\s*(.*)`) - -func swagComment(comment string) (string, string, bool) { - matches := swagCommentLineExpression.FindStringSubmatch(comment) - if matches == nil { - return "", "", false - } - return matches[1], matches[2], true -} diff --git a/vendor/github.com/swaggo/swag/gen/gen.go b/vendor/github.com/swaggo/swag/gen/gen.go index 43cf73ed8b..b80fd21f45 100644 --- a/vendor/github.com/swaggo/swag/gen/gen.go +++ b/vendor/github.com/swaggo/swag/gen/gen.go @@ -1,7 +1,6 @@ package gen import ( - "bufio" "bytes" "encoding/json" "fmt" @@ -10,259 +9,96 @@ import ( "log" "os" "path" - "path/filepath" "strings" "text/template" "time" + "github.com/ghodss/yaml" "github.com/go-openapi/spec" "github.com/swaggo/swag" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "sigs.k8s.io/yaml" ) -var open = os.Open - -// DefaultOverridesFile is the location swagger will look for type overrides. -const DefaultOverridesFile = ".swaggo" - -type genTypeWriter func(*Config, *spec.Swagger) error - // Gen presents a generate tool for swag. type Gen struct { - json func(data interface{}) ([]byte, error) - jsonIndent func(data interface{}) ([]byte, error) - jsonToYAML func(data []byte) ([]byte, error) - outputTypeMap map[string]genTypeWriter - debug Debugger -} - -// Debugger is the interface that wraps the basic Printf method. -type Debugger interface { - Printf(format string, v ...interface{}) + jsonIndent func(data interface{}) ([]byte, error) + jsonToYAML func(data []byte) ([]byte, error) } // New creates a new Gen. func New() *Gen { - gen := Gen{ - json: json.Marshal, + return &Gen{ jsonIndent: func(data interface{}) ([]byte, error) { return json.MarshalIndent(data, "", " ") }, jsonToYAML: yaml.JSONToYAML, - debug: log.New(os.Stdout, "", log.LstdFlags), - } - - gen.outputTypeMap = map[string]genTypeWriter{ - "go": gen.writeDocSwagger, - "json": gen.writeJSONSwagger, - "yaml": gen.writeYAMLSwagger, - "yml": gen.writeYAMLSwagger, } - - return &gen } // Config presents Gen configurations. type Config struct { - Debugger swag.Debugger - - // SearchDir the swag would parse,comma separated if multiple + // SearchDir the swag would be parse SearchDir string // excludes dirs and files in SearchDir,comma separated Excludes string - // outputs only specific extension - ParseExtension string - // OutputDir represents the output directory for all the generated files OutputDir string - // OutputTypes define types of files which should be generated - OutputTypes []string - // MainAPIFile the Go file path in which 'swagger general API Info' is written MainAPIFile string - // PropNamingStrategy represents property naming strategy like snake case,camel case,pascal case + // PropNamingStrategy represents property naming strategy like snakecase,camelcase,pascalcase PropNamingStrategy string - // MarkdownFilesDir used to find markdown files, which can be used for tag descriptions - MarkdownFilesDir string - - // CodeExampleFilesDir used to find code example files, which can be used for x-codeSamples - CodeExampleFilesDir string - - // InstanceName is used to get distinct names for different swagger documents in the - // same project. The default value is "swagger". - InstanceName string - - // ParseDepth dependency parse depth - ParseDepth int - // ParseVendor whether swag should be parse vendor folder ParseVendor bool - // ParseDependencies whether swag should be parse outside dependency folder: 0 none, 1 models, 2 operations, 3 all - ParseDependency int + // ParseDependencies whether swag should be parse outside dependency folder + ParseDependency bool // ParseInternal whether swag should parse internal packages ParseInternal bool - // Strict whether swag should error or warn when it detects cases which are most likely user errors - Strict bool + // MarkdownFilesDir used to find markdownfiles, which can be used for tag descriptions + MarkdownFilesDir string // GeneratedTime whether swag should generate the timestamp at the top of docs.go GeneratedTime bool - - // RequiredByDefault set validation required for all fields by default - RequiredByDefault bool - - // OverridesFile defines global type overrides. - OverridesFile string - - // ParseGoList whether swag use go list to parse dependency - ParseGoList bool - - // include only tags mentioned when searching, comma separated - Tags string - - // LeftTemplateDelim defines the left delimiter for the template generation - LeftTemplateDelim string - - // RightTemplateDelim defines the right delimiter for the template generation - RightTemplateDelim string - - // PackageName defines package name of generated `docs.go` - PackageName string - - // CollectionFormat set default collection format - CollectionFormat string - - // 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. +// Build builds swagger json file for given searchDir and mainAPIFile. Returns json func (g *Gen) Build(config *Config) error { - if config.Debugger != nil { - g.debug = config.Debugger - } - if config.InstanceName == "" { - config.InstanceName = swag.Name - } - - searchDirs := strings.Split(config.SearchDir, ",") - for _, searchDir := range searchDirs { - if _, err := os.Stat(searchDir); os.IsNotExist(err) { - return fmt.Errorf("dir: %s does not exist", searchDir) - } - } - - if config.LeftTemplateDelim == "" { - config.LeftTemplateDelim = "{{" + if _, err := os.Stat(config.SearchDir); os.IsNotExist(err) { + return fmt.Errorf("dir: %s is not exist", config.SearchDir) } - if config.RightTemplateDelim == "" { - config.RightTemplateDelim = "}}" - } - - var overrides map[string]string - - if config.OverridesFile != "" { - overridesFile, err := open(config.OverridesFile) - if err != nil { - // Don't bother reporting if the default file is missing; assume there are no overrides - if !(config.OverridesFile == DefaultOverridesFile && os.IsNotExist(err)) { - return fmt.Errorf("could not open overrides file: %w", err) - } - } else { - g.debug.Printf("Using overrides from %s", config.OverridesFile) - - overrides, err = parseOverrides(overridesFile) - if err != nil { - return err - } - } - } - - g.debug.Printf("Generate swagger docs....") - - p := swag.New( - swag.SetParseDependency(config.ParseDependency), - swag.SetMarkdownFileDirectory(config.MarkdownFilesDir), - swag.SetDebugger(config.Debugger), - swag.SetExcludedDirsAndFiles(config.Excludes), - swag.SetParseExtension(config.ParseExtension), - swag.SetCodeExamplesDirectory(config.CodeExampleFilesDir), - swag.SetStrict(config.Strict), - swag.SetOverrides(overrides), - swag.ParseUsingGoList(config.ParseGoList), - swag.SetTags(config.Tags), - swag.SetCollectionFormat(config.CollectionFormat), - swag.SetPackagePrefix(config.PackagePrefix), - ) - + log.Println("Generate swagger docs....") + p := swag.New(swag.SetMarkdownFileDirectory(config.MarkdownFilesDir), + swag.SetExcludedDirsAndFiles(config.Excludes)) p.PropNamingStrategy = config.PropNamingStrategy p.ParseVendor = config.ParseVendor + p.ParseDependency = config.ParseDependency p.ParseInternal = config.ParseInternal - p.RequiredByDefault = config.RequiredByDefault - p.HostState = config.State - if err := p.ParseAPIMultiSearchDir(searchDirs, config.MainAPIFile, config.ParseDepth); err != nil { + if err := p.ParseAPI(config.SearchDir, config.MainAPIFile); err != nil { return err } - swagger := p.GetSwagger() - if err := os.MkdirAll(config.OutputDir, os.ModePerm); err != nil { + b, err := g.jsonIndent(swagger) + if err != nil { return err } - for _, outputType := range config.OutputTypes { - outputType = strings.ToLower(strings.TrimSpace(outputType)) - if typeWriter, ok := g.outputTypeMap[outputType]; ok { - if err := typeWriter(config, swagger); err != nil { - return err - } - } else { - log.Printf("output type '%s' not supported", outputType) - } - } - - return nil -} - -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 - } - - docFileName := path.Join(config.OutputDir, filename) - - absOutputDir, err := filepath.Abs(config.OutputDir) - if err != nil { + if err := os.MkdirAll(config.OutputDir, os.ModePerm); err != nil { return err } - var packageName string - if len(config.PackageName) > 0 { - packageName = config.PackageName - } else { - packageName = filepath.Base(absOutputDir) - packageName = strings.ReplaceAll(packageName, "-", "_") - } + packageName := path.Base(config.OutputDir) + docFileName := path.Join(config.OutputDir, "docs.go") + jsonFileName := path.Join(config.OutputDir, "swagger.json") + yamlFileName := path.Join(config.OutputDir, "swagger.yaml") docs, err := os.Create(docFileName) if err != nil { @@ -270,74 +106,30 @@ func (g *Gen) writeDocSwagger(config *Config, swagger *spec.Swagger) error { } defer docs.Close() - // Write doc - err = g.writeGoDoc(packageName, docs, swagger, config) - if err != nil { - return err - } - - g.debug.Printf("create docs.go at %+v", docFileName) - - return nil -} - -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 - } - - jsonFileName := path.Join(config.OutputDir, filename) - - b, err := g.jsonIndent(swagger) - if err != nil { - return err - } - err = g.writeFile(b, jsonFileName) if err != nil { return err } - g.debug.Printf("create swagger.json at %+v", jsonFileName) - - return nil -} - -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 - } - - yamlFileName := path.Join(config.OutputDir, filename) - - b, err := g.json(swagger) + y, err := g.jsonToYAML(b) if err != nil { - return err + return fmt.Errorf("cannot convert json to yaml error: %s", err) } - y, err := g.jsonToYAML(b) + err = g.writeFile(y, yamlFileName) if err != nil { - return fmt.Errorf("cannot covert json to yaml error: %s", err) + return err } - err = g.writeFile(y, yamlFileName) + // Write doc + err = g.writeGoDoc(packageName, docs, swagger, config) if err != nil { return err } - g.debug.Printf("create swagger.yaml at %+v", yamlFileName) + log.Printf("create docs.go at %+v", docFileName) + log.Printf("create swagger.json at %+v", jsonFileName) + log.Printf("create swagger.yaml at %+v", yamlFileName) return nil } @@ -347,73 +139,25 @@ func (g *Gen) writeFile(b []byte, file string) error { if err != nil { return err } - defer f.Close() _, err = f.Write(b) - return err } func (g *Gen) formatSource(src []byte) []byte { code, err := format.Source(src) if err != nil { - code = src // Formatter failed, return original code. + code = src // Output the unformatted code anyway } - return code } -// Read and parse the overrides file. -func parseOverrides(r io.Reader) (map[string]string, error) { - overrides := make(map[string]string) - scanner := bufio.NewScanner(r) - - for scanner.Scan() { - line := scanner.Text() - - // Skip comments - if len(line) > 1 && line[0:2] == "//" { - continue - } - - parts := strings.Fields(line) - - switch len(parts) { - case 0: - // only whitespace - continue - case 2: - // either a skip or malformed - if parts[0] != "skip" { - return nil, fmt.Errorf("could not parse override: '%s'", line) - } - - overrides[parts[1]] = "" - case 3: - // either a replace or malformed - if parts[0] != "replace" { - return nil, fmt.Errorf("could not parse override: '%s'", line) - } - - overrides[parts[1]] = parts[2] - default: - return nil, fmt.Errorf("could not parse override: '%s'", line) - } - } - - if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("error reading overrides file: %w", err) - } - - return overrides, nil -} - func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swagger, config *Config) error { generator, err := template.New("swagger_info").Funcs(template.FuncMap{ "printDoc": func(v string) string { // Add schemes - v = "{\n \"schemes\": " + config.LeftTemplateDelim + " marshal .Schemes " + config.RightTemplateDelim + "," + v[1:] + v = "{\n \"schemes\": {{ marshal .Schemes }}," + v[1:] // Sanitize backticks return strings.Replace(v, "`", "`+\"`\"+`", -1) }, @@ -432,16 +176,16 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa Info: &spec.Info{ VendorExtensible: swagger.Info.VendorExtensible, InfoProps: spec.InfoProps{ - Description: config.LeftTemplateDelim + "escape .Description" + config.RightTemplateDelim, - Title: config.LeftTemplateDelim + ".Title" + config.RightTemplateDelim, + Description: "{{.Description}}", + Title: "{{.Title}}", TermsOfService: swagger.Info.TermsOfService, Contact: swagger.Info.Contact, License: swagger.Info.License, - Version: config.LeftTemplateDelim + ".Version" + config.RightTemplateDelim, + Version: "{{.Version}}", }, }, - Host: config.LeftTemplateDelim + ".Host" + config.RightTemplateDelim, - BasePath: config.LeftTemplateDelim + ".BasePath" + config.RightTemplateDelim, + Host: "{{.Host}}", + BasePath: "{{.BasePath}}", Paths: swagger.Paths, Definitions: swagger.Definitions, Parameters: swagger.Parameters, @@ -459,43 +203,29 @@ 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 { - Timestamp time.Time - Doc string - Host string - PackageName string - BasePath string - Title string - Description string - Version string - State string - InstanceName string - Schemes []string - GeneratedTime bool - LeftTemplateDelim string - RightTemplateDelim string + Timestamp time.Time + GeneratedTime bool + Doc string + Host string + PackageName string + BasePath string + Schemes []string + Title string + Description string + Version string }{ - Timestamp: time.Now(), - GeneratedTime: config.GeneratedTime, - Doc: string(buf), - Host: swagger.Host, - PackageName: packageName, - BasePath: swagger.BasePath, - Schemes: swagger.Schemes, - Title: swagger.Info.Title, - Description: swagger.Info.Description, - Version: swagger.Info.Version, - State: state, - InstanceName: config.InstanceName, - LeftTemplateDelim: config.LeftTemplateDelim, - RightTemplateDelim: config.RightTemplateDelim, + Timestamp: time.Now(), + GeneratedTime: config.GeneratedTime, + Doc: string(buf), + Host: swagger.Host, + PackageName: packageName, + BasePath: swagger.BasePath, + Schemes: swagger.Schemes, + Title: swagger.Info.Title, + Description: swagger.Info.Description, + Version: swagger.Info.Version, }) if err != nil { return err @@ -505,32 +235,70 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa // write _, err = output.Write(code) - return err } -var packageTemplate = `// Package {{.PackageName}} Code generated by swaggo/swag{{ if .GeneratedTime }} at {{ .Timestamp }}{{ end }}. DO NOT EDIT +var packageTemplate = `// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag{{ if .GeneratedTime }} at +// {{ .Timestamp }}{{ end }} + package {{.PackageName}} -import "github.com/swaggo/swag" +import ( + "bytes" + "encoding/json" + "strings" -const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}{{ .State }} = ` + "`{{ printDoc .Doc}}`" + ` + "github.com/alecthomas/template" + "github.com/swaggo/swag" +) + +var doc = ` + "`{{ printDoc .Doc}}`" + ` + +type swaggerInfo struct { + Version string + Host string + BasePath string + Schemes []string + Title string + Description string +} -// 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{ +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = swaggerInfo{ Version: {{ printf "%q" .Version}}, - Host: {{ printf "%q" .Host}}, + 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 }}{{ .State }}, - LeftDelim: {{ printf "%q" .LeftTemplateDelim}}, - RightDelim: {{ printf "%q" .RightTemplateDelim}}, +} + +type s struct{} + +func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + + t, err := template.New("swagger_info").Funcs(template.FuncMap{ + "marshal": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, + }).Parse(doc) + if err != nil { + return doc + } + + var tpl bytes.Buffer + if err := t.Execute(&tpl, sInfo); err != nil { + return doc + } + + return tpl.String() } func init() { - swag.Register(Swagger{{ .State }}Info{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), Swagger{{ .State }}Info{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}) + swag.Register(swag.Name, &s{}) } ` diff --git a/vendor/github.com/swaggo/swag/generics.go b/vendor/github.com/swaggo/swag/generics.go deleted file mode 100644 index 07344bbafb..0000000000 --- a/vendor/github.com/swaggo/swag/generics.go +++ /dev/null @@ -1,432 +0,0 @@ -//go:build go1.18 -// +build go1.18 - -package swag - -import ( - "errors" - "fmt" - "go/ast" - "strings" - "unicode" - - "github.com/go-openapi/spec" -) - -type genericTypeSpec struct { - TypeSpec *TypeSpecDef - Name string -} - -type formalParamType struct { - Name string - Type string -} - -func (t *genericTypeSpec) TypeName() string { - if t.TypeSpec != nil { - return t.TypeSpec.TypeName() - } - return t.Name -} - -func normalizeGenericTypeName(name string) string { - return strings.Replace(name, ".", "_", -1) -} - -func (pkgDefs *PackagesDefinitions) getTypeFromGenericParam(genericParam string, file *ast.File) (typeSpecDef *TypeSpecDef) { - if strings.HasPrefix(genericParam, "[]") { - typeSpecDef = pkgDefs.getTypeFromGenericParam(genericParam[2:], file) - if typeSpecDef == nil { - return nil - } - var expr ast.Expr - switch typeSpecDef.TypeSpec.Type.(type) { - case *ast.ArrayType, *ast.MapType: - expr = typeSpecDef.TypeSpec.Type - default: - name := typeSpecDef.TypeName() - expr = ast.NewIdent(name) - if _, ok := pkgDefs.uniqueDefinitions[name]; !ok { - pkgDefs.uniqueDefinitions[name] = typeSpecDef - } - } - return &TypeSpecDef{ - TypeSpec: &ast.TypeSpec{ - Name: ast.NewIdent(string(IgnoreNameOverridePrefix) + "array_" + typeSpecDef.TypeName()), - Type: &ast.ArrayType{ - Elt: expr, - }, - }, - Enums: typeSpecDef.Enums, - PkgPath: typeSpecDef.PkgPath, - ParentSpec: typeSpecDef.ParentSpec, - NotUnique: false, - } - } - - if strings.HasPrefix(genericParam, "map[") { - parts := strings.SplitN(genericParam[4:], "]", 2) - if len(parts) != 2 { - return nil - } - typeSpecDef = pkgDefs.getTypeFromGenericParam(parts[1], file) - if typeSpecDef == nil { - return nil - } - var expr ast.Expr - switch typeSpecDef.TypeSpec.Type.(type) { - case *ast.ArrayType, *ast.MapType: - expr = typeSpecDef.TypeSpec.Type - default: - name := typeSpecDef.TypeName() - expr = ast.NewIdent(name) - if _, ok := pkgDefs.uniqueDefinitions[name]; !ok { - pkgDefs.uniqueDefinitions[name] = typeSpecDef - } - } - return &TypeSpecDef{ - TypeSpec: &ast.TypeSpec{ - Name: ast.NewIdent(string(IgnoreNameOverridePrefix) + "map_" + parts[0] + "_" + typeSpecDef.TypeName()), - Type: &ast.MapType{ - Key: ast.NewIdent(parts[0]), //assume key is string or integer - Value: expr, - }, - }, - Enums: typeSpecDef.Enums, - PkgPath: typeSpecDef.PkgPath, - ParentSpec: typeSpecDef.ParentSpec, - NotUnique: false, - } - - } - if IsGolangPrimitiveType(genericParam) { - return &TypeSpecDef{ - TypeSpec: &ast.TypeSpec{ - Name: ast.NewIdent(genericParam), - Type: ast.NewIdent(genericParam), - }, - } - } - return pkgDefs.FindTypeSpec(genericParam, file) -} - -func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string) *TypeSpecDef { - if original == nil || original.TypeSpec.TypeParams == nil || len(original.TypeSpec.TypeParams.List) == 0 { - return original - } - - name, genericParams := splitGenericsTypeName(fullGenericForm) - if genericParams == nil { - return nil - } - - //generic[x,y any,z any] considered, TODO what if the type is not `any`, but a concrete one, such as `int32|int64` or an certain interface{} - var formals []formalParamType - for _, field := range original.TypeSpec.TypeParams.List { - for _, ident := range field.Names { - formal := formalParamType{Name: ident.Name} - if ident, ok := field.Type.(*ast.Ident); ok { - formal.Type = ident.Name - } - formals = append(formals, formal) - } - } - if len(genericParams) != len(formals) { - return nil - } - genericParamTypeDefs := map[string]*genericTypeSpec{} - - for i, genericParam := range genericParams { - var typeDef *TypeSpecDef - if !IsGolangPrimitiveType(genericParam) { - typeDef = pkgDefs.getTypeFromGenericParam(genericParam, file) - if typeDef != nil { - genericParam = typeDef.TypeName() - if _, ok := pkgDefs.uniqueDefinitions[genericParam]; !ok { - pkgDefs.uniqueDefinitions[genericParam] = typeDef - } - } - } - genericParamTypeDefs[formals[i].Name] = &genericTypeSpec{ - TypeSpec: typeDef, - Name: genericParam, - } - } - - name = fmt.Sprintf("%s%s-", string(IgnoreNameOverridePrefix), original.TypeName()) - var nameParts []string - for _, def := range formals { - if specDef, ok := genericParamTypeDefs[def.Name]; ok { - nameParts = append(nameParts, specDef.TypeName()) - } - } - - name += normalizeGenericTypeName(strings.Join(nameParts, "-")) - - if typeSpec, ok := pkgDefs.uniqueDefinitions[name]; ok { - return typeSpec - } - - parametrizedTypeSpec := &TypeSpecDef{ - File: original.File, - PkgPath: original.PkgPath, - TypeSpec: &ast.TypeSpec{ - Name: &ast.Ident{ - Name: name, - NamePos: original.TypeSpec.Name.NamePos, - Obj: original.TypeSpec.Name.Obj, - }, - Doc: original.TypeSpec.Doc, - Assign: original.TypeSpec.Assign, - }, - } - pkgDefs.uniqueDefinitions[name] = parametrizedTypeSpec - - parametrizedTypeSpec.TypeSpec.Type = pkgDefs.resolveGenericType(original.File, original.TypeSpec.Type, genericParamTypeDefs) - - return parametrizedTypeSpec -} - -// splitGenericsTypeName splits a generic struct name in his parts -func splitGenericsTypeName(fullGenericForm string) (string, []string) { - //remove all spaces character - fullGenericForm = strings.Map(func(r rune) rune { - if unicode.IsSpace(r) { - return -1 - } - return r - }, fullGenericForm) - - // split only at the first '[' and remove the last ']' - if fullGenericForm[len(fullGenericForm)-1] != ']' { - return "", nil - } - - genericParams := strings.SplitN(fullGenericForm[:len(fullGenericForm)-1], "[", 2) - if len(genericParams) == 1 { - return "", nil - } - - // generic type name - genericTypeName := genericParams[0] - - depth := 0 - genericParams = strings.FieldsFunc(genericParams[1], func(r rune) bool { - if r == '[' { - depth++ - } else if r == ']' { - depth-- - } else if r == ',' && depth == 0 { - return true - } - return false - }) - if depth != 0 { - return "", nil - } - - return genericTypeName, genericParams -} - -func (pkgDefs *PackagesDefinitions) getParametrizedType(genTypeSpec *genericTypeSpec) ast.Expr { - if genTypeSpec.TypeSpec != nil && strings.Contains(genTypeSpec.Name, ".") { - parts := strings.SplitN(genTypeSpec.Name, ".", 2) - return &ast.SelectorExpr{ - X: &ast.Ident{Name: parts[0]}, - Sel: &ast.Ident{Name: parts[1]}, - } - } - - //a primitive type name or a type name in current package - return &ast.Ident{Name: genTypeSpec.Name} -} - -func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) ast.Expr { - switch astExpr := expr.(type) { - case *ast.Ident: - if genTypeSpec, ok := genericParamTypeDefs[astExpr.Name]; ok { - return pkgDefs.getParametrizedType(genTypeSpec) - } - case *ast.ArrayType: - return &ast.ArrayType{ - Elt: pkgDefs.resolveGenericType(file, astExpr.Elt, genericParamTypeDefs), - Len: astExpr.Len, - Lbrack: astExpr.Lbrack, - } - case *ast.MapType: - return &ast.MapType{ - Map: astExpr.Map, - Key: pkgDefs.resolveGenericType(file, astExpr.Key, genericParamTypeDefs), - Value: pkgDefs.resolveGenericType(file, astExpr.Value, genericParamTypeDefs), - } - case *ast.StarExpr: - return &ast.StarExpr{ - Star: astExpr.Star, - X: pkgDefs.resolveGenericType(file, astExpr.X, genericParamTypeDefs), - } - case *ast.IndexExpr, *ast.IndexListExpr: - fullGenericName, _ := getGenericFieldType(file, expr, genericParamTypeDefs) - typeDef := pkgDefs.FindTypeSpec(fullGenericName, file) - if typeDef != nil { - return typeDef.TypeSpec.Name - } - case *ast.StructType: - newStructTypeDef := &ast.StructType{ - Struct: astExpr.Struct, - Incomplete: astExpr.Incomplete, - Fields: &ast.FieldList{ - Opening: astExpr.Fields.Opening, - Closing: astExpr.Fields.Closing, - }, - } - - for _, field := range astExpr.Fields.List { - newField := &ast.Field{ - Type: field.Type, - Doc: field.Doc, - Names: field.Names, - Tag: field.Tag, - Comment: field.Comment, - } - - newField.Type = pkgDefs.resolveGenericType(file, field.Type, genericParamTypeDefs) - - newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField) - } - return newStructTypeDef - } - return expr -} - -func getExtendedGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) { - switch fieldType := field.(type) { - case *ast.ArrayType: - fieldName, err := getExtendedGenericFieldType(file, fieldType.Elt, genericParamTypeDefs) - return "[]" + fieldName, err - case *ast.StarExpr: - return getExtendedGenericFieldType(file, fieldType.X, genericParamTypeDefs) - case *ast.Ident: - if genericParamTypeDefs != nil { - if typeSpec, ok := genericParamTypeDefs[fieldType.Name]; ok { - return typeSpec.Name, nil - } - } - if fieldType.Obj == nil { - return fieldType.Name, nil - } - - tSpec := &TypeSpecDef{ - File: file, - TypeSpec: fieldType.Obj.Decl.(*ast.TypeSpec), - PkgPath: file.Name.Name, - } - return tSpec.TypeName(), nil - default: - return getFieldType(file, field, genericParamTypeDefs) - } -} - -func getGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) { - var fullName string - var baseName string - var err error - switch fieldType := field.(type) { - case *ast.IndexListExpr: - baseName, err = getGenericTypeName(file, fieldType.X) - if err != nil { - return "", err - } - fullName = baseName + "[" - - for _, index := range fieldType.Indices { - fieldName, err := getExtendedGenericFieldType(file, index, genericParamTypeDefs) - if err != nil { - return "", err - } - - fullName += fieldName + "," - } - - fullName = strings.TrimRight(fullName, ",") + "]" - case *ast.IndexExpr: - baseName, err = getGenericTypeName(file, fieldType.X) - if err != nil { - return "", err - } - - indexName, err := getExtendedGenericFieldType(file, fieldType.Index, genericParamTypeDefs) - if err != nil { - return "", err - } - - fullName = fmt.Sprintf("%s[%s]", baseName, indexName) - } - - if fullName == "" { - return "", fmt.Errorf("unknown field type %#v", field) - } - - var packageName string - if !strings.Contains(baseName, ".") { - if file.Name == nil { - return "", errors.New("file name is nil") - } - packageName, _ = getFieldType(file, file.Name, genericParamTypeDefs) - } - - return strings.TrimLeft(fmt.Sprintf("%s.%s", packageName, fullName), "."), nil -} - -func getGenericTypeName(file *ast.File, field ast.Expr) (string, error) { - switch fieldType := field.(type) { - case *ast.Ident: - if fieldType.Obj == nil { - return fieldType.Name, nil - } - - tSpec := &TypeSpecDef{ - File: file, - TypeSpec: fieldType.Obj.Decl.(*ast.TypeSpec), - PkgPath: file.Name.Name, - } - return tSpec.TypeName(), nil - case *ast.ArrayType: - tSpec := &TypeSpecDef{ - File: file, - TypeSpec: fieldType.Elt.(*ast.Ident).Obj.Decl.(*ast.TypeSpec), - PkgPath: file.Name.Name, - } - return tSpec.TypeName(), nil - case *ast.SelectorExpr: - return fmt.Sprintf("%s.%s", fieldType.X.(*ast.Ident).Name, fieldType.Sel.Name), nil - } - return "", fmt.Errorf("unknown type %#v", field) -} - -func (parser *Parser) parseGenericTypeExpr(file *ast.File, typeExpr ast.Expr) (*spec.Schema, error) { - switch expr := typeExpr.(type) { - // suppress debug messages for these types - case *ast.InterfaceType: - case *ast.StructType: - case *ast.Ident: - case *ast.StarExpr: - case *ast.SelectorExpr: - case *ast.ArrayType: - case *ast.MapType: - case *ast.FuncType: - case *ast.IndexExpr, *ast.IndexListExpr: - name, err := getExtendedGenericFieldType(file, expr, nil) - if err == nil { - if schema, err := parser.getTypeSchema(name, file, false); err == nil { - return schema, nil - } - } - - parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead. (%s)\n", typeExpr, err) - default: - parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr) - } - - return PrimitiveSchema(OBJECT), nil -} diff --git a/vendor/github.com/swaggo/swag/golist.go b/vendor/github.com/swaggo/swag/golist.go deleted file mode 100644 index fa0b2cd9ee..0000000000 --- a/vendor/github.com/swaggo/swag/golist.go +++ /dev/null @@ -1,78 +0,0 @@ -package swag - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "go/build" - "os/exec" - "path/filepath" -) - -func listPackages(ctx context.Context, dir string, env []string, args ...string) (pkgs []*build.Package, finalErr error) { - cmd := exec.CommandContext(ctx, "go", append([]string{"list", "-json", "-e"}, args...)...) - cmd.Env = env - cmd.Dir = dir - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - var stderrBuf bytes.Buffer - cmd.Stderr = &stderrBuf - defer func() { - if stderrBuf.Len() > 0 { - finalErr = fmt.Errorf("%v\n%s", finalErr, stderrBuf.Bytes()) - } - }() - - err = cmd.Start() - if err != nil { - return nil, err - } - dec := json.NewDecoder(stdout) - for dec.More() { - var pkg build.Package - err = dec.Decode(&pkg) - if err != nil { - return nil, err - } - pkgs = append(pkgs, &pkg) - } - err = cmd.Wait() - if err != nil { - return nil, err - } - return pkgs, nil -} - -func (parser *Parser) getAllGoFileInfoFromDepsByList(pkg *build.Package, parseFlag ParseFlag) error { - ignoreInternal := pkg.Goroot && !parser.ParseInternal - if ignoreInternal { // ignored internal - return nil - } - - if parser.skipPackageByPrefix(pkg.ImportPath) { - return nil // ignored by user-defined package path prefixes - } - - srcDir := pkg.Dir - var err error - for i := range pkg.GoFiles { - err = parser.parseFile(pkg.ImportPath, filepath.Join(srcDir, pkg.GoFiles[i]), nil, parseFlag) - if err != nil { - return err - } - } - - // parse .go source files that import "C" - for i := range pkg.CgoFiles { - err = parser.parseFile(pkg.ImportPath, filepath.Join(srcDir, pkg.CgoFiles[i]), nil, parseFlag) - if err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/swaggo/swag/operation.go b/vendor/github.com/swaggo/swag/operation.go index 169510ffc1..4450bf697e 100644 --- a/vendor/github.com/swaggo/swag/operation.go +++ b/vendor/github.com/swaggo/swag/operation.go @@ -8,30 +8,24 @@ import ( "go/token" "net/http" "os" - "path/filepath" "regexp" + "sort" "strconv" "strings" + "github.com/go-openapi/jsonreference" "github.com/go-openapi/spec" "golang.org/x/tools/go/loader" ) -// RouteProperties describes HTTP properties of a single router comment. -type RouteProperties struct { - HTTPMethod string - Path string - Deprecated bool -} - // Operation describes a single API operation on a path. // For more information: https://github.com/swaggo/swag#api-operation type Operation struct { - parser *Parser - codeExampleFilesDir string + HTTPMethod string + Path string spec.Operation - RouterProperties []RouteProperties - State string + + parser *Parser } var mimeTypeAliases = map[string]string{ @@ -52,160 +46,75 @@ var mimeTypeAliases = map[string]string{ var mimeTypePattern = regexp.MustCompile("^[^/]+/[^/]+$") // NewOperation creates a new Operation with default properties. -// map[int]Response. -func NewOperation(parser *Parser, options ...func(*Operation)) *Operation { - if parser == nil { - parser = New() - } - - result := &Operation{ - parser: parser, - RouterProperties: []RouteProperties{}, +// map[int]Response +func NewOperation() *Operation { + return &Operation{ + HTTPMethod: "get", Operation: spec.Operation{ - OperationProps: spec.OperationProps{ - ID: "", - Description: "", - Summary: "", - Security: nil, - ExternalDocs: nil, - Deprecated: false, - Tags: []string{}, - Consumes: []string{}, - Produces: []string{}, - Schemes: []string{}, - Parameters: []spec.Parameter{}, - Responses: &spec.Responses{ - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{}, - }, - ResponsesProps: spec.ResponsesProps{ - Default: nil, - StatusCodeResponses: make(map[int]spec.Response), - }, - }, - }, - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{}, - }, + OperationProps: spec.OperationProps{}, }, - codeExampleFilesDir: "", - } - - for _, option := range options { - option(result) - } - - return result -} - -// SetCodeExampleFilesDirectory sets the directory to search for codeExamples. -func SetCodeExampleFilesDirectory(directoryPath string) func(*Operation) { - return func(o *Operation) { - o.codeExampleFilesDir = directoryPath } } // ParseComment parses comment for given comment string and returns error if error occurs. func (operation *Operation) ParseComment(comment string, astFile *ast.File) error { - commentLine := strings.TrimSpace(strings.TrimLeft(comment, "/")) + commentLine := strings.TrimSpace(strings.TrimLeft(comment, "//")) if len(commentLine) == 0 { return nil } - - fields := FieldsByAnySpace(commentLine, 2) - attribute := fields[0] + attribute := strings.Fields(commentLine)[0] + lineRemainder := strings.TrimSpace(commentLine[len(attribute):]) lowerAttribute := strings.ToLower(attribute) - var lineRemainder string - if len(fields) > 1 { - lineRemainder = fields[1] - } + + var err error switch lowerAttribute { - case stateAttr: - operation.ParseStateComment(lineRemainder) - case descriptionAttr: + case "@description": operation.ParseDescriptionComment(lineRemainder) - case descriptionMarkdownAttr: + case "@description.markdown": commentInfo, err := getMarkdownForTag(lineRemainder, operation.parser.markdownFileDir) if err != nil { return err } - operation.ParseDescriptionComment(string(commentInfo)) - case summaryAttr: + case "@summary": operation.Summary = lineRemainder - case idAttr: + case "@id": operation.ID = lineRemainder - case tagsAttr: + case "@tags": operation.ParseTagsComment(lineRemainder) - case acceptAttr: - return operation.ParseAcceptComment(lineRemainder) - case produceAttr: - return operation.ParseProduceComment(lineRemainder) - case paramAttr: - return operation.ParseParamComment(lineRemainder, astFile) - case successAttr, failureAttr, responseAttr: - return operation.ParseResponseComment(lineRemainder, astFile) - case headerAttr: - return operation.ParseResponseHeaderComment(lineRemainder, astFile) - case routerAttr: - return operation.ParseRouterComment(lineRemainder, false) - case deprecatedRouterAttr: - return operation.ParseRouterComment(lineRemainder, true) - case securityAttr: - return operation.ParseSecurityComment(lineRemainder) - case deprecatedAttr: + case "@accept": + err = operation.ParseAcceptComment(lineRemainder) + case "@produce": + err = operation.ParseProduceComment(lineRemainder) + case "@param": + err = operation.ParseParamComment(lineRemainder, astFile) + case "@success", "@failure": + err = operation.ParseResponseComment(lineRemainder, astFile) + case "@header": + err = operation.ParseResponseHeaderComment(lineRemainder, astFile) + case "@router": + err = operation.ParseRouterComment(lineRemainder) + case "@security": + err = operation.ParseSecurityComment(lineRemainder) + case "@deprecated": operation.Deprecate() - case xCodeSamplesAttr: - return operation.ParseCodeSample(attribute, commentLine, lineRemainder) default: - return operation.ParseMetadata(attribute, lowerAttribute, lineRemainder) + err = operation.ParseMetadata(attribute, lowerAttribute, lineRemainder) } - return nil + return err } -// ParseCodeSample parse code sample. -func (operation *Operation) ParseCodeSample(attribute, _, lineRemainder string) error { - if lineRemainder == "file" { - data, err := getCodeExampleForSummary(operation.Summary, operation.codeExampleFilesDir) - if err != nil { - return err - } - - var valueJSON interface{} - - err = json.Unmarshal(data, &valueJSON) - if err != nil { - return fmt.Errorf("annotation %s need a valid json value", attribute) - } - - // don't use the method provided by spec lib, because it will call toLower() on attribute names, which is wrongly - operation.Extensions[attribute[1:]] = valueJSON - - return nil - } - - // Fallback into existing logic - return operation.ParseMetadata(attribute, strings.ToLower(attribute), lineRemainder) -} - -// ParseStateComment parse state comment. -func (operation *Operation) ParseStateComment(lineRemainder string) { - operation.State = lineRemainder -} - -// ParseDescriptionComment parse description comment. +// ParseDescriptionComment godoc func (operation *Operation) ParseDescriptionComment(lineRemainder string) { if operation.Description == "" { operation.Description = lineRemainder - return } - operation.Description += "\n" + lineRemainder } -// ParseMetadata parse metadata. +// ParseMetadata godoc func (operation *Operation) ParseMetadata(attribute, lowerAttribute, lineRemainder string) error { // parsing specific meta data extensions if strings.HasPrefix(lowerAttribute, "@x-") { @@ -214,141 +123,122 @@ func (operation *Operation) ParseMetadata(attribute, lowerAttribute, lineRemaind } var valueJSON interface{} - - err := json.Unmarshal([]byte(lineRemainder), &valueJSON) - if err != nil { + if err := json.Unmarshal([]byte(lineRemainder), &valueJSON); err != nil { return fmt.Errorf("annotation %s need a valid json value", attribute) } - - // don't use the method provided by spec lib, because it will call toLower() on attribute names, which is wrongly - operation.Extensions[attribute[1:]] = valueJSON + operation.Operation.AddExtension(attribute[1:], valueJSON) // Trim "@" at head } - return nil } -var paramPattern = regexp.MustCompile(`(\S+)\s+(\w+)\s+([\S. ]+?)\s+(\w+)\s+"([^"]+)"`) - -func findInSlice(arr []string, target string) bool { - for _, str := range arr { - if str == target { - return true - } - } - - return false -} +var paramPattern = regexp.MustCompile(`(\S+)[\s]+([\w]+)[\s]+([\S.]+)[\s]+([\w]+)[\s]+"([^"]+)"`) // ParseParamComment parses params return []string of param properties // E.g. @Param queryText formData string true "The email for login" -// -// [param name] [paramType] [data type] [is mandatory?] [Comment] -// -// E.g. @Param some_id path int true "Some ID". +// [param name] [paramType] [data type] [is mandatory?] [Comment] +// E.g. @Param some_id path int true "Some ID" func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.File) error { matches := paramPattern.FindStringSubmatch(commentLine) if len(matches) != 6 { return fmt.Errorf("missing required param comment parameters \"%s\"", commentLine) } - name := matches[1] paramType := matches[2] refType := TransToValidSchemeType(matches[3]) // Detect refType - objectType := OBJECT - + objectType := "object" if strings.HasPrefix(refType, "[]") { - objectType = ARRAY + objectType = "array" refType = strings.TrimPrefix(refType, "[]") refType = TransToValidSchemeType(refType) } else if IsPrimitiveType(refType) || paramType == "formData" && refType == "file" { - objectType = PRIMITIVE - } - - var enums []interface{} - if !IsPrimitiveType(refType) { - schema, _ := operation.parser.getTypeSchema(refType, astFile, false) - if schema != nil && len(schema.Type) == 1 && schema.Enum != nil { - if objectType == OBJECT { - objectType = PRIMITIVE - } - refType = TransToValidSchemeType(schema.Type[0]) - enums = schema.Enum - } + objectType = "primitive" } requiredText := strings.ToLower(matches[4]) - required := requiredText == "true" || requiredText == requiredLabel + required := requiredText == "true" || requiredText == "required" description := matches[5] - param := createParameter(paramType, description, name, objectType, refType, required, enums, operation.parser.collectionFormatInQuery) + param := createParameter(paramType, description, name, refType, required) switch paramType { - case "path", "header", "query", "formData": + case "path", "header", "formData": + switch objectType { + case "array", "object": + return fmt.Errorf("%s is not supported type for %s", refType, paramType) + } + case "query": switch objectType { - case ARRAY: - if !IsPrimitiveType(refType) && !(refType == "file" && paramType == "formData") { + case "array": + if !IsPrimitiveType(refType) { return fmt.Errorf("%s is not supported array type for %s", refType, paramType) } - case PRIMITIVE: - break - case OBJECT: - schema, err := operation.parser.getTypeSchema(refType, astFile, false) + param.SimpleSchema.Type = "array" + if operation.parser != nil { + param.CollectionFormat = TransToValidCollectionFormat(operation.parser.collectionFormatInQuery) + } + param.SimpleSchema.Items = &spec.Items{ + SimpleSchema: spec.SimpleSchema{ + Type: refType, + }, + } + case "object": + refType, typeSpec, err := operation.registerSchemaType(refType, astFile) + if err != nil { + return err + } + structType, ok := typeSpec.Type.(*ast.StructType) + if !ok { + return fmt.Errorf("%s is not supported type for %s", refType, paramType) + } + refSplit := strings.Split(refType, ".") + schema, err := operation.parser.parseStruct(refSplit[0], structType.Fields) if err != nil { return err } - if len(schema.Properties) == 0 { return nil } - - items := schema.Properties.ToOrderedSchemaItems() - - for _, item := range items { - name, prop := item.Name, &item.Schema - if len(prop.Type) == 0 { - prop = operation.parser.getUnderlyingSchema(prop) - if len(prop.Type) == 0 { - continue + find := func(arr []string, target string) bool { + for _, str := range arr { + if str == target { + return true } } - - nameOverrideType := paramType - // query also uses formData tags - if paramType == "query" { - nameOverrideType = "formData" - } - // load overridden type specific name from extensions if exists - if nameVal, ok := item.Schema.Extensions[nameOverrideType]; ok { - name = nameVal.(string) + return false + } + orderedNames := make([]string, 0, len(schema.Properties)) + for k := range schema.Properties { + orderedNames = append(orderedNames, k) + } + sort.Strings(orderedNames) + for _, name := range orderedNames { + prop := schema.Properties[name] + if len(prop.Type) == 0 { + continue } - - switch { - case prop.Type[0] == ARRAY: - if prop.Items.Schema == nil { - continue - } - itemSchema := prop.Items.Schema - if len(itemSchema.Type) == 0 { - itemSchema = operation.parser.getUnderlyingSchema(prop.Items.Schema) + if prop.Type[0] == "array" && + prop.Items.Schema != nil && + len(prop.Items.Schema.Type) > 0 && + IsSimplePrimitiveType(prop.Items.Schema.Type[0]) { + param = createParameter(paramType, prop.Description, name, prop.Type[0], find(schema.Required, name)) + param.SimpleSchema.Type = prop.Type[0] + if operation.parser != nil && operation.parser.collectionFormatInQuery != "" && param.CollectionFormat == "" { + param.CollectionFormat = TransToValidCollectionFormat(operation.parser.collectionFormatInQuery) } - if len(itemSchema.Type) == 0 { - continue + param.SimpleSchema.Items = &spec.Items{ + SimpleSchema: spec.SimpleSchema{ + Type: prop.Items.Schema.Type[0], + }, } - if !IsSimplePrimitiveType(itemSchema.Type[0]) { - continue - } - param = createParameter(paramType, prop.Description, name, prop.Type[0], itemSchema.Type[0], findInSlice(schema.Required, item.Name), itemSchema.Enum, operation.parser.collectionFormatInQuery) - - case IsSimplePrimitiveType(prop.Type[0]): - param = createParameter(paramType, prop.Description, name, PRIMITIVE, prop.Type[0], findInSlice(schema.Required, item.Name), nil, operation.parser.collectionFormatInQuery) - default: - operation.parser.debug.Printf("skip field [%s] in %s is not supported type for %s", name, refType, paramType) + } else if IsSimplePrimitiveType(prop.Type[0]) { + param = createParameter(paramType, prop.Description, name, prop.Type[0], find(schema.Required, name)) + } else { + Println(fmt.Sprintf("skip field [%s] in %s is not supported type for %s", name, refType, paramType)) continue } - param.Nullable = prop.Nullable param.Format = prop.Format param.Default = prop.Default @@ -368,173 +258,187 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F param.CommonValidations.Enum = prop.Enum operation.Operation.Parameters = append(operation.Operation.Parameters, param) } - return nil } case "body": - if objectType == PRIMITIVE { - param.Schema = PrimitiveSchema(refType) - } else { - schema, err := operation.parseAPIObjectSchema(commentLine, objectType, refType, astFile) + switch objectType { + case "primitive": + param.Schema.Type = spec.StringOrArray{refType} + case "array": + refType = "[]" + refType + fallthrough + case "object": + schema, err := operation.parseObjectSchema(refType, astFile) if err != nil { return err } - param.Schema = schema } default: return fmt.Errorf("%s is not supported paramType", paramType) } - err := operation.parseParamAttribute(commentLine, objectType, refType, paramType, ¶m) - - if err != nil { + if err := operation.parseAndExtractionParamAttribute(commentLine, objectType, refType, ¶m); err != nil { return err } - operation.Operation.Parameters = append(operation.Operation.Parameters, param) - return nil } -const ( - formTag = "form" - jsonTag = "json" - uriTag = "uri" - headerTag = "header" - bindingTag = "binding" - defaultTag = "default" - enumsTag = "enums" - exampleTag = "example" - schemaExampleTag = "schemaExample" - formatTag = "format" - validateTag = "validate" - minimumTag = "minimum" - maximumTag = "maximum" - minLengthTag = "minLength" - maxLengthTag = "maxLength" - multipleOfTag = "multipleOf" - readOnlyTag = "readonly" - extensionsTag = "extensions" - collectionFormatTag = "collectionFormat" -) +func (operation *Operation) registerSchemaType(schemaType string, astFile *ast.File) (string, *ast.TypeSpec, error) { + if !strings.ContainsRune(schemaType, '.') { + if astFile == nil { + return schemaType, nil, fmt.Errorf("no package name for type %s", schemaType) + } + schemaType = fullTypeName(astFile.Name.String(), schemaType) + } + refSplit := strings.Split(schemaType, ".") + pkgName := refSplit[0] + typeName := refSplit[1] + if typeSpec, ok := operation.parser.TypeDefinitions[pkgName][typeName]; ok { + operation.parser.registerTypes[schemaType] = typeSpec + return schemaType, typeSpec, nil + } + var typeSpec *ast.TypeSpec + if astFile == nil { + return schemaType, nil, fmt.Errorf("can not register schema type: %q reason: astFile == nil", schemaType) + } + for _, imp := range astFile.Imports { + if imp.Name != nil && imp.Name.Name == pkgName { // the import had an alias that matched + break + } + impPath := strings.Replace(imp.Path.Value, `"`, ``, -1) + if strings.HasSuffix(impPath, "/"+pkgName) { + var err error + typeSpec, err = findTypeDef(impPath, typeName) + if err != nil { + return schemaType, nil, fmt.Errorf("can not find type def: %q error: %s", schemaType, err) + } + break + } + } + + if typeSpec == nil { + return schemaType, nil, fmt.Errorf("can not find schema type: %q", schemaType) + } + + if _, ok := operation.parser.TypeDefinitions[pkgName]; !ok { + operation.parser.TypeDefinitions[pkgName] = make(map[string]*ast.TypeSpec) + } + + operation.parser.TypeDefinitions[pkgName][typeName] = typeSpec + operation.parser.registerTypes[schemaType] = typeSpec + return schemaType, typeSpec, nil +} var regexAttributes = map[string]*regexp.Regexp{ // for Enums(A, B) - enumsTag: regexp.MustCompile(`(?i)\s+enums\(.*\)`), - // for maximum(0) - maximumTag: regexp.MustCompile(`(?i)\s+maxinum|maximum\(.*\)`), - // for minimum(0) - minimumTag: regexp.MustCompile(`(?i)\s+mininum|minimum\(.*\)`), - // for default(0) - defaultTag: regexp.MustCompile(`(?i)\s+default\(.*\)`), + "enums": regexp.MustCompile(`(?i)\s+enums\(.*\)`), + // for Minimum(0) + "maxinum": regexp.MustCompile(`(?i)\s+maxinum\(.*\)`), + // for Maximum(0) + "mininum": regexp.MustCompile(`(?i)\s+mininum\(.*\)`), + // for Maximum(0) + "default": regexp.MustCompile(`(?i)\s+default\(.*\)`), // for minlength(0) - minLengthTag: regexp.MustCompile(`(?i)\s+minlength\(.*\)`), + "minlength": regexp.MustCompile(`(?i)\s+minlength\(.*\)`), // for maxlength(0) - maxLengthTag: regexp.MustCompile(`(?i)\s+maxlength\(.*\)`), + "maxlength": regexp.MustCompile(`(?i)\s+maxlength\(.*\)`), // for format(email) - formatTag: regexp.MustCompile(`(?i)\s+format\(.*\)`), - // for extensions(x-example=test) - extensionsTag: regexp.MustCompile(`(?i)\s+extensions\(.*\)`), + "format": regexp.MustCompile(`(?i)\s+format\(.*\)`), // for collectionFormat(csv) - collectionFormatTag: regexp.MustCompile(`(?i)\s+collectionFormat\(.*\)`), - // example(0) - exampleTag: regexp.MustCompile(`(?i)\s+example\(.*\)`), - // schemaExample(0) - schemaExampleTag: regexp.MustCompile(`(?i)\s+schemaExample\(.*\)`), + "collectionFormat": regexp.MustCompile(`(?i)\s+collectionFormat\(.*\)`), } -func (operation *Operation) parseParamAttribute(comment, objectType, schemaType, paramType string, param *spec.Parameter) error { +func (operation *Operation) parseAndExtractionParamAttribute(commentLine, objectType, schemaType string, param *spec.Parameter) error { schemaType = TransToValidSchemeType(schemaType) - for attrKey, re := range regexAttributes { - attr, err := findAttr(re, comment) + attr, err := findAttr(re, commentLine) if err != nil { continue } - switch attrKey { - case enumsTag: - err = setEnumParam(param, attr, objectType, schemaType, paramType) - case minimumTag, maximumTag: - err = setNumberParam(param, attrKey, schemaType, attr, comment) - case defaultTag: - err = setDefault(param, schemaType, attr) - case minLengthTag, maxLengthTag: - err = setStringParam(param, attrKey, schemaType, attr, comment) - case formatTag: + case "enums": + err := setEnumParam(attr, schemaType, param) + if err != nil { + return err + } + case "maxinum": + n, err := setNumberParam(attrKey, schemaType, attr, commentLine) + if err != nil { + return err + } + param.Maximum = &n + case "mininum": + n, err := setNumberParam(attrKey, schemaType, attr, commentLine) + if err != nil { + return err + } + param.Minimum = &n + case "default": + value, err := defineType(schemaType, attr) + if err != nil { + return nil + } + param.Default = value + case "maxlength": + n, err := setStringParam(attrKey, schemaType, attr, commentLine) + if err != nil { + return err + } + param.MaxLength = &n + case "minlength": + n, err := setStringParam(attrKey, schemaType, attr, commentLine) + if err != nil { + return err + } + param.MinLength = &n + case "format": param.Format = attr - case exampleTag: - err = setExample(param, schemaType, attr) - case schemaExampleTag: - err = setSchemaExample(param, schemaType, attr) - case extensionsTag: - param.Extensions = setExtensionParam(attr) - case collectionFormatTag: - err = setCollectionFormatParam(param, attrKey, objectType, attr, comment) - } - - if err != nil { - return err + case "collectionFormat": + n, err := setCollectionFormatParam(attrKey, objectType, attr, commentLine) + if err != nil { + return err + } + param.CollectionFormat = n } } - return nil } func findAttr(re *regexp.Regexp, commentLine string) (string, error) { attr := re.FindString(commentLine) - - l, r := strings.Index(attr, "("), strings.Index(attr, ")") + l := strings.Index(attr, "(") + r := strings.Index(attr, ")") if l == -1 || r == -1 { return "", fmt.Errorf("can not find regex=%s, comment=%s", re.String(), commentLine) } - return strings.TrimSpace(attr[l+1 : r]), nil } -func setStringParam(param *spec.Parameter, name, schemaType, attr, commentLine string) error { - if schemaType != STRING { - return fmt.Errorf("%s is attribute to set to a number. comment=%s got=%s", name, commentLine, schemaType) +func setStringParam(name, schemaType, attr, commentLine string) (int64, error) { + if schemaType != "string" { + return 0, fmt.Errorf("%s is attribute to set to a number. comment=%s got=%s", name, commentLine, schemaType) } - n, err := strconv.ParseInt(attr, 10, 64) if err != nil { - return fmt.Errorf("%s is allow only a number got=%s", name, attr) - } - - switch name { - case minLengthTag: - param.MinLength = &n - case maxLengthTag: - param.MaxLength = &n + return 0, fmt.Errorf("%s is allow only a number got=%s", name, attr) } - - return nil + return n, nil } -func setNumberParam(param *spec.Parameter, name, schemaType, attr, commentLine string) error { - switch schemaType { - case INTEGER, NUMBER: - n, err := strconv.ParseFloat(attr, 64) - if err != nil { - return fmt.Errorf("maximum is allow only a number. comment=%s got=%s", commentLine, attr) - } - - switch name { - case minimumTag: - param.Minimum = &n - case maximumTag: - param.Maximum = &n - } - - return nil - default: - return fmt.Errorf("%s is attribute to set to a number. comment=%s got=%s", name, commentLine, schemaType) +func setNumberParam(name, schemaType, attr, commentLine string) (float64, error) { + if schemaType != "integer" && schemaType != "number" { + return 0, fmt.Errorf("%s is attribute to set to a number. comment=%s got=%s", name, commentLine, schemaType) } + n, err := strconv.ParseFloat(attr, 64) + if err != nil { + return 0, fmt.Errorf("maximum is allow only a number. comment=%s got=%s", commentLine, attr) + } + return n, nil } -func setEnumParam(param *spec.Parameter, attr, objectType, schemaType, paramType string) error { +func setEnumParam(attr, schemaType string, param *spec.Parameter) error { for _, e := range strings.Split(attr, ",") { e = strings.TrimSpace(e) @@ -542,131 +446,51 @@ func setEnumParam(param *spec.Parameter, attr, objectType, schemaType, paramType if err != nil { return err } - - switch objectType { - case ARRAY: - param.Items.Enum = append(param.Items.Enum, value) - default: - switch paramType { - case "body": - param.Schema.Enum = append(param.Schema.Enum, value) - default: - param.Enum = append(param.Enum, value) - } - } - } - - return nil -} - -func setExtensionParam(attr string) spec.Extensions { - extensions := spec.Extensions{} - - for _, val := range splitNotWrapped(attr, ',') { - parts := strings.SplitN(val, "=", 2) - if len(parts) == 2 { - extensions.Add(parts[0], parts[1]) - - continue - } - - if len(parts[0]) > 0 && string(parts[0][0]) == "!" { - extensions.Add(parts[0][1:], false) - - continue - } - - extensions.Add(parts[0], true) - } - - return extensions -} - -func setCollectionFormatParam(param *spec.Parameter, name, schemaType, attr, commentLine string) error { - if schemaType == ARRAY { - param.CollectionFormat = TransToValidCollectionFormat(attr) - - return nil - } - - return fmt.Errorf("%s is attribute to set to an array. comment=%s got=%s", name, commentLine, schemaType) -} - -func setDefault(param *spec.Parameter, schemaType string, value string) error { - val, err := defineType(schemaType, value) - if err != nil { - return nil // Don't set a default value if it's not valid - } - - param.Default = val - - return nil -} - -func setSchemaExample(param *spec.Parameter, schemaType string, value string) error { - val, err := defineType(schemaType, value) - if err != nil { - return nil // Don't set a example value if it's not valid - } - // skip schema - if param.Schema == nil { - return nil + param.Enum = append(param.Enum, value) } - - switch v := val.(type) { - case string: - // replaces \r \n \t in example string values. - param.Schema.Example = strings.NewReplacer(`\r`, "\r", `\n`, "\n", `\t`, "\t").Replace(v) - default: - param.Schema.Example = val - } - return nil } -func setExample(param *spec.Parameter, schemaType string, value string) error { - val, err := defineType(schemaType, value) - if err != nil { - return nil // Don't set a example value if it's not valid +func setCollectionFormatParam(name, schemaType, attr, commentLine string) (string, error) { + if schemaType != "array" { + return "", fmt.Errorf("%s is attribute to set to an array. comment=%s got=%s", name, commentLine, schemaType) } - - param.Example = val - - return nil + return TransToValidCollectionFormat(attr), nil } -// defineType enum value define the type (object and array unsupported). -func defineType(schemaType string, value string) (v interface{}, err error) { +// defineType enum value define the type (object and array unsupported) +func defineType(schemaType string, value string) (interface{}, error) { schemaType = TransToValidSchemeType(schemaType) - switch schemaType { - case STRING: + case "string": return value, nil - case NUMBER: - v, err = strconv.ParseFloat(value, 64) + case "number": + v, err := strconv.ParseFloat(value, 64) if err != nil { return nil, fmt.Errorf("enum value %s can't convert to %s err: %s", value, schemaType, err) } - case INTEGER: - v, err = strconv.Atoi(value) + return v, nil + case "integer": + v, err := strconv.Atoi(value) if err != nil { return nil, fmt.Errorf("enum value %s can't convert to %s err: %s", value, schemaType, err) } - case BOOLEAN: - v, err = strconv.ParseBool(value) + return v, nil + case "boolean": + v, err := strconv.ParseBool(value) if err != nil { return nil, fmt.Errorf("enum value %s can't convert to %s err: %s", value, schemaType, err) } + return v, nil default: - return nil, fmt.Errorf("%s is unsupported type in enum value %s", schemaType, value) + return nil, fmt.Errorf("%s is unsupported type in enum value", schemaType) } - - return v, nil } // ParseTagsComment parses comment for given `tag` comment string. func (operation *Operation) ParseTagsComment(commentLine string) { - for _, tag := range strings.Split(commentLine, ",") { + tags := strings.Split(commentLine, ",") + for _, tag := range tags { operation.Tags = append(operation.Tags, strings.TrimSpace(tag)) } } @@ -683,92 +507,70 @@ func (operation *Operation) ParseProduceComment(commentLine string) error { // parseMimeTypeList parses a list of MIME Types for a comment like // `produce` (`Content-Type:` response header) or -// `accept` (`Accept:` request header). +// `accept` (`Accept:` request header) func parseMimeTypeList(mimeTypeList string, typeList *[]string, format string) error { - for _, typeName := range strings.Split(mimeTypeList, ",") { + mimeTypes := strings.Split(mimeTypeList, ",") + for _, typeName := range mimeTypes { if mimeTypePattern.MatchString(typeName) { *typeList = append(*typeList, typeName) - continue } - - aliasMimeType, ok := mimeTypeAliases[typeName] - if !ok { - return fmt.Errorf(format, typeName) + if aliasMimeType, ok := mimeTypeAliases[typeName]; ok { + *typeList = append(*typeList, aliasMimeType) + continue } - - *typeList = append(*typeList, aliasMimeType) + return fmt.Errorf(format, typeName) } - return nil } -var routerPattern = regexp.MustCompile(`^(/[\w./\-{}+:$]*)[[:blank:]]+\[(\w+)]`) - -// ParseRouterComment parses comment for given `router` comment string. -func (operation *Operation) ParseRouterComment(commentLine string, deprecated bool) error { - matches := routerPattern.FindStringSubmatch(commentLine) - if len(matches) != 3 { - return fmt.Errorf("can not parse router comment \"%s\"", commentLine) - } +var routerPattern = regexp.MustCompile(`^(/[\w\.\/\-{}\+:]*)[[:blank:]]+\[(\w+)]`) - signature := RouteProperties{ - Path: matches[1], - HTTPMethod: strings.ToUpper(matches[2]), - Deprecated: deprecated, - } +// ParseRouterComment parses comment for gived `router` comment string. +func (operation *Operation) ParseRouterComment(commentLine string) error { + var matches []string - if _, ok := allMethod[signature.HTTPMethod]; !ok { - return fmt.Errorf("invalid method: %s", signature.HTTPMethod) + if matches = routerPattern.FindStringSubmatch(commentLine); len(matches) != 3 { + return fmt.Errorf("can not parse router comment \"%s\"", commentLine) } + path := matches[1] + httpMethod := matches[2] - operation.RouterProperties = append(operation.RouterProperties, signature) + operation.Path = path + operation.HTTPMethod = strings.ToUpper(httpMethod) return nil } -// ParseSecurityComment parses comment for given `security` comment string. +// ParseSecurityComment parses comment for gived `security` comment string. func (operation *Operation) ParseSecurityComment(commentLine string) error { - if len(commentLine) == 0 { - operation.Security = []map[string][]string{} - return nil + securitySource := commentLine[strings.Index(commentLine, "@Security")+1:] + l := strings.Index(securitySource, "[") + r := strings.Index(securitySource, "]") + // exists scope + if !(l == -1 && r == -1) { + scopes := securitySource[l+1 : r] + s := []string{} + for _, scope := range strings.Split(scopes, ",") { + scope = strings.TrimSpace(scope) + s = append(s, scope) + } + securityKey := securitySource[0:l] + securityMap := map[string][]string{} + securityMap[securityKey] = append(securityMap[securityKey], s...) + operation.Security = append(operation.Security, securityMap) + } else { + securityKey := strings.TrimSpace(securitySource) + securityMap := map[string][]string{} + securityMap[securityKey] = []string{} + operation.Security = append(operation.Security, securityMap) } - - var ( - securityMap = make(map[string][]string) - securitySource = commentLine[strings.Index(commentLine, "@Security")+1:] - ) - - for _, securityOption := range strings.Split(securitySource, "||") { - securityOption = strings.TrimSpace(securityOption) - - left, right := strings.Index(securityOption, "["), strings.Index(securityOption, "]") - - if !(left == -1 && right == -1) { - scopes := securityOption[left+1 : right] - - var options []string - - for _, scope := range strings.Split(scopes, ",") { - options = append(options, strings.TrimSpace(scope)) - } - - securityKey := securityOption[0:left] - securityMap[securityKey] = append(securityMap[securityKey], options...) - } else { - securityKey := strings.TrimSpace(securityOption) - securityMap[securityKey] = []string{} - } - } - - operation.Security = append(operation.Security, securityMap) - return nil } // findTypeDef attempts to find the *ast.TypeSpec for a specific type given the -// type's name and the package's import path. -// TODO: improve finding external pkg. +// type's name and the package's import path +// TODO: improve finding external pkg func findTypeDef(importPath, typeName string) (*ast.TypeSpec, error) { cwd, err := os.Getwd() if err != nil { @@ -804,13 +606,12 @@ func findTypeDef(importPath, typeName string) (*ast.TypeSpec, error) { } // TODO: possibly cache pkgInfo since it's an expensive operation + for i := range pkgInfo.Files { for _, astDeclaration := range pkgInfo.Files[i].Decls { - generalDeclaration, ok := astDeclaration.(*ast.GenDecl) - if ok && generalDeclaration.Tok == token.TYPE { + if generalDeclaration, ok := astDeclaration.(*ast.GenDecl); ok && generalDeclaration.Tok == token.TYPE { for _, astSpec := range generalDeclaration.Specs { - typeSpec, ok := astSpec.(*ast.TypeSpec) - if ok { + if typeSpec, ok := astSpec.(*ast.TypeSpec); ok { if typeSpec.Name.String() == typeName { return typeSpec, nil } @@ -819,427 +620,320 @@ func findTypeDef(importPath, typeName string) (*ast.TypeSpec, error) { } } } - return nil, fmt.Errorf("type spec not found") } -var responsePattern = regexp.MustCompile(`^([\w,]+)\s+([\w{}]+)\s+([\w\-.\\{}=,\[\s\]]+)\s*(".*)?`) +var responsePattern = regexp.MustCompile(`([\d]+)[\s]+([\w\{\}]+)[\s]+([\w\-\.\/\{\}=,\[\]]+)[^"]*(.*)?`) -// ResponseType{data1=Type1,data2=Type2}. -var combinedPattern = regexp.MustCompile(`^([\w\-./\[\]]+){(.*)}$`) +//RepsonseType{data1=Type1,data2=Type2} +var combinedPattern = regexp.MustCompile(`^([\w\-\.\/\[\]]+)\{(.*)\}$`) func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) { - return parseObjectSchema(operation.parser, refType, astFile) -} - -func parseObjectSchema(parser *Parser, refType string, astFile *ast.File) (*spec.Schema, error) { switch { - case refType == NIL: - return nil, nil - case refType == INTERFACE: - return PrimitiveSchema(OBJECT), nil - case refType == ANY: - return PrimitiveSchema(OBJECT), nil + case refType == "interface{}": + return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{"object"}}}, nil case IsGolangPrimitiveType(refType): refType = TransToValidSchemeType(refType) - - return PrimitiveSchema(refType), nil + return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{refType}}}, nil case IsPrimitiveType(refType): - return PrimitiveSchema(refType), nil + return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{refType}}}, nil case strings.HasPrefix(refType, "[]"): - schema, err := parseObjectSchema(parser, refType[2:], astFile) + schema, err := operation.parseObjectSchema(refType[2:], astFile) if err != nil { return nil, err } - - return spec.ArrayProperty(schema), nil + return &spec.Schema{SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{Schema: schema}}, + }, nil case strings.HasPrefix(refType, "map["): - // ignore key type + //ignore key type idx := strings.Index(refType, "]") if idx < 0 { return nil, fmt.Errorf("invalid type: %s", refType) } - refType = refType[idx+1:] - if refType == INTERFACE || refType == ANY { - return spec.MapProperty(nil), nil - } - - schema, err := parseObjectSchema(parser, refType, astFile) - if err != nil { - return nil, err + var valueSchema spec.SchemaOrBool + if refType == "interface{}" { + valueSchema.Allows = true + } else { + schema, err := operation.parseObjectSchema(refType, astFile) + if err != nil { + return &spec.Schema{}, err + } + valueSchema.Schema = schema } - - return spec.MapProperty(schema), nil + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &valueSchema, + }, + }, nil case strings.Contains(refType, "{"): - return parseCombinedObjectSchema(parser, refType, astFile) + return operation.parseResponseCombinedObjectSchema(refType, astFile) default: - if parser != nil { // checking refType has existing in 'TypeDefinitions' - schema, err := parser.getTypeSchema(refType, astFile, true) + if operation.parser != nil { // checking refType has existing in 'TypeDefinitions' + refNewType, typeSpec, err := operation.registerSchemaType(refType, astFile) if err != nil { return nil, err } - - return schema, nil + refType = TypeDocName(refNewType, typeSpec) } - - return RefSchema(refType), nil + return &spec.Schema{SchemaProps: spec.SchemaProps{Ref: spec.Ref{ + Ref: jsonreference.MustCreateRef("#/definitions/" + refType), + }}}, nil } } -func parseFields(s string) []string { - nestLevel := 0 - - return strings.FieldsFunc(s, func(char rune) bool { - if char == '{' { - nestLevel++ - - return false - } else if char == '}' { - nestLevel-- - - return false - } - - return char == ',' && nestLevel == 0 - }) -} - -func parseCombinedObjectSchema(parser *Parser, refType string, astFile *ast.File) (*spec.Schema, error) { +func (operation *Operation) parseResponseCombinedObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) { matches := combinedPattern.FindStringSubmatch(refType) if len(matches) != 3 { return nil, fmt.Errorf("invalid type: %s", refType) } - - schema, err := parseObjectSchema(parser, matches[1], astFile) + refType = matches[1] + schema, err := operation.parseObjectSchema(refType, astFile) if err != nil { return nil, err } - fields, props := parseFields(matches[2]), map[string]spec.Schema{} - - for _, field := range fields { - keyVal := strings.SplitN(field, "=", 2) - if len(keyVal) == 2 { - schema, err := parseObjectSchema(parser, keyVal[1], astFile) - if err != nil { - return nil, err + parseFields := func(s string) []string { + n := 0 + return strings.FieldsFunc(s, func(r rune) bool { + if r == '{' { + n++ + return false + } else if r == '}' { + n-- + return false } + return r == ',' && n == 0 + }) + } - if schema == nil { - schema = PrimitiveSchema(OBJECT) + fields := parseFields(matches[2]) + props := map[string]spec.Schema{} + for _, field := range fields { + if matches := strings.SplitN(field, "=", 2); len(matches) == 2 { + if strings.HasPrefix(matches[1], "[]") { + itemSchema, err := operation.parseObjectSchema(matches[1][2:], astFile) + if err != nil { + return nil, err + } + props[matches[0]] = spec.Schema{SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{Schema: itemSchema}}, + } + } else { + schema, err := operation.parseObjectSchema(matches[1], astFile) + if err != nil { + return nil, err + } + props[matches[0]] = *schema } - - props[keyVal[0]] = *schema } } if len(props) == 0 { return schema, nil } - - if schema.Ref.GetURL() == nil && len(schema.Type) > 0 && schema.Type[0] == OBJECT && len(schema.Properties) == 0 && schema.AdditionalProperties == nil { - schema.Properties = props - return schema, nil - } - - return spec.ComposedSchema(*schema, spec.Schema{ + return &spec.Schema{ SchemaProps: spec.SchemaProps{ - Type: []string{OBJECT}, - Properties: props, + AllOf: []spec.Schema{ + *schema, + { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: props, + }, + }, + }, }, - }), nil + }, nil } -func (operation *Operation) parseAPIObjectSchema(commentLine, schemaType, refType string, astFile *ast.File) (*spec.Schema, error) { - if strings.HasSuffix(refType, ",") && strings.Contains(refType, "[") { - // regexp may have broken generic syntax. find closing bracket and add it back - allMatchesLenOffset := strings.Index(commentLine, refType) + len(refType) - lostPartEndIdx := strings.Index(commentLine[allMatchesLenOffset:], "]") - if lostPartEndIdx >= 0 { - refType += commentLine[allMatchesLenOffset : allMatchesLenOffset+lostPartEndIdx+1] - } - } - +func (operation *Operation) parseResponseSchema(schemaType, refType string, astFile *ast.File) (*spec.Schema, error) { switch schemaType { - case OBJECT: + case "object": if !strings.HasPrefix(refType, "[]") { return operation.parseObjectSchema(refType, astFile) } - refType = refType[2:] - fallthrough - case ARRAY: + case "array": schema, err := operation.parseObjectSchema(refType, astFile) if err != nil { return nil, err } - - return spec.ArrayProperty(schema), nil + return &spec.Schema{SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{Schema: schema}}, + }, nil default: - return PrimitiveSchema(schemaType), nil + return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{schemaType}}}, nil } } // ParseResponseComment parses comment for given `response` comment string. func (operation *Operation) ParseResponseComment(commentLine string, astFile *ast.File) error { - matches := responsePattern.FindStringSubmatch(commentLine) - if len(matches) != 5 { + var matches []string + + if matches = responsePattern.FindStringSubmatch(commentLine); len(matches) != 5 { err := operation.ParseEmptyResponseComment(commentLine) if err != nil { return operation.ParseEmptyResponseOnly(commentLine) } - return err } - description := strings.Trim(matches[4], "\"") + code, _ := strconv.Atoi(matches[1]) - schema, err := operation.parseAPIObjectSchema(commentLine, strings.Trim(matches[2], "{}"), strings.TrimSpace(matches[3]), astFile) + responseDescription := strings.Trim(matches[4], "\"") + if responseDescription == "" { + responseDescription = http.StatusText(code) + } + + schemaType := strings.Trim(matches[2], "{}") + refType := matches[3] + schema, err := operation.parseResponseSchema(schemaType, refType, astFile) if err != nil { return err } - for _, codeStr := range strings.Split(matches[1], ",") { - if strings.EqualFold(codeStr, defaultTag) { - operation.DefaultResponse().WithSchema(schema).WithDescription(description) - - continue - } - - code, err := strconv.Atoi(codeStr) - if err != nil { - return fmt.Errorf("can not parse response comment \"%s\"", commentLine) - } - - resp := spec.NewResponse().WithSchema(schema).WithDescription(description) - if description == "" { - resp.WithDescription(http.StatusText(code)) + if operation.Responses == nil { + operation.Responses = &spec.Responses{ + ResponsesProps: spec.ResponsesProps{ + StatusCodeResponses: make(map[int]spec.Response), + }, } - - operation.AddResponse(code, resp) } + operation.Responses.StatusCodeResponses[code] = spec.Response{ + ResponseProps: spec.ResponseProps{Schema: schema, Description: responseDescription}, + } return nil } -func newHeaderSpec(schemaType, description string) spec.Header { - return spec.Header{ - SimpleSchema: spec.SimpleSchema{ - Type: schemaType, - }, - HeaderProps: spec.HeaderProps{ - Description: description, - }, - VendorExtensible: spec.VendorExtensible{ - Extensions: nil, - }, - CommonValidations: spec.CommonValidations{ - Maximum: nil, - ExclusiveMaximum: false, - Minimum: nil, - ExclusiveMinimum: false, - MaxLength: nil, - MinLength: nil, - Pattern: "", - MaxItems: nil, - MinItems: nil, - UniqueItems: false, - MultipleOf: nil, - Enum: nil, - }, - } -} +// ParseResponseHeaderComment parses comment for gived `response header` comment string. +func (operation *Operation) ParseResponseHeaderComment(commentLine string, astFile *ast.File) error { + var matches []string -// ParseResponseHeaderComment parses comment for given `response header` comment string. -func (operation *Operation) ParseResponseHeaderComment(commentLine string, _ *ast.File) error { - matches := responsePattern.FindStringSubmatch(commentLine) - if len(matches) != 5 { + if matches = responsePattern.FindStringSubmatch(commentLine); len(matches) != 5 { return fmt.Errorf("can not parse response comment \"%s\"", commentLine) } - header := newHeaderSpec(strings.Trim(matches[2], "{}"), strings.Trim(matches[4], "\"")) - - headerKey := strings.TrimSpace(matches[3]) + response := spec.Response{} - if strings.EqualFold(matches[1], "all") { - if operation.Responses.Default != nil { - operation.Responses.Default.Headers[headerKey] = header - } - - if operation.Responses.StatusCodeResponses != nil { - for code, response := range operation.Responses.StatusCodeResponses { - response.Headers[headerKey] = header - operation.Responses.StatusCodeResponses[code] = response - } - } + code, _ := strconv.Atoi(matches[1]) - return nil + responseDescription := strings.Trim(matches[4], "\"") + if responseDescription == "" { + responseDescription = http.StatusText(code) } + response.Description = responseDescription - for _, codeStr := range strings.Split(matches[1], ",") { - if strings.EqualFold(codeStr, defaultTag) { - if operation.Responses.Default != nil { - operation.Responses.Default.Headers[headerKey] = header - } - - continue - } + schemaType := strings.Trim(matches[2], "{}") + refType := matches[3] - code, err := strconv.Atoi(codeStr) - if err != nil { - return fmt.Errorf("can not parse response comment \"%s\"", commentLine) + if operation.Responses == nil { + operation.Responses = &spec.Responses{ + ResponsesProps: spec.ResponsesProps{ + StatusCodeResponses: make(map[int]spec.Response), + }, } + } - if operation.Responses.StatusCodeResponses != nil { - response, responseExist := operation.Responses.StatusCodeResponses[code] - if responseExist { - response.Headers[headerKey] = header + response, responseExist := operation.Responses.StatusCodeResponses[code] + if responseExist { + header := spec.Header{} + header.Description = responseDescription + header.Type = schemaType - operation.Responses.StatusCodeResponses[code] = response - } + if response.Headers == nil { + response.Headers = make(map[string]spec.Header) } + response.Headers[refType] = header + + operation.Responses.StatusCodeResponses[code] = response } return nil } -var emptyResponsePattern = regexp.MustCompile(`([\w,]+)\s+"(.*)"`) +var emptyResponsePattern = regexp.MustCompile(`([\d]+)[\s]+"(.*)"`) -// ParseEmptyResponseComment parse only comment out status code and description,eg: @Success 200 "it's ok". +// ParseEmptyResponseComment parse only comment out status code and description,eg: @Success 200 "it's ok" func (operation *Operation) ParseEmptyResponseComment(commentLine string) error { - matches := emptyResponsePattern.FindStringSubmatch(commentLine) - if len(matches) != 3 { + var matches []string + + if matches = emptyResponsePattern.FindStringSubmatch(commentLine); len(matches) != 3 { return fmt.Errorf("can not parse response comment \"%s\"", commentLine) } - description := strings.Trim(matches[2], "\"") + response := spec.Response{} - for _, codeStr := range strings.Split(matches[1], ",") { - if strings.EqualFold(codeStr, defaultTag) { - operation.DefaultResponse().WithDescription(description) + code, _ := strconv.Atoi(matches[1]) - continue - } + response.Description = strings.Trim(matches[2], "") - code, err := strconv.Atoi(codeStr) - if err != nil { - return fmt.Errorf("can not parse response comment \"%s\"", commentLine) + if operation.Responses == nil { + operation.Responses = &spec.Responses{ + ResponsesProps: spec.ResponsesProps{ + StatusCodeResponses: make(map[int]spec.Response), + }, } - - operation.AddResponse(code, spec.NewResponse().WithDescription(description)) } + operation.Responses.StatusCodeResponses[code] = response + return nil } -// ParseEmptyResponseOnly parse only comment out status code ,eg: @Success 200. +//ParseEmptyResponseOnly parse only comment out status code ,eg: @Success 200 func (operation *Operation) ParseEmptyResponseOnly(commentLine string) error { - for _, codeStr := range strings.Split(commentLine, ",") { - if strings.EqualFold(codeStr, defaultTag) { - _ = operation.DefaultResponse() - - continue - } - - code, err := strconv.Atoi(codeStr) - if err != nil { - return fmt.Errorf("can not parse response comment \"%s\"", commentLine) - } + response := spec.Response{} - operation.AddResponse(code, spec.NewResponse().WithDescription(http.StatusText(code))) + code, err := strconv.Atoi(commentLine) + if err != nil { + return fmt.Errorf("can not parse response comment \"%s\"", commentLine) } - - return nil -} - -// DefaultResponse return the default response member pointer. -func (operation *Operation) DefaultResponse() *spec.Response { - if operation.Responses.Default == nil { - operation.Responses.Default = &spec.Response{ - ResponseProps: spec.ResponseProps{ - Description: "", - Headers: make(map[string]spec.Header), + if operation.Responses == nil { + operation.Responses = &spec.Responses{ + ResponsesProps: spec.ResponsesProps{ + StatusCodeResponses: make(map[int]spec.Response), }, } } - return operation.Responses.Default -} - -// AddResponse add a response for a code. -func (operation *Operation) AddResponse(code int, response *spec.Response) { - if response.Headers == nil { - response.Headers = make(map[string]spec.Header) - } + operation.Responses.StatusCodeResponses[code] = response - operation.Responses.StatusCodeResponses[code] = *response + return nil } -// createParameter returns swagger spec.Parameter for given paramType, description, paramName, schemaType, required. -func createParameter(paramType, description, paramName, objectType, schemaType string, required bool, enums []interface{}, collectionFormat string) spec.Parameter { +// createParameter returns swagger spec.Parameter for gived paramType, description, paramName, schemaType, required +func createParameter(paramType, description, paramName, schemaType string, required bool) spec.Parameter { // //five possible parameter types. query, path, body, header, form - result := spec.Parameter{ - ParamProps: spec.ParamProps{ - Name: paramName, - Description: description, - Required: required, - In: paramType, - }, + paramProps := spec.ParamProps{ + Name: paramName, + Description: description, + Required: required, + In: paramType, } - if paramType == "body" { - return result - } - - switch objectType { - case ARRAY: - result.Type = objectType - result.CollectionFormat = collectionFormat - result.Items = &spec.Items{ - CommonValidations: spec.CommonValidations{ - Enum: enums, - }, - SimpleSchema: spec.SimpleSchema{ - Type: schemaType, + paramProps.Schema = &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{schemaType}, }, } - case PRIMITIVE, OBJECT: - result.Type = schemaType - result.Enum = enums - } - return result -} - -func getCodeExampleForSummary(summaryName string, dirPath string) ([]byte, error) { - dirEntries, err := os.ReadDir(dirPath) - if err != nil { - return nil, err - } - - for _, entry := range dirEntries { - if entry.IsDir() { - continue - } - - fileName := entry.Name() - - if !strings.Contains(fileName, ".json") { - continue - } - - if strings.Contains(fileName, summaryName) { - fullPath := filepath.Join(dirPath, fileName) - - commentInfo, err := os.ReadFile(fullPath) - if err != nil { - return nil, fmt.Errorf("Failed to read code example file %s error: %s ", fullPath, err) - } - - return commentInfo, nil + parameter := spec.Parameter{ + ParamProps: paramProps, } + return parameter } - - return nil, fmt.Errorf("unable to find code example file for tag %s in the given directory", summaryName) + parameter := spec.Parameter{ + ParamProps: paramProps, + SimpleSchema: spec.SimpleSchema{ + Type: schemaType, + }, + } + return parameter } diff --git a/vendor/github.com/swaggo/swag/package.go b/vendor/github.com/swaggo/swag/package.go deleted file mode 100644 index 6c3129e52e..0000000000 --- a/vendor/github.com/swaggo/swag/package.go +++ /dev/null @@ -1,187 +0,0 @@ -package swag - -import ( - "go/ast" - "go/token" - "reflect" - "strconv" - "strings" -) - -// PackageDefinitions files and definition in a package. -type PackageDefinitions struct { - // files in this package, map key is file's relative path starting package path - Files map[string]*ast.File - - // definitions in this package, map key is typeName - TypeDefinitions map[string]*TypeSpecDef - - // const variables in this package, map key is the name - ConstTable map[string]*ConstVariable - - // const variables in order in this package - OrderedConst []*ConstVariable - - // package name - Name string - - // package path - Path string -} - -// ConstVariableGlobalEvaluator an interface used to evaluate enums across packages -type ConstVariableGlobalEvaluator interface { - EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) - EvaluateConstValueByName(file *ast.File, pkgPath, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr) - FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef -} - -// NewPackageDefinitions new a PackageDefinitions object -func NewPackageDefinitions(name, pkgPath string) *PackageDefinitions { - return &PackageDefinitions{ - Name: name, - Path: pkgPath, - Files: make(map[string]*ast.File), - TypeDefinitions: make(map[string]*TypeSpecDef), - ConstTable: make(map[string]*ConstVariable), - } -} - -// AddFile add a file -func (pkg *PackageDefinitions) AddFile(pkgPath string, file *ast.File) *PackageDefinitions { - pkg.Files[pkgPath] = file - return pkg -} - -// AddTypeSpec add a type spec. -func (pkg *PackageDefinitions) AddTypeSpec(name string, typeSpec *TypeSpecDef) *PackageDefinitions { - pkg.TypeDefinitions[name] = typeSpec - return pkg -} - -// AddConst add a const variable. -func (pkg *PackageDefinitions) AddConst(astFile *ast.File, valueSpec *ast.ValueSpec) *PackageDefinitions { - for i := 0; i < len(valueSpec.Names) && i < len(valueSpec.Values); i++ { - variable := &ConstVariable{ - Name: valueSpec.Names[i], - Type: valueSpec.Type, - Value: valueSpec.Values[i], - Comment: valueSpec.Comment, - File: astFile, - } - pkg.ConstTable[valueSpec.Names[i].Name] = variable - pkg.OrderedConst = append(pkg.OrderedConst, variable) - } - return pkg -} - -func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr ast.Expr, globalEvaluator ConstVariableGlobalEvaluator, recursiveStack map[string]struct{}) (interface{}, ast.Expr) { - switch valueExpr := expr.(type) { - case *ast.Ident: - if valueExpr.Name == "iota" { - return iota, nil - } - if pkg.ConstTable != nil { - if cv, ok := pkg.ConstTable[valueExpr.Name]; ok { - return globalEvaluator.EvaluateConstValue(pkg, cv, recursiveStack) - } - } - case *ast.SelectorExpr: - pkgIdent, ok := valueExpr.X.(*ast.Ident) - if !ok { - return nil, nil - } - return globalEvaluator.EvaluateConstValueByName(file, pkgIdent.Name, valueExpr.Sel.Name, recursiveStack) - case *ast.BasicLit: - switch valueExpr.Kind { - case token.INT: - // handle underscored number, such as 1_000_000 - if strings.ContainsRune(valueExpr.Value, '_') { - valueExpr.Value = strings.Replace(valueExpr.Value, "_", "", -1) - } - if len(valueExpr.Value) >= 2 && valueExpr.Value[0] == '0' { - var start, base = 2, 8 - switch valueExpr.Value[1] { - case 'x', 'X': - //hex - base = 16 - case 'b', 'B': - //binary - base = 2 - default: - //octet - start = 1 - } - if x, err := strconv.ParseInt(valueExpr.Value[start:], base, 64); err == nil { - return int(x), nil - } else if x, err := strconv.ParseUint(valueExpr.Value[start:], base, 64); err == nil { - return x, nil - } else { - panic(err) - } - } - - //a basic literal integer is int type in default, or must have an explicit converting type in front - if x, err := strconv.ParseInt(valueExpr.Value, 10, 64); err == nil { - return int(x), nil - } else if x, err := strconv.ParseUint(valueExpr.Value, 10, 64); err == nil { - return x, nil - } else { - panic(err) - } - case token.STRING: - if valueExpr.Value[0] == '`' { - return valueExpr.Value[1 : len(valueExpr.Value)-1], nil - } - return EvaluateEscapedString(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil - case token.CHAR: - return EvaluateEscapedChar(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil - } - case *ast.UnaryExpr: - x, evalType := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) - if x == nil { - return x, evalType - } - return EvaluateUnary(x, valueExpr.Op, evalType) - case *ast.BinaryExpr: - x, evalTypex := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) - y, evalTypey := pkg.evaluateConstValue(file, iota, valueExpr.Y, globalEvaluator, recursiveStack) - if x == nil || y == nil { - return nil, nil - } - return EvaluateBinary(x, y, valueExpr.Op, evalTypex, evalTypey) - case *ast.ParenExpr: - return pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) - case *ast.CallExpr: - //data conversion - if len(valueExpr.Args) != 1 { - return nil, nil - } - arg := valueExpr.Args[0] - if ident, ok := valueExpr.Fun.(*ast.Ident); ok { - name := ident.Name - if name == "uintptr" { - name = "uint" - } - value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack) - if IsGolangPrimitiveType(name) { - value = EvaluateDataConversion(value, name) - return value, nil - } else if name == "len" { - return reflect.ValueOf(value).Len(), nil - } - typeDef := globalEvaluator.FindTypeSpec(name, file) - if typeDef == nil { - return nil, nil - } - return value, valueExpr.Fun - } else if selector, ok := valueExpr.Fun.(*ast.SelectorExpr); ok { - typeDef := globalEvaluator.FindTypeSpec(fullTypeName(selector.X.(*ast.Ident).Name, selector.Sel.Name), file) - if typeDef == nil { - return nil, nil - } - return arg, typeDef.TypeSpec.Type - } - } - return nil, nil -} diff --git a/vendor/github.com/swaggo/swag/packages.go b/vendor/github.com/swaggo/swag/packages.go deleted file mode 100644 index 69a1b05298..0000000000 --- a/vendor/github.com/swaggo/swag/packages.go +++ /dev/null @@ -1,574 +0,0 @@ -package swag - -import ( - "fmt" - "go/ast" - goparser "go/parser" - "go/token" - "os" - "path/filepath" - "runtime" - "sort" - "strings" - - "golang.org/x/tools/go/loader" -) - -// PackagesDefinitions map[package import path]*PackageDefinitions. -type PackagesDefinitions struct { - files map[*ast.File]*AstFileInfo - packages map[string]*PackageDefinitions - uniqueDefinitions map[string]*TypeSpecDef - parseDependency ParseFlag - debug Debugger -} - -// NewPackagesDefinitions create object PackagesDefinitions. -func NewPackagesDefinitions() *PackagesDefinitions { - return &PackagesDefinitions{ - files: make(map[*ast.File]*AstFileInfo), - packages: make(map[string]*PackageDefinitions), - uniqueDefinitions: make(map[string]*TypeSpecDef), - } -} - -// ParseFile parse a source file. -func (pkgDefs *PackagesDefinitions) ParseFile(packageDir, path string, src interface{}, flag ParseFlag) error { - // positions are relative to FileSet - fileSet := token.NewFileSet() - astFile, err := goparser.ParseFile(fileSet, path, src, goparser.ParseComments) - if err != nil { - return fmt.Errorf("failed to parse file %s, error:%+v", path, err) - } - return pkgDefs.collectAstFile(fileSet, packageDir, path, astFile, flag) -} - -// collectAstFile collect ast.file. -func (pkgDefs *PackagesDefinitions) collectAstFile(fileSet *token.FileSet, packageDir, path string, astFile *ast.File, flag ParseFlag) error { - if pkgDefs.files == nil { - pkgDefs.files = make(map[*ast.File]*AstFileInfo) - } - - if pkgDefs.packages == nil { - pkgDefs.packages = make(map[string]*PackageDefinitions) - } - - // return without storing the file if we lack a packageDir - if packageDir == "" { - return nil - } - - path, err := filepath.Abs(path) - if err != nil { - return err - } - - dependency, ok := pkgDefs.packages[packageDir] - if ok { - // return without storing the file if it already exists - _, exists := dependency.Files[path] - if exists { - return nil - } - - dependency.Files[path] = astFile - } else { - pkgDefs.packages[packageDir] = NewPackageDefinitions(astFile.Name.Name, packageDir).AddFile(path, astFile) - } - - pkgDefs.files[astFile] = &AstFileInfo{ - FileSet: fileSet, - File: astFile, - Path: path, - PackagePath: packageDir, - ParseFlag: flag, - } - - return nil -} - -// RangeFiles for range the collection of ast.File in alphabetic order. -func (pkgDefs *PackagesDefinitions) RangeFiles(handle func(info *AstFileInfo) error) error { - sortedFiles := make([]*AstFileInfo, 0, len(pkgDefs.files)) - for _, info := range pkgDefs.files { - // ignore package path prefix with 'vendor' or $GOROOT, - // because the router info of api will not be included these files. - if strings.HasPrefix(info.PackagePath, "vendor") || strings.HasPrefix(info.Path, runtime.GOROOT()) { - continue - } - sortedFiles = append(sortedFiles, info) - } - - sort.Slice(sortedFiles, func(i, j int) bool { - return strings.Compare(sortedFiles[i].Path, sortedFiles[j].Path) < 0 - }) - - for _, info := range sortedFiles { - err := handle(info) - if err != nil { - return err - } - } - - return nil -} - -// ParseTypes parse types -// @Return parsed definitions. -func (pkgDefs *PackagesDefinitions) ParseTypes() (map[*TypeSpecDef]*Schema, error) { - parsedSchemas := make(map[*TypeSpecDef]*Schema) - for astFile, info := range pkgDefs.files { - pkgDefs.parseTypesFromFile(astFile, info.PackagePath, parsedSchemas) - pkgDefs.parseFunctionScopedTypesFromFile(astFile, info.PackagePath, parsedSchemas) - } - pkgDefs.removeAllNotUniqueTypes() - pkgDefs.evaluateAllConstVariables() - pkgDefs.collectConstEnums(parsedSchemas) - return parsedSchemas, nil -} - -func (pkgDefs *PackagesDefinitions) parseTypesFromFile(astFile *ast.File, packagePath string, parsedSchemas map[*TypeSpecDef]*Schema) { - for _, astDeclaration := range astFile.Decls { - generalDeclaration, ok := astDeclaration.(*ast.GenDecl) - if !ok { - continue - } - if generalDeclaration.Tok == token.TYPE { - for _, astSpec := range generalDeclaration.Specs { - if typeSpec, ok := astSpec.(*ast.TypeSpec); ok { - typeSpecDef := &TypeSpecDef{ - PkgPath: packagePath, - File: astFile, - TypeSpec: typeSpec, - } - - if idt, ok := typeSpec.Type.(*ast.Ident); ok && IsGolangPrimitiveType(idt.Name) && parsedSchemas != nil { - parsedSchemas[typeSpecDef] = &Schema{ - PkgPath: typeSpecDef.PkgPath, - Name: astFile.Name.Name, - Schema: PrimitiveSchema(TransToValidSchemeType(idt.Name)), - } - } - - if pkgDefs.uniqueDefinitions == nil { - pkgDefs.uniqueDefinitions = make(map[string]*TypeSpecDef) - } - - fullName := typeSpecDef.TypeName() - - anotherTypeDef, ok := pkgDefs.uniqueDefinitions[fullName] - if ok { - if anotherTypeDef == nil { - typeSpecDef.NotUnique = true - fullName = typeSpecDef.TypeName() - pkgDefs.uniqueDefinitions[fullName] = typeSpecDef - } else if typeSpecDef.PkgPath != anotherTypeDef.PkgPath { - pkgDefs.uniqueDefinitions[fullName] = nil - anotherTypeDef.NotUnique = true - pkgDefs.uniqueDefinitions[anotherTypeDef.TypeName()] = anotherTypeDef - typeSpecDef.NotUnique = true - fullName = typeSpecDef.TypeName() - pkgDefs.uniqueDefinitions[fullName] = typeSpecDef - } - } else { - pkgDefs.uniqueDefinitions[fullName] = typeSpecDef - } - - if pkgDefs.packages[typeSpecDef.PkgPath] == nil { - pkgDefs.packages[typeSpecDef.PkgPath] = NewPackageDefinitions(astFile.Name.Name, typeSpecDef.PkgPath).AddTypeSpec(typeSpecDef.Name(), typeSpecDef) - } else if _, ok = pkgDefs.packages[typeSpecDef.PkgPath].TypeDefinitions[typeSpecDef.Name()]; !ok { - pkgDefs.packages[typeSpecDef.PkgPath].AddTypeSpec(typeSpecDef.Name(), typeSpecDef) - } - } - } - } else if generalDeclaration.Tok == token.CONST { - // collect consts - pkgDefs.collectConstVariables(astFile, packagePath, generalDeclaration) - } - } -} - -func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *ast.File, packagePath string, parsedSchemas map[*TypeSpecDef]*Schema) { - for _, astDeclaration := range astFile.Decls { - funcDeclaration, ok := astDeclaration.(*ast.FuncDecl) - if ok && funcDeclaration.Body != nil { - for _, stmt := range funcDeclaration.Body.List { - if declStmt, ok := (stmt).(*ast.DeclStmt); ok { - if genDecl, ok := (declStmt.Decl).(*ast.GenDecl); ok && genDecl.Tok == token.TYPE { - for _, astSpec := range genDecl.Specs { - if typeSpec, ok := astSpec.(*ast.TypeSpec); ok { - typeSpecDef := &TypeSpecDef{ - PkgPath: packagePath, - File: astFile, - TypeSpec: typeSpec, - ParentSpec: astDeclaration, - } - - if idt, ok := typeSpec.Type.(*ast.Ident); ok && IsGolangPrimitiveType(idt.Name) && parsedSchemas != nil { - parsedSchemas[typeSpecDef] = &Schema{ - PkgPath: typeSpecDef.PkgPath, - Name: astFile.Name.Name, - Schema: PrimitiveSchema(TransToValidSchemeType(idt.Name)), - } - } - - if pkgDefs.uniqueDefinitions == nil { - pkgDefs.uniqueDefinitions = make(map[string]*TypeSpecDef) - } - - fullName := typeSpecDef.TypeName() - - anotherTypeDef, ok := pkgDefs.uniqueDefinitions[fullName] - if ok { - if anotherTypeDef == nil { - typeSpecDef.NotUnique = true - fullName = typeSpecDef.TypeName() - pkgDefs.uniqueDefinitions[fullName] = typeSpecDef - } else if typeSpecDef.PkgPath != anotherTypeDef.PkgPath { - pkgDefs.uniqueDefinitions[fullName] = nil - anotherTypeDef.NotUnique = true - pkgDefs.uniqueDefinitions[anotherTypeDef.TypeName()] = anotherTypeDef - typeSpecDef.NotUnique = true - fullName = typeSpecDef.TypeName() - pkgDefs.uniqueDefinitions[fullName] = typeSpecDef - } - } else { - pkgDefs.uniqueDefinitions[fullName] = typeSpecDef - } - - if pkgDefs.packages[typeSpecDef.PkgPath] == nil { - pkgDefs.packages[typeSpecDef.PkgPath] = NewPackageDefinitions(astFile.Name.Name, typeSpecDef.PkgPath).AddTypeSpec(fullName, typeSpecDef) - } else if _, ok = pkgDefs.packages[typeSpecDef.PkgPath].TypeDefinitions[fullName]; !ok { - pkgDefs.packages[typeSpecDef.PkgPath].AddTypeSpec(fullName, typeSpecDef) - } - } - } - - } - } - } - } - } -} - -func (pkgDefs *PackagesDefinitions) collectConstVariables(astFile *ast.File, packagePath string, generalDeclaration *ast.GenDecl) { - pkg, ok := pkgDefs.packages[packagePath] - if !ok { - pkg = NewPackageDefinitions(astFile.Name.Name, packagePath) - pkgDefs.packages[packagePath] = pkg - } - - var lastValueSpec *ast.ValueSpec - for _, astSpec := range generalDeclaration.Specs { - valueSpec, ok := astSpec.(*ast.ValueSpec) - if !ok { - continue - } - if len(valueSpec.Names) == 1 && len(valueSpec.Values) == 1 { - lastValueSpec = valueSpec - } else if len(valueSpec.Names) == 1 && len(valueSpec.Values) == 0 && valueSpec.Type == nil && lastValueSpec != nil { - valueSpec.Type = lastValueSpec.Type - valueSpec.Values = lastValueSpec.Values - } - pkg.AddConst(astFile, valueSpec) - } -} - -func (pkgDefs *PackagesDefinitions) evaluateAllConstVariables() { - for _, pkg := range pkgDefs.packages { - for _, constVar := range pkg.OrderedConst { - pkgDefs.EvaluateConstValue(pkg, constVar, nil) - } - } -} - -// EvaluateConstValue evaluate a const variable. -func (pkgDefs *PackagesDefinitions) EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) { - if expr, ok := cv.Value.(ast.Expr); ok { - defer func() { - if err := recover(); err != nil { - if fi, ok := pkgDefs.files[cv.File]; ok { - pos := fi.FileSet.Position(cv.Name.NamePos) - pkgDefs.debug.Printf("warning: failed to evaluate const %s at %s:%d:%d, %v", cv.Name.Name, fi.Path, pos.Line, pos.Column, err) - } - } - }() - if recursiveStack == nil { - recursiveStack = make(map[string]struct{}) - } - fullConstName := fullTypeName(pkg.Path, cv.Name.Name) - if _, ok = recursiveStack[fullConstName]; ok { - return nil, nil - } - recursiveStack[fullConstName] = struct{}{} - - value, evalType := pkg.evaluateConstValue(cv.File, cv.Name.Obj.Data.(int), expr, pkgDefs, recursiveStack) - if cv.Type == nil && evalType != nil { - cv.Type = evalType - } - if value != nil { - cv.Value = value - } - return value, cv.Type - } - return cv.Value, cv.Type -} - -// EvaluateConstValueByName evaluate a const variable by name. -func (pkgDefs *PackagesDefinitions) EvaluateConstValueByName(file *ast.File, pkgName, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr) { - matchedPkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports(pkgName, file) - for _, pkgPath := range matchedPkgPaths { - if pkg, ok := pkgDefs.packages[pkgPath]; ok { - if cv, ok := pkg.ConstTable[constVariableName]; ok { - return pkgDefs.EvaluateConstValue(pkg, cv, recursiveStack) - } - } - } - if pkgDefs.parseDependency > 0 { - for _, pkgPath := range externalPkgPaths { - if err := pkgDefs.loadExternalPackage(pkgPath); err == nil { - if pkg, ok := pkgDefs.packages[pkgPath]; ok { - if cv, ok := pkg.ConstTable[constVariableName]; ok { - return pkgDefs.EvaluateConstValue(pkg, cv, recursiveStack) - } - } - } - } - } - return nil, nil -} - -func (pkgDefs *PackagesDefinitions) collectConstEnums(parsedSchemas map[*TypeSpecDef]*Schema) { - for _, pkg := range pkgDefs.packages { - for _, constVar := range pkg.OrderedConst { - if constVar.Type == nil { - continue - } - ident, ok := constVar.Type.(*ast.Ident) - if !ok || IsGolangPrimitiveType(ident.Name) { - continue - } - typeDef, ok := pkg.TypeDefinitions[ident.Name] - if !ok { - continue - } - - //delete it from parsed schemas, and will parse it again - if _, ok = parsedSchemas[typeDef]; ok { - delete(parsedSchemas, typeDef) - } - - if typeDef.Enums == nil { - typeDef.Enums = make([]EnumValue, 0) - } - - name := constVar.Name.Name - if _, ok = constVar.Value.(ast.Expr); ok { - continue - } - - enumValue := EnumValue{ - key: name, - Value: constVar.Value, - } - if constVar.Comment != nil && len(constVar.Comment.List) > 0 { - enumValue.Comment = constVar.Comment.List[0].Text - enumValue.Comment = strings.TrimPrefix(enumValue.Comment, "//") - enumValue.Comment = strings.TrimPrefix(enumValue.Comment, "/*") - enumValue.Comment = strings.TrimSuffix(enumValue.Comment, "*/") - enumValue.Comment = strings.TrimSpace(enumValue.Comment) - } - typeDef.Enums = append(typeDef.Enums, enumValue) - } - } -} - -func (pkgDefs *PackagesDefinitions) removeAllNotUniqueTypes() { - for key, ud := range pkgDefs.uniqueDefinitions { - if ud == nil { - delete(pkgDefs.uniqueDefinitions, key) - } - } -} - -func (pkgDefs *PackagesDefinitions) findTypeSpec(pkgPath string, typeName string) *TypeSpecDef { - if pkgDefs.packages == nil { - return nil - } - - pd, found := pkgDefs.packages[pkgPath] - if found { - typeSpec, ok := pd.TypeDefinitions[typeName] - if ok { - return typeSpec - } - } - - return nil -} - -func (pkgDefs *PackagesDefinitions) loadExternalPackage(importPath string) error { - cwd, err := os.Getwd() - if err != nil { - return err - } - - conf := loader.Config{ - ParserMode: goparser.ParseComments, - Cwd: cwd, - } - - conf.Import(importPath) - - loaderProgram, err := conf.Load() - if err != nil { - return err - } - - for _, info := range loaderProgram.AllPackages { - pkgPath := strings.TrimPrefix(info.Pkg.Path(), "vendor/") - for _, astFile := range info.Files { - pkgDefs.parseTypesFromFile(astFile, pkgPath, nil) - } - } - - return nil -} - -// findPackagePathFromImports finds out the package path of a package via ranging imports of an ast.File -// @pkg the name of the target package -// @file current ast.File in which to search imports -// @return the package paths of a package of @pkg. -func (pkgDefs *PackagesDefinitions) findPackagePathFromImports(pkg string, file *ast.File) (matchedPkgPaths, externalPkgPaths []string) { - if file == nil { - return - } - - if strings.ContainsRune(pkg, '.') { - pkg = strings.Split(pkg, ".")[0] - } - - matchLastPathPart := func(pkgPath string) bool { - paths := strings.Split(pkgPath, "/") - return paths[len(paths)-1] == pkg - } - - // prior to match named package - for _, imp := range file.Imports { - path := strings.Trim(imp.Path.Value, `"`) - if imp.Name != nil { - if imp.Name.Name == pkg { - // if name match, break loop and return - _, ok := pkgDefs.packages[path] - if ok { - matchedPkgPaths = []string{path} - externalPkgPaths = nil - } else { - externalPkgPaths = []string{path} - matchedPkgPaths = nil - } - break - } else if imp.Name.Name == "_" && len(pkg) > 0 { - //for unused types - pd, ok := pkgDefs.packages[path] - if ok { - if pd.Name == pkg { - matchedPkgPaths = append(matchedPkgPaths, path) - } - } else if matchLastPathPart(path) { - externalPkgPaths = append(externalPkgPaths, path) - } - } else if imp.Name.Name == "." && len(pkg) == 0 { - _, ok := pkgDefs.packages[path] - if ok { - matchedPkgPaths = append(matchedPkgPaths, path) - } else if len(pkg) == 0 || matchLastPathPart(path) { - externalPkgPaths = append(externalPkgPaths, path) - } - } - } else if pkgDefs.packages != nil && len(pkg) > 0 { - pd, ok := pkgDefs.packages[path] - if ok { - if pd.Name == pkg { - matchedPkgPaths = append(matchedPkgPaths, path) - } - } else if matchLastPathPart(path) { - externalPkgPaths = append(externalPkgPaths, path) - } - } - } - - if len(pkg) == 0 || file.Name.Name == pkg { - matchedPkgPaths = append(matchedPkgPaths, pkgDefs.files[file].PackagePath) - } - - return -} - -func (pkgDefs *PackagesDefinitions) findTypeSpecFromPackagePaths(matchedPkgPaths, externalPkgPaths []string, name string) (typeDef *TypeSpecDef) { - if pkgDefs.parseDependency > 0 { - for _, pkgPath := range externalPkgPaths { - if err := pkgDefs.loadExternalPackage(pkgPath); err == nil { - typeDef = pkgDefs.findTypeSpec(pkgPath, name) - if typeDef != nil { - return typeDef - } - } - } - } - - for _, pkgPath := range matchedPkgPaths { - typeDef = pkgDefs.findTypeSpec(pkgPath, name) - if typeDef != nil { - return typeDef - } - } - - return typeDef -} - -// FindTypeSpec finds out TypeSpecDef of a type by typeName -// @typeName the name of the target type, if it starts with a package name, find its own package path from imports on top of @file -// @file the ast.file in which @typeName is used -// @pkgPath the package path of @file. -func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef { - if IsGolangPrimitiveType(typeName) { - return nil - } - - if file == nil { // for test - return pkgDefs.uniqueDefinitions[typeName] - } - - parts := strings.Split(strings.Split(typeName, "[")[0], ".") - if len(parts) > 1 { - pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports(parts[0], file) - if len(externalPkgPaths) == 0 || pkgDefs.parseDependency == ParseNone { - typeDef, ok := pkgDefs.uniqueDefinitions[typeName] - if ok { - return typeDef - } - } - typeDef := pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, parts[1]) - return pkgDefs.parametrizeGenericType(file, typeDef, typeName) - } - - typeDef, ok := pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, typeName)] - if ok { - return typeDef - } - - //in case that comment //@name renamed the type with a name without a dot - typeDef, ok = pkgDefs.uniqueDefinitions[typeName] - if ok { - return typeDef - } - - name := parts[0] - typeDef, ok = pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, name)] - if !ok { - pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports("", file) - typeDef = pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, name) - } - return pkgDefs.parametrizeGenericType(file, typeDef, typeName) -} diff --git a/vendor/github.com/swaggo/swag/parser.go b/vendor/github.com/swaggo/swag/parser.go index 604f8278e9..b4871f4331 100644 --- a/vendor/github.com/swaggo/swag/parser.go +++ b/vendor/github.com/swaggo/swag/parser.go @@ -1,15 +1,13 @@ package swag import ( - "context" "encoding/json" - "errors" "fmt" "go/ast" "go/build" goparser "go/parser" "go/token" - "log" + "io/ioutil" "net/http" "os" "os/exec" @@ -18,8 +16,10 @@ import ( "sort" "strconv" "strings" + "unicode" "github.com/KyleBanks/depth" + "github.com/go-openapi/jsonreference" "github.com/go-openapi/spec" ) @@ -32,173 +32,49 @@ const ( // SnakeCase indicates using SnakeCase strategy for struct field. SnakeCase = "snakecase" - - idAttr = "@id" - acceptAttr = "@accept" - produceAttr = "@produce" - paramAttr = "@param" - successAttr = "@success" - failureAttr = "@failure" - responseAttr = "@response" - headerAttr = "@header" - tagsAttr = "@tags" - routerAttr = "@router" - deprecatedRouterAttr = "@deprecatedrouter" - summaryAttr = "@summary" - deprecatedAttr = "@deprecated" - securityAttr = "@security" - titleAttr = "@title" - conNameAttr = "@contact.name" - conURLAttr = "@contact.url" - conEmailAttr = "@contact.email" - licNameAttr = "@license.name" - licURLAttr = "@license.url" - versionAttr = "@version" - descriptionAttr = "@description" - descriptionMarkdownAttr = "@description.markdown" - secBasicAttr = "@securitydefinitions.basic" - secAPIKeyAttr = "@securitydefinitions.apikey" - secApplicationAttr = "@securitydefinitions.oauth2.application" - secImplicitAttr = "@securitydefinitions.oauth2.implicit" - secPasswordAttr = "@securitydefinitions.oauth2.password" - secAccessCodeAttr = "@securitydefinitions.oauth2.accesscode" - tosAttr = "@termsofservice" - extDocsDescAttr = "@externaldocs.description" - extDocsURLAttr = "@externaldocs.url" - xCodeSamplesAttr = "@x-codesamples" - scopeAttrPrefix = "@scope." - stateAttr = "@state" -) - -// ParseFlag determine what to parse -type ParseFlag int - -const ( - // ParseNone parse nothing - ParseNone ParseFlag = 0x00 - // ParseModels parse models - ParseModels = 0x01 - // ParseOperations parse operations - ParseOperations = 0x02 - // ParseAll parse operations and models - ParseAll = ParseOperations | ParseModels ) -var ( - // ErrRecursiveParseStruct recursively parsing struct. - ErrRecursiveParseStruct = errors.New("recursively parsing struct") - - // ErrFuncTypeField field type is func. - ErrFuncTypeField = errors.New("field type is func") - - // ErrFailedConvertPrimitiveType Failed to convert for swag to interpretable type. - ErrFailedConvertPrimitiveType = errors.New("swag property: failed convert primitive type") - - // ErrSkippedField .swaggo specifies field should be skipped. - ErrSkippedField = errors.New("field is skipped by global overrides") -) - -var allMethod = map[string]struct{}{ - http.MethodGet: {}, - http.MethodPut: {}, - http.MethodPost: {}, - http.MethodDelete: {}, - http.MethodOptions: {}, - http.MethodHead: {}, - http.MethodPatch: {}, -} - // Parser implements a parser for Go source files. type Parser struct { // swagger represents the root document object for the API specification swagger *spec.Swagger - // packages store entities of APIs, definitions, file, package path etc. and their relations - packages *PackagesDefinitions + // files is a map that stores map[real_go_file_path][astFile] + files map[string]*ast.File - // parsedSchemas store schemas which have been parsed from ast.TypeSpec - parsedSchemas map[*TypeSpecDef]*Schema + // TypeDefinitions is a map that stores [package name][type name][*ast.TypeSpec] + TypeDefinitions map[string]map[string]*ast.TypeSpec - // outputSchemas store schemas which will be export to swagger - outputSchemas map[*TypeSpecDef]*Schema + // ImportAliases is map that stores [import name][import package name][*ast.ImportSpec] + ImportAliases map[string]map[string]*ast.ImportSpec + + // CustomPrimitiveTypes is a map that stores custom primitive types to actual golang types [type name][string] + CustomPrimitiveTypes map[string]string + + // registerTypes is a map that stores [refTypeName][*ast.TypeSpec] + registerTypes map[string]*ast.TypeSpec - // PropNamingStrategy naming strategy PropNamingStrategy string - // ParseVendor parse vendor folder ParseVendor bool - // ParseDependencies whether swag should be parse outside dependency folder: 0 none, 1 models, 2 operations, 3 all - ParseDependency ParseFlag + // ParseDependencies whether swag should be parse outside dependency folder + ParseDependency bool // ParseInternal whether swag should parse internal packages ParseInternal bool - // Strict whether swag should error or warn when it detects cases which are most likely user errors - Strict bool - - // RequiredByDefault set validation required for all fields by default - RequiredByDefault bool - // structStack stores full names of the structures that were already parsed or are being parsed now - structStack []*TypeSpecDef + structStack []string // markdownFileDir holds the path to the folder, where markdown files are stored markdownFileDir string - // codeExampleFilesDir holds path to the folder, where code example files are stored - codeExampleFilesDir string - // collectionFormatInQuery set the default collectionFormat otherwise then 'csv' for array in query params collectionFormatInQuery string // excludes excludes dirs and files in SearchDir - excludes map[string]struct{} - - // packagePrefix is a list of package path prefixes, packages that do not - // match any one of them will be excluded when searching. - packagePrefix []string - - // tells parser to include only specific extension - parseExtension string - - // debugging output goes here - debug Debugger - - // fieldParserFactory create FieldParser - fieldParserFactory FieldParserFactory - - // Overrides allows global replacements of types. A blank replacement will be skipped. - Overrides map[string]string - - // parseGoList whether swag use go list to parse dependency - parseGoList bool - - // tags to filter the APIs after - tags map[string]struct{} - - // HostState is the state of the host - HostState string -} - -// FieldParserFactory create FieldParser. -type FieldParserFactory func(ps *Parser, field *ast.Field) FieldParser - -// FieldParser parse struct field. -type FieldParser interface { - ShouldSkip() bool - FieldName() (string, error) - FormName() string - HeaderName() string - PathName() string - CustomSchema() (*spec.Schema, error) - ComplementSchema(schema *spec.Schema) error - IsRequired() (bool, error) -} - -// Debugger is the interface that wraps the basic Printf method. -type Debugger interface { - Printf(format string, v ...interface{}) + excludes map[string]bool } // New creates a new Parser with default properties. @@ -209,262 +85,102 @@ func New(options ...func(*Parser)) *Parser { Info: &spec.Info{ InfoProps: spec.InfoProps{ Contact: &spec.ContactInfo{}, - License: nil, - }, - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{}, + License: &spec.License{}, }, }, Paths: &spec.Paths{ Paths: make(map[string]spec.PathItem), - VendorExtensible: spec.VendorExtensible{ - Extensions: nil, - }, }, - Definitions: make(map[string]spec.Schema), - SecurityDefinitions: make(map[string]*spec.SecurityScheme), - }, - VendorExtensible: spec.VendorExtensible{ - Extensions: nil, + Definitions: make(map[string]spec.Schema), }, }, - packages: NewPackagesDefinitions(), - debug: log.New(os.Stdout, "", log.LstdFlags), - parsedSchemas: make(map[*TypeSpecDef]*Schema), - outputSchemas: make(map[*TypeSpecDef]*Schema), - excludes: make(map[string]struct{}), - tags: make(map[string]struct{}), - fieldParserFactory: newTagBaseFieldParser, - Overrides: make(map[string]string), + files: make(map[string]*ast.File), + TypeDefinitions: make(map[string]map[string]*ast.TypeSpec), + ImportAliases: make(map[string]map[string]*ast.ImportSpec), + CustomPrimitiveTypes: make(map[string]string), + registerTypes: make(map[string]*ast.TypeSpec), + excludes: make(map[string]bool), } for _, option := range options { option(parser) } - parser.packages.debug = parser.debug - return parser } -// SetParseDependency sets whether to parse the dependent packages. -func SetParseDependency(parseDependency int) func(*Parser) { - return func(p *Parser) { - p.ParseDependency = ParseFlag(parseDependency) - if p.packages != nil { - p.packages.parseDependency = p.ParseDependency - } - } -} - -// SetMarkdownFileDirectory sets the directory to search for markdown files. +// SetMarkdownFileDirectory sets the directory to search for markdownfiles func SetMarkdownFileDirectory(directoryPath string) func(*Parser) { return func(p *Parser) { p.markdownFileDir = directoryPath } } -// SetCodeExamplesDirectory sets the directory to search for code example files. -func SetCodeExamplesDirectory(directoryPath string) func(*Parser) { - return func(p *Parser) { - p.codeExampleFilesDir = directoryPath - } -} - -// SetExcludedDirsAndFiles sets directories and files to be excluded when searching. +// SetExcludedDirsAndFiles sets directories and files to be excluded when searching func SetExcludedDirsAndFiles(excludes string) func(*Parser) { return func(p *Parser) { for _, f := range strings.Split(excludes, ",") { f = strings.TrimSpace(f) if f != "" { f = filepath.Clean(f) - p.excludes[f] = struct{}{} + p.excludes[f] = true } } } } -// SetPackagePrefix sets a list of package path prefixes from a comma-separated -// string, packages that do not match any one of them will be excluded when -// searching. -func SetPackagePrefix(packagePrefix string) func(*Parser) { - return func(p *Parser) { - for _, f := range strings.Split(packagePrefix, ",") { - f = strings.TrimSpace(f) - if f != "" { - p.packagePrefix = append(p.packagePrefix, f) - } - } - } -} - -// SetTags sets the tags to be included -func SetTags(include string) func(*Parser) { - return func(p *Parser) { - for _, f := range strings.Split(include, ",") { - f = strings.TrimSpace(f) - if f != "" { - p.tags[f] = struct{}{} - } - } - } -} - -// SetParseExtension parses only those operations which match given extension -func SetParseExtension(parseExtension string) func(*Parser) { - return func(p *Parser) { - p.parseExtension = parseExtension - } -} - -// SetStrict sets whether swag should error or warn when it detects cases which are most likely user errors. -func SetStrict(strict bool) func(*Parser) { - return func(p *Parser) { - p.Strict = strict - } -} - -// SetDebugger allows the use of user-defined implementations. -func SetDebugger(logger Debugger) func(parser *Parser) { - return func(p *Parser) { - if logger != nil { - p.debug = logger - } - } -} - -// SetFieldParserFactory allows the use of user-defined implementations. -func SetFieldParserFactory(factory FieldParserFactory) func(parser *Parser) { - return func(p *Parser) { - p.fieldParserFactory = factory - } -} - -// SetOverrides allows the use of user-defined global type overrides. -func SetOverrides(overrides map[string]string) func(parser *Parser) { - return func(p *Parser) { - for k, v := range overrides { - p.Overrides[k] = v - } - } -} - -// SetCollectionFormat set default collection format -func SetCollectionFormat(collectionFormat string) func(*Parser) { - return func(p *Parser) { - p.collectionFormatInQuery = collectionFormat - } -} +// ParseAPI parses general api info for given searchDir and mainAPIFile +func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error { + Printf("Generate general API Info, search dir:%s", searchDir) -// ParseUsingGoList sets whether swag use go list to parse dependency -func ParseUsingGoList(enabled bool) func(parser *Parser) { - return func(p *Parser) { - p.parseGoList = enabled + if err := parser.getAllGoFileInfo(searchDir); err != nil { + return err } -} -// ParseAPI parses general api info for given searchDir and mainAPIFile. -func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string, parseDepth int) error { - return parser.ParseAPIMultiSearchDir([]string{searchDir}, mainAPIFile, parseDepth) -} + var t depth.Tree + t.ResolveInternal = true -// skipPackageByPrefix returns true the given pkgpath does not match -// any user-defined package path prefixes. -func (parser *Parser) skipPackageByPrefix(pkgpath string) bool { - if len(parser.packagePrefix) == 0 { - return false - } - for _, prefix := range parser.packagePrefix { - if strings.HasPrefix(pkgpath, prefix) { - return false - } + absMainAPIFilePath, err := filepath.Abs(filepath.Join(searchDir, mainAPIFile)) + if err != nil { + return err } - return true -} - -// ParseAPIMultiSearchDir is like ParseAPI but for multiple search dirs. -func (parser *Parser) ParseAPIMultiSearchDir(searchDirs []string, mainAPIFile string, parseDepth int) error { - for _, searchDir := range searchDirs { - parser.debug.Printf("Generate general API Info, search dir:%s", searchDir) - packageDir, err := getPkgName(searchDir) - if err != nil { - parser.debug.Printf("warning: failed to get package name in dir: %s, error: %s", searchDir, err.Error()) - } - - err = parser.getAllGoFileInfo(packageDir, searchDir) + if parser.ParseDependency { + pkgName, err := getPkgName(filepath.Dir(absMainAPIFilePath)) if err != nil { return err } - } - - absMainAPIFilePath, err := filepath.Abs(filepath.Join(searchDirs[0], mainAPIFile)) - if err != nil { - return err - } - - // Use 'go list' command instead of depth.Resolve() - if parser.ParseDependency > 0 { - if parser.parseGoList { - pkgs, err := listPackages(context.Background(), filepath.Dir(absMainAPIFilePath), nil, "-deps") - if err != nil { - return fmt.Errorf("pkg %s cannot find all dependencies, %s", filepath.Dir(absMainAPIFilePath), err) - } - - length := len(pkgs) - for i := 0; i < length; i++ { - err := parser.getAllGoFileInfoFromDepsByList(pkgs[i], parser.ParseDependency) - if err != nil { - return err - } - } - } else { - var t depth.Tree - t.ResolveInternal = true - t.MaxDepth = parseDepth - - pkgName, err := getPkgName(filepath.Dir(absMainAPIFilePath)) - if err != nil { + if err := t.Resolve(pkgName); err != nil { + return fmt.Errorf("pkg %s cannot find all dependencies, %s", pkgName, err) + } + for i := 0; i < len(t.Root.Deps); i++ { + if err := parser.getAllGoFileInfoFromDeps(&t.Root.Deps[i]); err != nil { return err } - - err = t.Resolve(pkgName) - if err != nil { - return fmt.Errorf("pkg %s cannot find all dependencies, %s", pkgName, err) - } - for i := 0; i < len(t.Root.Deps); i++ { - err := parser.getAllGoFileInfoFromDeps(&t.Root.Deps[i], parser.ParseDependency) - if err != nil { - return err - } - } } } - err = parser.ParseGeneralAPIInfo(absMainAPIFilePath) - if err != nil { + if err := parser.ParseGeneralAPIInfo(absMainAPIFilePath); err != nil { return err } - parser.parsedSchemas, err = parser.packages.ParseTypes() - if err != nil { - return err + for _, astFile := range parser.files { + parser.ParseType(astFile) } - err = parser.packages.RangeFiles(parser.ParseRouterAPIInfo) - if err != nil { - return err + for fileName, astFile := range parser.files { + if err := parser.ParseRouterAPIInfo(fileName, astFile); err != nil { + return err + } } - return parser.checkOperationIDUniqueness() + return parser.parseDefinitions() } func getPkgName(searchDir string) (string, error) { cmd := exec.Command("go", "list", "-f={{.ImportPath}}") cmd.Dir = searchDir - var stdout, stderr strings.Builder - cmd.Stdout = &stdout cmd.Stderr = &stderr @@ -477,421 +193,253 @@ func getPkgName(searchDir string) (string, error) { if outStr[0] == '_' { // will shown like _/{GOPATH}/src/{YOUR_PACKAGE} when NOT enable GO MODULE. outStr = strings.TrimPrefix(outStr, "_"+build.Default.GOPATH+"/src/") } - f := strings.Split(outStr, "\n") - outStr = f[0] return outStr, nil } -// ParseGeneralAPIInfo parses general api info for given mainAPIFile path. +// ParseGeneralAPIInfo parses general api info for given mainAPIFile path func (parser *Parser) ParseGeneralAPIInfo(mainAPIFile string) error { - fileTree, err := goparser.ParseFile(token.NewFileSet(), mainAPIFile, nil, goparser.ParseComments) + fileSet := token.NewFileSet() + fileTree, err := goparser.ParseFile(fileSet, mainAPIFile, nil, goparser.ParseComments) if err != nil { return fmt.Errorf("cannot parse source files %s: %s", mainAPIFile, err) } parser.swagger.Swagger = "2.0" + securityMap := map[string]*spec.SecurityScheme{} for _, comment := range fileTree.Comments { - comments := strings.Split(comment.Text(), "\n") - if !isGeneralAPIComment(comments) { + if !isGeneralAPIComment(comment) { continue } - - err = parseGeneralAPIInfo(parser, comments) - if err != nil { - return err - } - } - - return nil -} - -func parseGeneralAPIInfo(parser *Parser, comments []string) error { - previousAttribute := "" - - // parsing classic meta data model - for line := 0; line < len(comments); line++ { - commentLine := comments[line] - commentLine = strings.TrimSpace(commentLine) - if len(commentLine) == 0 { - continue - } - fields := FieldsByAnySpace(commentLine, 2) - - attribute := fields[0] - var value string - if len(fields) > 1 { - value = fields[1] - } - - switch attr := strings.ToLower(attribute); attr { - case versionAttr, titleAttr, tosAttr, licNameAttr, licURLAttr, conNameAttr, conURLAttr, conEmailAttr: - setSwaggerInfo(parser.swagger, attr, value) - case descriptionAttr: + comments := strings.Split(comment.Text(), "\n") + previousAttribute := "" + // parsing classic meta data model + for i, commentLine := range comments { + attribute := strings.ToLower(strings.Split(commentLine, " ")[0]) + value := strings.TrimSpace(commentLine[len(attribute):]) + multilineBlock := false if previousAttribute == attribute { - parser.swagger.Info.Description += "\n" + value - - continue - } - - setSwaggerInfo(parser.swagger, attr, value) - case descriptionMarkdownAttr: - commentInfo, err := getMarkdownForTag("api", parser.markdownFileDir) - if err != nil { - return err - } - - setSwaggerInfo(parser.swagger, descriptionAttr, string(commentInfo)) - - 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 - - case acceptAttr: - err := parser.ParseAcceptComment(value) - if err != nil { - return err - } - case produceAttr: - err := parser.ParseProduceComment(value) - if err != nil { - return err - } - case "@schemes": - parser.swagger.Schemes = strings.Split(value, " ") - case "@tag.name": - parser.swagger.Tags = append(parser.swagger.Tags, spec.Tag{ - TagProps: spec.TagProps{ - Name: value, - }, - }) - case "@tag.description": - tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] - tag.TagProps.Description = value - replaceLastTag(parser.swagger.Tags, tag) - case "@tag.description.markdown": - tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] - - commentInfo, err := getMarkdownForTag(tag.TagProps.Name, parser.markdownFileDir) - if err != nil { - return err - } - - tag.TagProps.Description = string(commentInfo) - replaceLastTag(parser.swagger.Tags, tag) - case "@tag.docs.url": - tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] - tag.TagProps.ExternalDocs = &spec.ExternalDocumentation{ - URL: value, - Description: "", - } - - replaceLastTag(parser.swagger.Tags, tag) - case "@tag.docs.description": - tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] - if tag.TagProps.ExternalDocs == nil { - return fmt.Errorf("%s needs to come after a @tags.docs.url", attribute) - } - - tag.TagProps.ExternalDocs.Description = value - replaceLastTag(parser.swagger.Tags, tag) - - case secBasicAttr, secAPIKeyAttr, secApplicationAttr, secImplicitAttr, secPasswordAttr, secAccessCodeAttr: - scheme, err := parseSecAttributes(attribute, comments, &line) - if err != nil { - return err - } - - parser.swagger.SecurityDefinitions[value] = scheme - - case securityAttr: - parser.swagger.Security = append(parser.swagger.Security, parseSecurity(value)) - - case "@query.collection.format": - parser.collectionFormatInQuery = TransToValidCollectionFormat(value) - - case extDocsDescAttr, extDocsURLAttr: - if parser.swagger.ExternalDocs == nil { - parser.swagger.ExternalDocs = new(spec.ExternalDocumentation) + multilineBlock = true } - switch attr { - case extDocsDescAttr: - parser.swagger.ExternalDocs.Description = value - case extDocsURLAttr: - parser.swagger.ExternalDocs.URL = value - } - - default: - if strings.HasPrefix(attribute, "@x-") { - extensionName := attribute[1:] - - extExistsInSecurityDef := false - // for each security definition - for _, v := range parser.swagger.SecurityDefinitions { - // check if extension exists - _, extExistsInSecurityDef = v.VendorExtensible.Extensions.GetString(extensionName) - // if it exists in at least one, then we stop iterating - if extExistsInSecurityDef { - break - } + switch attribute { + case "@version": + parser.swagger.Info.Version = value + case "@title": + parser.swagger.Info.Title = value + case "@description": + if multilineBlock { + parser.swagger.Info.Description += "\n" + value + continue } - - // if it is present on security def, don't add it again - if extExistsInSecurityDef { - break + parser.swagger.Info.Description = value + case "@description.markdown": + commentInfo, err := getMarkdownForTag("api", parser.markdownFileDir) + if err != nil { + return err } - - if len(value) == 0 { - return fmt.Errorf("annotation %s need a value", attribute) + parser.swagger.Info.Description = string(commentInfo) + case "@termsofservice": + parser.swagger.Info.TermsOfService = value + case "@contact.name": + parser.swagger.Info.Contact.Name = value + case "@contact.email": + parser.swagger.Info.Contact.Email = value + case "@contact.url": + parser.swagger.Info.Contact.URL = value + case "@license.name": + parser.swagger.Info.License.Name = value + case "@license.url": + parser.swagger.Info.License.URL = value + case "@host": + parser.swagger.Host = value + case "@basepath": + parser.swagger.BasePath = value + case "@schemes": + parser.swagger.Schemes = getSchemes(commentLine) + case "@tag.name": + parser.swagger.Tags = append(parser.swagger.Tags, spec.Tag{ + TagProps: spec.TagProps{ + Name: value, + }, + }) + case "@tag.description": + tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] + tag.TagProps.Description = value + replaceLastTag(parser.swagger.Tags, tag) + case "@tag.description.markdown": + tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] + commentInfo, err := getMarkdownForTag(tag.TagProps.Name, parser.markdownFileDir) + if err != nil { + return err } - - var valueJSON interface{} - err := json.Unmarshal([]byte(value), &valueJSON) + tag.TagProps.Description = string(commentInfo) + replaceLastTag(parser.swagger.Tags, tag) + case "@tag.docs.url": + tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] + tag.TagProps.ExternalDocs = &spec.ExternalDocumentation{ + URL: value, + } + replaceLastTag(parser.swagger.Tags, tag) + case "@tag.docs.description": + tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] + if tag.TagProps.ExternalDocs == nil { + return fmt.Errorf("%s needs to come after a @tags.docs.url", attribute) + } + tag.TagProps.ExternalDocs.Description = value + replaceLastTag(parser.swagger.Tags, tag) + case "@securitydefinitions.basic": + securityMap[value] = spec.BasicAuth() + case "@securitydefinitions.apikey": + attrMap, _, err := extractSecurityAttribute(attribute, []string{"@in", "@name"}, comments[i+1:]) if err != nil { - return fmt.Errorf("annotation %s need a valid json value", attribute) + return err } - - if strings.Contains(extensionName, "logo") { - parser.swagger.Info.Extensions.Add(extensionName, valueJSON) - } else { - if parser.swagger.Extensions == nil { - parser.swagger.Extensions = make(map[string]interface{}) + securityMap[value] = spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]) + case "@securitydefinitions.oauth2.application": + attrMap, scopes, err := extractSecurityAttribute(attribute, []string{"@tokenurl"}, comments[i+1:]) + if err != nil { + return err + } + securityMap[value] = securitySchemeOAuth2Application(attrMap["@tokenurl"], scopes) + case "@securitydefinitions.oauth2.implicit": + attrMap, scopes, err := extractSecurityAttribute(attribute, []string{"@authorizationurl"}, comments[i+1:]) + if err != nil { + return err + } + securityMap[value] = securitySchemeOAuth2Implicit(attrMap["@authorizationurl"], scopes) + case "@securitydefinitions.oauth2.password": + attrMap, scopes, err := extractSecurityAttribute(attribute, []string{"@tokenurl"}, comments[i+1:]) + if err != nil { + return err + } + securityMap[value] = securitySchemeOAuth2Password(attrMap["@tokenurl"], scopes) + case "@securitydefinitions.oauth2.accesscode": + attrMap, scopes, err := extractSecurityAttribute(attribute, []string{"@tokenurl", "@authorizationurl"}, comments[i+1:]) + if err != nil { + return err + } + securityMap[value] = securitySchemeOAuth2AccessToken(attrMap["@authorizationurl"], attrMap["@tokenurl"], scopes) + case "@query.collection.format": + parser.collectionFormatInQuery = value + default: + prefixExtension := "@x-" + if len(attribute) > 5 { // Prefix extension + 1 char + 1 space + 1 char + if attribute[:len(prefixExtension)] == prefixExtension { + var valueJSON interface{} + split := strings.SplitAfter(commentLine, attribute+" ") + if len(split) < 2 { + return fmt.Errorf("annotation %s need a value", attribute) + } + extensionName := "x-" + strings.SplitAfter(attribute, prefixExtension)[1] + if err := json.Unmarshal([]byte(split[1]), &valueJSON); err != nil { + return fmt.Errorf("annotation %s need a valid json value", attribute) + } + parser.swagger.AddExtension(extensionName, valueJSON) } - - parser.swagger.Extensions[attribute[1:]] = valueJSON } } + previousAttribute = attribute } + } - previousAttribute = attribute + if len(securityMap) > 0 { + parser.swagger.SecurityDefinitions = securityMap } return nil } -func setSwaggerInfo(swagger *spec.Swagger, attribute, value string) { - switch attribute { - case versionAttr: - swagger.Info.Version = value - case titleAttr: - swagger.Info.Title = value - case tosAttr: - swagger.Info.TermsOfService = value - case descriptionAttr: - swagger.Info.Description = value - case conNameAttr: - swagger.Info.Contact.Name = value - case conEmailAttr: - swagger.Info.Contact.Email = value - case conURLAttr: - swagger.Info.Contact.URL = value - case licNameAttr: - swagger.Info.License = initIfEmpty(swagger.Info.License) - swagger.Info.License.Name = value - case licURLAttr: - swagger.Info.License = initIfEmpty(swagger.Info.License) - swagger.Info.License.URL = value +func isGeneralAPIComment(comment *ast.CommentGroup) bool { + for _, commentLine := range strings.Split(comment.Text(), "\n") { + attribute := strings.ToLower(strings.Split(commentLine, " ")[0]) + switch attribute { + // The @summary, @router, @success,@failure annotation belongs to Operation + case "@summary", "@router", "@success", "@failure": + return false + } } + return true } -func parseSecAttributes(context string, lines []string, index *int) (*spec.SecurityScheme, error) { - const ( - in = "@in" - name = "@name" - descriptionAttr = "@description" - tokenURL = "@tokenurl" - authorizationURL = "@authorizationurl" - ) - - var search []string - - attribute := strings.ToLower(FieldsByAnySpace(lines[*index], 2)[0]) - switch attribute { - case secBasicAttr: - return spec.BasicAuth(), nil - case secAPIKeyAttr: - search = []string{in, name} - case secApplicationAttr, secPasswordAttr: - search = []string{tokenURL} - case secImplicitAttr: - search = []string{authorizationURL} - case secAccessCodeAttr: - search = []string{tokenURL, authorizationURL} - } - - // For the first line we get the attributes in the context parameter, so we skip to the next one - *index++ - - attrMap, scopes := make(map[string]string), make(map[string]string) - extensions, description := make(map[string]interface{}), "" - -loopline: - for ; *index < len(lines); *index++ { - v := strings.TrimSpace(lines[*index]) - if len(v) == 0 { - continue - } - - fields := FieldsByAnySpace(v, 2) - securityAttr := strings.ToLower(fields[0]) - var value string - if len(fields) > 1 { - value = fields[1] - } - +func extractSecurityAttribute(context string, search []string, lines []string) (map[string]string, map[string]string, error) { + attrMap := map[string]string{} + scopes := map[string]string{} + for _, v := range lines { + securityAttr := strings.ToLower(strings.Split(v, " ")[0]) for _, findterm := range search { if securityAttr == findterm { - attrMap[securityAttr] = value - continue loopline + attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):]) + continue } } - - if isExists, err := isExistsScope(securityAttr); err != nil { - return nil, err - } else if isExists { - scopes[securityAttr[len(scopeAttrPrefix):]] = value - continue - } - - if strings.HasPrefix(securityAttr, "@x-") { - // Add the custom attribute without the @ - extensions[securityAttr[1:]] = value - continue + isExists, err := isExistsScope(securityAttr) + if err != nil { + return nil, nil, err } - - // Not mandatory field - if securityAttr == descriptionAttr { - description = value + if isExists { + scopScheme, err := getScopeScheme(securityAttr) + if err != nil { + return nil, nil, err + } + scopes[scopScheme] = v[len(securityAttr):] } - // next securityDefinitions if strings.Index(securityAttr, "@securitydefinitions.") == 0 { - // Go back to the previous line and break - *index-- - break } } - if len(attrMap) != len(search) { - return nil, fmt.Errorf("%s is %v required", context, search) + return nil, nil, fmt.Errorf("%s is %v required", context, search) } - - var scheme *spec.SecurityScheme - - switch attribute { - case secAPIKeyAttr: - scheme = spec.APIKeyAuth(attrMap[name], attrMap[in]) - case secApplicationAttr: - scheme = spec.OAuth2Application(attrMap[tokenURL]) - case secImplicitAttr: - scheme = spec.OAuth2Implicit(attrMap[authorizationURL]) - case secPasswordAttr: - scheme = spec.OAuth2Password(attrMap[tokenURL]) - case secAccessCodeAttr: - scheme = spec.OAuth2AccessToken(attrMap[authorizationURL], attrMap[tokenURL]) - } - - scheme.Description = description - - for extKey, extValue := range extensions { - scheme.AddExtension(extKey, extValue) - } - - for scope, scopeDescription := range scopes { - scheme.AddScope(scope, scopeDescription) - } - - return scheme, nil + return attrMap, scopes, nil } -func parseSecurity(commentLine string) map[string][]string { - securityMap := make(map[string][]string) - - for _, securityOption := range strings.Split(commentLine, "||") { - securityOption = strings.TrimSpace(securityOption) - - left, right := strings.Index(securityOption, "["), strings.Index(securityOption, "]") - - if !(left == -1 && right == -1) { - scopes := securityOption[left+1 : right] - - var options []string - - for _, scope := range strings.Split(scopes, ",") { - options = append(options, strings.TrimSpace(scope)) - } - - securityKey := securityOption[0:left] - securityMap[securityKey] = append(securityMap[securityKey], options...) - } else { - securityKey := strings.TrimSpace(securityOption) - securityMap[securityKey] = []string{} - } +func securitySchemeOAuth2Application(tokenurl string, scopes map[string]string) *spec.SecurityScheme { + securityScheme := spec.OAuth2Application(tokenurl) + for scope, description := range scopes { + securityScheme.AddScope(scope, description) } - - return securityMap + return securityScheme } -func initIfEmpty(license *spec.License) *spec.License { - if license == nil { - return new(spec.License) +func securitySchemeOAuth2Implicit(authorizationurl string, scopes map[string]string) *spec.SecurityScheme { + securityScheme := spec.OAuth2Implicit(authorizationurl) + for scope, description := range scopes { + securityScheme.AddScope(scope, description) } - - return license -} - -// ParseAcceptComment parses comment for given `accept` comment string. -func (parser *Parser) ParseAcceptComment(commentLine string) error { - return parseMimeTypeList(commentLine, &parser.swagger.Consumes, "%v accept type can't be accepted") + return securityScheme } -// ParseProduceComment parses comment for given `produce` comment string. -func (parser *Parser) ParseProduceComment(commentLine string) error { - return parseMimeTypeList(commentLine, &parser.swagger.Produces, "%v produce type can't be accepted") +func securitySchemeOAuth2Password(tokenurl string, scopes map[string]string) *spec.SecurityScheme { + securityScheme := spec.OAuth2Password(tokenurl) + for scope, description := range scopes { + securityScheme.AddScope(scope, description) + } + return securityScheme } -func isGeneralAPIComment(comments []string) bool { - for _, commentLine := range comments { - commentLine = strings.TrimSpace(commentLine) - if len(commentLine) == 0 { - continue - } - attribute := strings.ToLower(FieldsByAnySpace(commentLine, 2)[0]) - switch attribute { - // The @summary, @router, @success, @failure annotation belongs to Operation - case summaryAttr, routerAttr, successAttr, failureAttr, responseAttr: - return false - } +func securitySchemeOAuth2AccessToken(authorizationurl, tokenurl string, scopes map[string]string) *spec.SecurityScheme { + securityScheme := spec.OAuth2AccessToken(authorizationurl, tokenurl) + for scope, description := range scopes { + securityScheme.AddScope(scope, description) } - - return true + return securityScheme } func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) { - dirEntries, err := os.ReadDir(dirPath) + filesInfos, err := ioutil.ReadDir(dirPath) if err != nil { return nil, err } - for _, entry := range dirEntries { - if entry.IsDir() { + for _, fileInfo := range filesInfos { + if fileInfo.IsDir() { continue } - - fileName := entry.Name() + fileName := fileInfo.Name() if !strings.Contains(fileName, ".md") { continue @@ -899,747 +447,1038 @@ func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) { if strings.Contains(fileName, tagName) { fullPath := filepath.Join(dirPath, fileName) - - commentInfo, err := os.ReadFile(fullPath) + commentInfo, err := ioutil.ReadFile(fullPath) if err != nil { return nil, fmt.Errorf("Failed to read markdown file %s error: %s ", fullPath, err) } - return commentInfo, nil } } - return nil, fmt.Errorf("Unable to find markdown file for tag %s in the given directory", tagName) } +func getScopeScheme(scope string) (string, error) { + scopeValue := scope[strings.Index(scope, "@scope."):] + if scopeValue == "" { + return "", fmt.Errorf("@scope is empty") + } + return scope[len("@scope."):], nil +} + func isExistsScope(scope string) (bool, error) { s := strings.Fields(scope) for _, v := range s { - if strings.HasPrefix(v, scopeAttrPrefix) { + if strings.Contains(v, "@scope.") { if strings.Contains(v, ",") { return false, fmt.Errorf("@scope can't use comma(,) get=" + v) } } } + return strings.Contains(scope, "@scope."), nil +} - return strings.HasPrefix(scope, scopeAttrPrefix), nil +// getSchemes parses swagger schemes for given commentLine +func getSchemes(commentLine string) []string { + attribute := strings.ToLower(strings.Split(commentLine, " ")[0]) + return strings.Split(strings.TrimSpace(commentLine[len(attribute):]), " ") } -func getTagsFromComment(comment string) (tags []string) { - commentLine := strings.TrimSpace(strings.TrimLeft(comment, "/")) - if len(commentLine) == 0 { - return nil - } +// ParseRouterAPIInfo parses router api info for given astFile +func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) error { + for _, astDescription := range astFile.Decls { + switch astDeclaration := astDescription.(type) { + case *ast.FuncDecl: + if astDeclaration.Doc != nil && astDeclaration.Doc.List != nil { + operation := NewOperation() //for per 'function' comment, create a new 'Operation' object + operation.parser = parser + for _, comment := range astDeclaration.Doc.List { + if err := operation.ParseComment(comment.Text, astFile); err != nil { + return fmt.Errorf("ParseComment error in file %s :%+v", fileName, err) + } + } + var pathItem spec.PathItem + var ok bool - attribute := strings.Fields(commentLine)[0] - lineRemainder, lowerAttribute := strings.TrimSpace(commentLine[len(attribute):]), strings.ToLower(attribute) + if pathItem, ok = parser.swagger.Paths.Paths[operation.Path]; !ok { + pathItem = spec.PathItem{} + } + switch strings.ToUpper(operation.HTTPMethod) { + case http.MethodGet: + pathItem.Get = &operation.Operation + case http.MethodPost: + pathItem.Post = &operation.Operation + case http.MethodDelete: + pathItem.Delete = &operation.Operation + case http.MethodPut: + pathItem.Put = &operation.Operation + case http.MethodPatch: + pathItem.Patch = &operation.Operation + case http.MethodHead: + pathItem.Head = &operation.Operation + case http.MethodOptions: + pathItem.Options = &operation.Operation + } - if lowerAttribute == tagsAttr { - for _, tag := range strings.Split(lineRemainder, ",") { - tags = append(tags, strings.TrimSpace(tag)) + parser.swagger.Paths.Paths[operation.Path] = pathItem + } } } - return + return nil } -func (parser *Parser) matchTags(comments []*ast.Comment) (match bool) { - if len(parser.tags) == 0 { - return true - } +// ParseType parses type info for given astFile. +func (parser *Parser) ParseType(astFile *ast.File) { + if _, ok := parser.TypeDefinitions[astFile.Name.String()]; !ok { + parser.TypeDefinitions[astFile.Name.String()] = make(map[string]*ast.TypeSpec) + } + + for _, astDeclaration := range astFile.Decls { + if generalDeclaration, ok := astDeclaration.(*ast.GenDecl); ok && generalDeclaration.Tok == token.TYPE { + for _, astSpec := range generalDeclaration.Specs { + if typeSpec, ok := astSpec.(*ast.TypeSpec); ok { + typeName := fmt.Sprintf("%v", typeSpec.Type) + // check if its a custom primitive type + if IsGolangPrimitiveType(typeName) { + var typeSpecFullName = fmt.Sprintf("%s.%s", astFile.Name.String(), typeSpec.Name.String()) + parser.CustomPrimitiveTypes[typeSpecFullName] = TransToValidSchemeType(typeName) + } else { + parser.TypeDefinitions[astFile.Name.String()][typeSpec.Name.String()] = typeSpec + } - match = false - for _, comment := range comments { - for _, tag := range getTagsFromComment(comment.Text) { - if _, has := parser.tags["!"+tag]; has { - return false - } - if _, has := parser.tags[tag]; has { - match = true // keep iterating as it may contain a tag that is excluded + } } } } - if !match { - // If all tags are negation then we should return true - for key := range parser.tags { - if key[0] != '!' { - return false - } + for _, importSpec := range astFile.Imports { + if importSpec.Name == nil { + continue } - } - return true -} -func matchExtension(extensionToMatch string, comments []*ast.Comment) (match bool) { - if len(extensionToMatch) != 0 { - for _, comment := range comments { - commentLine := strings.TrimSpace(strings.TrimLeft(comment.Text, "/")) - fields := FieldsByAnySpace(commentLine, 2) - if len(fields) > 0 { - lowerAttribute := strings.ToLower(fields[0]) + alias := importSpec.Name.Name - if lowerAttribute == fmt.Sprintf("@x-%s", strings.ToLower(extensionToMatch)) { - return true - } - } + if _, ok := parser.ImportAliases[alias]; !ok { + parser.ImportAliases[alias] = make(map[string]*ast.ImportSpec) } - return false + + importParts := strings.Split(strings.Trim(importSpec.Path.Value, "\""), "/") + importPkgName := importParts[len(importParts)-1] + + parser.ImportAliases[alias][importPkgName] = importSpec } - return true } -// 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 - } - astDeclaration, ok := astDescription.(*ast.FuncDecl) - if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil { - if parser.matchTags(astDeclaration.Doc.List) && - matchExtension(parser.parseExtension, astDeclaration.Doc.List) { - // for per 'function' comment, create a new 'Operation' object - operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir)) - for _, comment := range astDeclaration.Doc.List { - err := operation.ParseComment(comment.Text, fileInfo.File) - 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 { - return err - } - } +func (parser *Parser) isInStructStack(refTypeName string) bool { + for _, structName := range parser.structStack { + if refTypeName == structName { + return true } } - - return nil + return false } -func refRouteMethodOp(item *spec.PathItem, method string) (op **spec.Operation) { - switch method { - case http.MethodGet: - op = &item.Get - case http.MethodPost: - op = &item.Post - case http.MethodDelete: - op = &item.Delete - case http.MethodPut: - op = &item.Put - case http.MethodPatch: - op = &item.Patch - case http.MethodHead: - op = &item.Head - case http.MethodOptions: - op = &item.Options +// parseDefinitions parses Swagger Api definitions. +func (parser *Parser) parseDefinitions() error { + // sort the typeNames so that parsing definitions is deterministic + typeNames := make([]string, 0, len(parser.registerTypes)) + for refTypeName := range parser.registerTypes { + typeNames = append(typeNames, refTypeName) + } + sort.Strings(typeNames) + + for _, refTypeName := range typeNames { + typeSpec := parser.registerTypes[refTypeName] + ss := strings.Split(refTypeName, ".") + pkgName := ss[0] + parser.structStack = nil + if err := parser.ParseDefinition(pkgName, typeSpec.Name.Name, typeSpec); err != nil { + return err + } } - - return + return nil } -func processRouterOperation(parser *Parser, operation *Operation) error { - for _, routeProperties := range operation.RouterProperties { - var ( - pathItem spec.PathItem - ok bool - ) - - pathItem, ok = parser.swagger.Paths.Paths[routeProperties.Path] - if !ok { - pathItem = spec.PathItem{} - } - - op := refRouteMethodOp(&pathItem, routeProperties.HTTPMethod) +// ParseDefinition parses given type spec that corresponds to the type under +// given name and package, and populates swagger schema definitions registry +// with a schema for the given type +func (parser *Parser) ParseDefinition(pkgName, typeName string, typeSpec *ast.TypeSpec) error { + refTypeName := TypeDocName(pkgName, typeSpec) - // check if we already have an operation for this path and method - if *op != nil { - err := fmt.Errorf("route %s %s is declared multiple times", routeProperties.HTTPMethod, routeProperties.Path) - if parser.Strict { - return err - } + if typeSpec == nil { + Println("Skipping '" + refTypeName + "', pkg '" + pkgName + "' not found, try add flag --parseDependency or --parseVendor.") + return nil + } - parser.debug.Printf("warning: %s\n", err) - } + if _, isParsed := parser.swagger.Definitions[refTypeName]; isParsed { + Println("Skipping '" + refTypeName + "', already parsed.") + return nil + } - if len(operation.RouterProperties) > 1 { - newOp := *operation - var validParams []spec.Parameter - for _, param := range newOp.Operation.OperationProps.Parameters { - if param.In == "path" && !strings.Contains(routeProperties.Path, param.Name) { - // This path param is not actually contained in the path, skip adding it to the final params - continue - } - validParams = append(validParams, param) - } - newOp.Operation.OperationProps.Parameters = validParams - *op = &newOp.Operation - } else { - *op = &operation.Operation - } + if parser.isInStructStack(refTypeName) { + Println("Skipping '" + refTypeName + "', recursion detected.") + return nil + } + parser.structStack = append(parser.structStack, refTypeName) - if routeProperties.Deprecated { - (*op).Deprecated = routeProperties.Deprecated - } + Println("Generating " + refTypeName) - parser.swagger.Paths.Paths[routeProperties.Path] = pathItem + schema, err := parser.parseTypeExpr(pkgName, typeName, typeSpec.Type) + if err != nil { + return err } - + parser.swagger.Definitions[refTypeName] = *schema return nil } -func convertFromSpecificToPrimitive(typeName string) (string, error) { - name := typeName - if strings.ContainsRune(name, '.') { - name = strings.Split(name, ".")[1] +func (parser *Parser) collectRequiredFields(pkgName string, properties map[string]spec.Schema, extraRequired []string) (requiredFields []string) { + // created sorted list of properties keys so when we iterate over them it's deterministic + ks := make([]string, 0, len(properties)) + for k := range properties { + ks = append(ks, k) } + sort.Strings(ks) - switch strings.ToUpper(name) { - case "TIME", "OBJECTID", "UUID": - return STRING, nil - case "DECIMAL": - return NUMBER, nil - } + requiredFields = make([]string, 0) - return typeName, ErrFailedConvertPrimitiveType -} + // iterate over keys list instead of map to avoid the random shuffle of the order that go does for maps + for _, k := range ks { + prop := properties[k] -func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (*spec.Schema, error) { - if override, ok := parser.Overrides[typeName]; ok { - parser.debug.Printf("Override detected for %s: using %s instead", typeName, override) - return parseObjectSchema(parser, override, file) + // todo find the pkgName of the property type + tname := prop.SchemaProps.Type[0] + if _, ok := parser.TypeDefinitions[pkgName][tname]; ok { + tspec := parser.TypeDefinitions[pkgName][tname] + parser.ParseDefinition(pkgName, tname, tspec) + } + requiredFields = append(requiredFields, prop.SchemaProps.Required...) + properties[k] = prop } - if IsInterfaceLike(typeName) { - return &spec.Schema{}, nil - } - if IsGolangPrimitiveType(typeName) { - return PrimitiveSchema(TransToValidSchemeType(typeName)), nil + if extraRequired != nil { + requiredFields = append(requiredFields, extraRequired...) } - schemaType, err := convertFromSpecificToPrimitive(typeName) - if err == nil { - return PrimitiveSchema(schemaType), nil - } + sort.Strings(requiredFields) + + return +} - typeSpecDef := parser.packages.FindTypeSpec(typeName, file) - if typeSpecDef == nil { - return nil, fmt.Errorf("cannot find type definition: %s", typeName) +func fullTypeName(pkgName, typeName string) string { + if pkgName != "" { + return pkgName + "." + typeName } + return typeName +} - if override, ok := parser.Overrides[typeSpecDef.FullPath()]; ok { - if override == "" { - parser.debug.Printf("Override detected for %s: ignoring", typeSpecDef.FullPath()) +// parseTypeExpr parses given type expression that corresponds to the type under +// given name and package, and returns swagger schema for it. +func (parser *Parser) parseTypeExpr(pkgName, typeName string, typeExpr ast.Expr) (*spec.Schema, error) { - return nil, ErrSkippedField + switch expr := typeExpr.(type) { + // type Foo struct {...} + case *ast.StructType: + if typedef, ok := parser.TypeDefinitions[pkgName][typeName]; ok { + refTypeName := TypeDocName(pkgName, typedef) + if schema, isParsed := parser.swagger.Definitions[refTypeName]; isParsed { + return &schema, nil + } } - parser.debug.Printf("Override detected for %s: using %s instead", typeSpecDef.FullPath(), override) - - separator := strings.LastIndex(override, ".") - if separator == -1 { - // treat as a swaggertype tag - parts := strings.Split(override, ",") + return parser.parseStruct(pkgName, expr.Fields) - return BuildCustomSchema(parts) + // type Foo Baz + case *ast.Ident: + if IsGolangPrimitiveType(expr.Name) { + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: spec.StringOrArray{TransToValidSchemeType(expr.Name)}, + }, + }, nil + } + refTypeName := fullTypeName(pkgName, expr.Name) + if typedef, ok := parser.TypeDefinitions[pkgName][expr.Name]; ok { + refTypeName = TypeDocName(pkgName, typedef) + if _, isParsed := parser.swagger.Definitions[refTypeName]; !isParsed { + parser.ParseDefinition(pkgName, expr.Name, typedef) + } } - typeSpecDef = parser.packages.findTypeSpec(override[0:separator], override[separator+1:]) - } + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.Ref{ + Ref: jsonreference.MustCreateRef("#/definitions/" + refTypeName), + }, + }, + }, nil - schema, ok := parser.parsedSchemas[typeSpecDef] - if !ok { - var err error + // type Foo *Baz + case *ast.StarExpr: + return parser.parseTypeExpr(pkgName, typeName, expr.X) - schema, err = parser.ParseDefinition(typeSpecDef) + // type Foo []Baz + case *ast.ArrayType: + itemSchema, err := parser.parseTypeExpr(pkgName, "", expr.Elt) if err != nil { - if err == ErrRecursiveParseStruct && ref { - return parser.getRefTypeSchema(typeSpecDef, schema), nil - } - return nil, fmt.Errorf("%s: %w", typeName, err) + return &spec.Schema{}, err } - } + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: itemSchema, + }, + }, + }, nil - if ref { - if IsComplexSchema(schema.Schema) { - return parser.getRefTypeSchema(typeSpecDef, schema), nil + // type Foo pkg.Bar + case *ast.SelectorExpr: + if xIdent, ok := expr.X.(*ast.Ident); ok { + return parser.parseTypeExpr(xIdent.Name, expr.Sel.Name, expr.Sel) } - // if it is a simple schema, just return a copy - newSchema := *schema.Schema - return &newSchema, nil + + // type Foo map[string]Bar + case *ast.MapType: + var valueSchema spec.SchemaOrBool + if _, ok := expr.Value.(*ast.InterfaceType); ok { + valueSchema.Allows = true + } else { + schema, err := parser.parseTypeExpr(pkgName, "", expr.Value) + if err != nil { + return &spec.Schema{}, err + } + valueSchema.Schema = schema + } + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &valueSchema, + }, + }, nil + // ... + default: + Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr) } - return schema.Schema, nil + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + }, + }, nil } -func (parser *Parser) getRefTypeSchema(typeSpecDef *TypeSpecDef, schema *Schema) *spec.Schema { - _, ok := parser.outputSchemas[typeSpecDef] - if !ok { - parser.swagger.Definitions[schema.Name] = spec.Schema{} +func (parser *Parser) parseStruct(pkgName string, fields *ast.FieldList) (*spec.Schema, error) { - if schema.Schema != nil { - parser.swagger.Definitions[schema.Name] = *schema.Schema + extraRequired := make([]string, 0) + properties := make(map[string]spec.Schema) + for _, field := range fields.List { + fieldProps, requiredFromAnon, err := parser.parseStructField(pkgName, field) + if err != nil { + return &spec.Schema{}, err + } + extraRequired = append(extraRequired, requiredFromAnon...) + for k, v := range fieldProps { + properties[k] = v } - - parser.outputSchemas[typeSpecDef] = schema } - refSchema := RefSchema(schema.Name) - - return refSchema -} + // collect requireds from our properties and anonymous fields + required := parser.collectRequiredFields(pkgName, properties, extraRequired) -func (parser *Parser) isInStructStack(typeSpecDef *TypeSpecDef) bool { - for _, specDef := range parser.structStack { - if typeSpecDef == specDef { - return true - } + // unset required from properties because we've collected them + for k, prop := range properties { + prop.SchemaProps.Required = make([]string, 0) + properties[k] = prop } - return false + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: properties, + Required: required, + }}, nil } -// ParseDefinition parses given type spec that corresponds to the type under -// given name and package, and populates swagger schema definitions registry -// with a schema for the given type -func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error) { - typeName := typeSpecDef.TypeName() - schema, found := parser.parsedSchemas[typeSpecDef] - if found { - parser.debug.Printf("Skipping '%s', already parsed.", typeName) +type structField struct { + name string + desc string + schemaType string + arrayType string + formatType string + isRequired bool + readOnly bool + crossPkg string + exampleValue interface{} + maximum *float64 + minimum *float64 + maxLength *int64 + minLength *int64 + enums []interface{} + defaultValue interface{} + extensions map[string]interface{} +} - return schema, nil +func (sf *structField) toStandardSchema() *spec.Schema { + required := make([]string, 0) + if sf.isRequired { + required = append(required, sf.name) } - - if parser.isInStructStack(typeSpecDef) { - parser.debug.Printf("Skipping '%s', recursion detected.", typeName) - - return &Schema{ - Name: typeName, - PkgPath: typeSpecDef.PkgPath, - Schema: PrimitiveSchema(OBJECT), - }, - ErrRecursiveParseStruct + return &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{sf.schemaType}, + Description: sf.desc, + Format: sf.formatType, + Required: required, + Maximum: sf.maximum, + Minimum: sf.minimum, + MaxLength: sf.maxLength, + MinLength: sf.minLength, + Enum: sf.enums, + Default: sf.defaultValue, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: sf.exampleValue, + ReadOnly: sf.readOnly, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: sf.extensions, + }, } +} - parser.structStack = append(parser.structStack, typeSpecDef) +func (parser *Parser) parseStructField(pkgName string, field *ast.Field) (map[string]spec.Schema, []string, error) { + properties := map[string]spec.Schema{} - parser.debug.Printf("Generating %s", typeName) + if field.Names == nil { + fullTypeName, err := getFieldType(field.Type) + if err != nil { + return properties, []string{}, nil + } - definition, err := parser.parseTypeExpr(typeSpecDef.File, typeSpecDef.TypeSpec.Type, false) - if err != nil { - parser.debug.Printf("Error parsing type definition '%s': %s", typeName, err) - return nil, err - } + typeName := fullTypeName - if definition.Description == "" { - fillDefinitionDescription(definition, typeSpecDef.File, typeSpecDef) - } + if splits := strings.Split(fullTypeName, "."); len(splits) > 1 { + pkgName = splits[0] + typeName = splits[1] + } - if len(typeSpecDef.Enums) > 0 { - var varnames []string - var enumComments = make(map[string]string) - for _, value := range typeSpecDef.Enums { - definition.Enum = append(definition.Enum, value.Value) - varnames = append(varnames, value.key) - if len(value.Comment) > 0 { - enumComments[value.key] = value.Comment + typeSpec := parser.TypeDefinitions[pkgName][typeName] + if typeSpec == nil { + // Check if the pkg name is an alias and try to define type spec using real package name + if aliases, ok := parser.ImportAliases[pkgName]; ok { + for alias := range aliases { + typeSpec = parser.TypeDefinitions[alias][typeName] + if typeSpec != nil { + break + } + } } } - if definition.Extensions == nil { - definition.Extensions = make(spec.Extensions) - } - definition.Extensions[enumVarNamesExtension] = varnames - if len(enumComments) > 0 { - definition.Extensions[enumCommentsExtension] = enumComments + if typeSpec != nil { + schema, err := parser.parseTypeExpr(pkgName, typeName, typeSpec.Type) + if err != nil { + return properties, []string{}, err + } + schemaType := "unknown" + if len(schema.SchemaProps.Type) > 0 { + schemaType = schema.SchemaProps.Type[0] + } + + switch schemaType { + case "object": + for k, v := range schema.SchemaProps.Properties { + properties[k] = v + } + case "array": + properties[typeName] = *schema + default: + Printf("Can't extract properties from a schema of type '%s'", schemaType) + } + return properties, schema.SchemaProps.Required, nil } - } - sch := Schema{ - Name: typeName, - PkgPath: typeSpecDef.PkgPath, - Schema: definition, + return properties, nil, nil } - parser.parsedSchemas[typeSpecDef] = &sch - // update an empty schema as a result of recursion - s2, found := parser.outputSchemas[typeSpecDef] - if found { - parser.swagger.Definitions[s2.Name] = *definition + structField, err := parser.parseField(pkgName, field) + if err != nil { + return properties, nil, err + } + if structField.name == "" { + return properties, nil, nil } - return &sch, nil -} - -func fullTypeName(parts ...string) string { - return strings.Join(parts, ".") -} + // TODO: find package of schemaType and/or arrayType + if structField.crossPkg != "" { + pkgName = structField.crossPkg + } -// fillDefinitionDescription additionally fills fields in definition (spec.Schema) -// TODO: If .go file contains many types, it may work for a long time -func fillDefinitionDescription(definition *spec.Schema, file *ast.File, typeSpecDef *TypeSpecDef) { - if file == nil { - return + fillObject := func(src, dest interface{}) error { + bin, err := json.Marshal(src) + if err != nil { + return err + } + return json.Unmarshal(bin, dest) } - for _, astDeclaration := range file.Decls { - generalDeclaration, ok := astDeclaration.(*ast.GenDecl) - if !ok || generalDeclaration.Tok != token.TYPE { - continue + + //for spec.Schema have implemented json.Marshaler, here in another way to convert + fillSchema := func(src, dest *spec.Schema) error { + err = fillObject(&src.SchemaProps, &dest.SchemaProps) + if err != nil { + return err } + err = fillObject(&src.SwaggerSchemaProps, &dest.SwaggerSchemaProps) + if err != nil { + return err + } + return fillObject(&src.VendorExtensible, &dest.VendorExtensible) + } - for _, astSpec := range generalDeclaration.Specs { - typeSpec, ok := astSpec.(*ast.TypeSpec) - if !ok || typeSpec != typeSpecDef.TypeSpec { - continue + if typeSpec, ok := parser.TypeDefinitions[pkgName][structField.schemaType]; ok { // user type field + // write definition if not yet present + err = parser.ParseDefinition(pkgName, structField.schemaType, typeSpec) + if err != nil { + return properties, nil, err + } + required := make([]string, 0) + if structField.isRequired { + required = append(required, structField.name) + } + properties[structField.name] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, // to avoid swagger validation error + Description: structField.desc, + Required: required, + Ref: spec.Ref{ + Ref: jsonreference.MustCreateRef("#/definitions/" + TypeDocName(pkgName, typeSpec)), + }, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + ReadOnly: structField.readOnly, + }, + } + } else if structField.schemaType == "array" { // array field type + // if defined -- ref it + if typeSpec, ok := parser.TypeDefinitions[pkgName][structField.arrayType]; ok { // user type in array + parser.ParseDefinition(pkgName, structField.arrayType, + parser.TypeDefinitions[pkgName][structField.arrayType]) + required := make([]string, 0) + if structField.isRequired { + required = append(required, structField.name) + } + properties[structField.name] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{structField.schemaType}, + Description: structField.desc, + Required: required, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.Ref{ + Ref: jsonreference.MustCreateRef("#/definitions/" + TypeDocName(pkgName, typeSpec)), + }, + }, + }, + }, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + ReadOnly: structField.readOnly, + }, + } + } else if structField.arrayType == "object" { + // Anonymous struct + if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { // if array + props := make(map[string]spec.Schema) + if expr, ok := astTypeArray.Elt.(*ast.StructType); ok { + for _, field := range expr.Fields.List { + var fieldProps map[string]spec.Schema + fieldProps, _, err = parser.parseStructField(pkgName, field) + if err != nil { + return properties, nil, err + } + for k, v := range fieldProps { + props[k] = v + } + } + properties[structField.name] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{structField.schemaType}, + Description: structField.desc, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: props, + }, + }, + }, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + ReadOnly: structField.readOnly, + }, + } + } else { + schema, _ := parser.parseTypeExpr(pkgName, "", astTypeArray.Elt) + properties[structField.name] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{structField.schemaType}, + Description: structField.desc, + Items: &spec.SchemaOrArray{ + Schema: schema, + }, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + ReadOnly: structField.readOnly, + }, + } + } + } + } else if structField.arrayType == "array" { + if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { + schema, _ := parser.parseTypeExpr(pkgName, "", astTypeArray.Elt) + properties[structField.name] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{structField.schemaType}, + Description: structField.desc, + Items: &spec.SchemaOrArray{ + Schema: schema, + }, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + ReadOnly: structField.readOnly, + }, + } + } + } else { + // standard type in array + required := make([]string, 0) + if structField.isRequired { + required = append(required, structField.name) } - definition.Description = - extractDeclarationDescription(typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc) - } - } -} - -// extractDeclarationDescription gets first description -// from attribute descriptionAttr in commentGroups (ast.CommentGroup) -func extractDeclarationDescription(commentGroups ...*ast.CommentGroup) string { - var description string - - for _, commentGroup := range commentGroups { - if commentGroup == nil { - continue + properties[structField.name] = spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{structField.schemaType}, + Description: structField.desc, + Format: structField.formatType, + Required: required, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{structField.arrayType}, + Maximum: structField.maximum, + Minimum: structField.minimum, + MaxLength: structField.maxLength, + MinLength: structField.minLength, + Enum: structField.enums, + Default: structField.defaultValue, + }, + }, + }, + }, + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: structField.exampleValue, + ReadOnly: structField.readOnly, + }, + } } + } else if astTypeMap, ok := field.Type.(*ast.MapType); ok { // if map + stdSchema := structField.toStandardSchema() + mapValueSchema, err := parser.parseTypeExpr(pkgName, "", astTypeMap) + if err != nil { + return properties, nil, err + } + stdSchema.Type = mapValueSchema.Type + stdSchema.AdditionalProperties = mapValueSchema.AdditionalProperties + properties[structField.name] = *stdSchema + } else { + stdSchema := structField.toStandardSchema() + properties[structField.name] = *stdSchema + + if nestStar, ok := field.Type.(*ast.StarExpr); ok { + if !IsGolangPrimitiveType(structField.schemaType) { + schema, err := parser.parseTypeExpr(pkgName, structField.schemaType, nestStar.X) + if err != nil { + return properties, nil, err + } - isHandlingDescription := false - - for _, comment := range commentGroup.List { - commentText := strings.TrimSpace(strings.TrimLeft(comment.Text, "/")) - if len(commentText) == 0 { - continue + if len(schema.SchemaProps.Type) > 0 { + err = fillSchema(schema, stdSchema) + if err != nil { + return properties, nil, err + } + properties[structField.name] = *stdSchema + return properties, nil, nil + } } - attribute := FieldsByAnySpace(commentText, 2)[0] - - if strings.ToLower(attribute) != descriptionAttr { - if !isHandlingDescription { - continue + } else if nestStruct, ok := field.Type.(*ast.StructType); ok { + props := map[string]spec.Schema{} + nestRequired := make([]string, 0) + for _, v := range nestStruct.Fields.List { + p, _, err := parser.parseStructField(pkgName, v) + if err != nil { + return properties, nil, err + } + for k, v := range p { + if v.SchemaProps.Type[0] != "object" { + nestRequired = append(nestRequired, v.SchemaProps.Required...) + v.SchemaProps.Required = make([]string, 0) + } + props[k] = v } - - break } - - isHandlingDescription = true - description += " " + strings.TrimSpace(commentText[len(attribute):]) + stdSchema.Properties = props + stdSchema.Required = nestRequired + properties[structField.name] = *stdSchema } } - - return strings.TrimLeft(description, " ") + return properties, nil, nil } -// parseTypeExpr parses given type expression that corresponds to the type under -// given name and package, and returns swagger schema for it. -func (parser *Parser) parseTypeExpr(file *ast.File, typeExpr ast.Expr, ref bool) (*spec.Schema, error) { - switch expr := typeExpr.(type) { - // type Foo interface{} - case *ast.InterfaceType: - return &spec.Schema{}, nil - - // type Foo struct {...} - case *ast.StructType: - return parser.parseStruct(file, expr.Fields) +func getFieldType(field interface{}) (string, error) { - // type Foo Baz + switch ftype := field.(type) { case *ast.Ident: - return parser.getTypeSchema(expr.Name, file, ref) + return ftype.Name, nil - // type Foo *Baz - case *ast.StarExpr: - return parser.parseTypeExpr(file, expr.X, ref) - - // type Foo pkg.Bar case *ast.SelectorExpr: - if xIdent, ok := expr.X.(*ast.Ident); ok { - return parser.getTypeSchema(fullTypeName(xIdent.Name, expr.Sel.Name), file, ref) - } - // type Foo []Baz - case *ast.ArrayType: - itemSchema, err := parser.parseTypeExpr(file, expr.Elt, true) + packageName, err := getFieldType(ftype.X) if err != nil { - return nil, err + return "", err } + return fmt.Sprintf("%s.%s", packageName, ftype.Sel.Name), nil - return spec.ArrayProperty(itemSchema), nil - // type Foo map[string]Bar - case *ast.MapType: - if _, ok := expr.Value.(*ast.InterfaceType); ok { - return spec.MapProperty(nil), nil - } - schema, err := parser.parseTypeExpr(file, expr.Value, true) + case *ast.StarExpr: + fullName, err := getFieldType(ftype.X) if err != nil { - return nil, err + return "", err } + return fullName, nil - return spec.MapProperty(schema), nil - - case *ast.FuncType: - return nil, ErrFuncTypeField - // ... } - - return parser.parseGenericTypeExpr(file, typeExpr) + return "", fmt.Errorf("unknown field type %#v", field) } -func (parser *Parser) parseStruct(file *ast.File, fields *ast.FieldList) (*spec.Schema, error) { - required, properties := make([]string, 0), make(map[string]spec.Schema) - - for _, field := range fields.List { - fieldProps, requiredFromAnon, err := parser.parseStructField(file, field) - if err != nil { - if errors.Is(err, ErrFuncTypeField) || errors.Is(err, ErrSkippedField) { - continue - } +func (parser *Parser) parseField(pkgName string, field *ast.Field) (*structField, error) { + prop, err := getPropertyName(pkgName, field.Type, parser) + if err != nil { + return nil, err + } + if len(prop.ArrayType) == 0 { + if err := CheckSchemaType(prop.SchemaType); err != nil { return nil, err } - - if len(fieldProps) == 0 { - continue - } - - required = append(required, requiredFromAnon...) - - for k, v := range fieldProps { - properties[k] = v + } else { + if err := CheckSchemaType("array"); err != nil { + return nil, err } } - sort.Strings(required) + // Skip func fields. + if prop.SchemaType == "func" { + return &structField{name: ""}, nil + } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{OBJECT}, - Properties: properties, - Required: required, - }, - }, nil -} + // Skip non-exported fields. + if !ast.IsExported(field.Names[0].Name) { + return &structField{name: ""}, nil + } -func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[string]spec.Schema, []string, error) { - if field.Tag != nil { - skip, ok := reflect.StructTag(strings.ReplaceAll(field.Tag.Value, "`", "")).Lookup("swaggerignore") - if ok && strings.EqualFold(skip, "true") { - return nil, nil, nil - } + structField := &structField{ + name: field.Names[0].Name, + schemaType: prop.SchemaType, + arrayType: prop.ArrayType, + crossPkg: prop.CrossPkg, } - ps := parser.fieldParserFactory(parser, field) + switch parser.PropNamingStrategy { + case SnakeCase: + structField.name = toSnakeCase(structField.name) + case PascalCase: + //use struct field name + case CamelCase: + structField.name = toLowerCamelCase(structField.name) + default: + structField.name = toLowerCamelCase(structField.name) + } - if ps.ShouldSkip() { - return nil, nil, nil + if field.Doc != nil { + structField.desc = strings.TrimSpace(field.Doc.Text()) + } + if structField.desc == "" && field.Comment != nil { + structField.desc = strings.TrimSpace(field.Comment.Text()) } - fieldName, err := ps.FieldName() - if err != nil { - return nil, nil, err + if field.Tag == nil { + return structField, nil } + // `json:"tag"` -> json:"tag" + structTag := reflect.StructTag(strings.Replace(field.Tag.Value, "`", "", -1)) - if fieldName == "" { - typeName, err := getFieldType(file, field.Type, nil) - if err != nil { - return nil, nil, fmt.Errorf("%s: %w", fieldName, err) - } + if ignoreTag := structTag.Get("swaggerignore"); ignoreTag == "true" { + structField.name = "" + return structField, nil + } - schema, err := parser.getTypeSchema(typeName, file, false) - if err != nil { - return nil, nil, fmt.Errorf("%s: %w", fieldName, err) + jsonTag := structTag.Get("json") + hasStringTag := false + // json:"tag,hoge" + if strings.Contains(jsonTag, ",") { + // json:"name,string" or json:",string" + if strings.Contains(jsonTag, ",string") { + hasStringTag = true } - if len(schema.Type) > 0 && schema.Type[0] == OBJECT { - if len(schema.Properties) == 0 { - return nil, nil, nil + // json:",hoge" + if strings.HasPrefix(jsonTag, ",") { + jsonTag = "" + } else { + jsonTag = strings.SplitN(jsonTag, ",", 2)[0] + } + } + if jsonTag == "-" { + structField.name = "" + return structField, nil + } else if jsonTag != "" { + structField.name = jsonTag + } + + if typeTag := structTag.Get("swaggertype"); typeTag != "" { + parts := strings.Split(typeTag, ",") + if 0 < len(parts) && len(parts) <= 2 { + newSchemaType := parts[0] + newArrayType := structField.arrayType + if len(parts) >= 2 { + if newSchemaType == "array" { + newArrayType = parts[1] + if err := CheckSchemaType(newArrayType); err != nil { + return nil, err + } + } else if newSchemaType == "primitive" { + newSchemaType = parts[1] + newArrayType = parts[1] + } } - properties := map[string]spec.Schema{} - for k, v := range schema.Properties { - properties[k] = v + if err := CheckSchemaType(newSchemaType); err != nil { + return nil, err } - return properties, schema.SchemaProps.Required, nil + structField.schemaType = newSchemaType + structField.arrayType = newArrayType } - // for alias type of non-struct types ,such as array,map, etc. ignore field tag. - return map[string]spec.Schema{typeName: *schema}, nil, nil - } - - schema, err := ps.CustomSchema() - if err != nil { - return nil, nil, fmt.Errorf("%s: %w", fieldName, err) - } - - if schema == nil { - typeName, err := getFieldType(file, field.Type, nil) - if err == nil { - // named type - schema, err = parser.getTypeSchema(typeName, file, true) + if exampleTag := structTag.Get("example"); exampleTag != "" { + if hasStringTag { + // then the example must be in string format + structField.exampleValue = exampleTag } else { - // unnamed type - schema, err = parser.parseTypeExpr(file, field.Type, false) - } - - if err != nil { - return nil, nil, fmt.Errorf("%s: %w", fieldName, err) + example, err := defineTypeOfExample(structField.schemaType, structField.arrayType, exampleTag) + if err != nil { + return nil, err + } + structField.exampleValue = example } } - - err = ps.ComplementSchema(schema) - if err != nil { - return nil, nil, fmt.Errorf("%s: %w", fieldName, err) - } - - var tagRequired []string - - required, err := ps.IsRequired() - if err != nil { - return nil, nil, fmt.Errorf("%s: %w", fieldName, err) + if formatTag := structTag.Get("format"); formatTag != "" { + structField.formatType = formatTag } - - if required { - tagRequired = append(tagRequired, fieldName) + if bindingTag := structTag.Get("binding"); bindingTag != "" { + for _, val := range strings.Split(bindingTag, ",") { + if val == "required" { + structField.isRequired = true + break + } + } } - - if schema.Extensions == nil { - schema.Extensions = make(spec.Extensions) + if validateTag := structTag.Get("validate"); validateTag != "" { + for _, val := range strings.Split(validateTag, ",") { + if val == "required" { + structField.isRequired = true + break + } + } } - if formName := ps.FormName(); len(formName) > 0 { - schema.Extensions["formData"] = formName + if extensionsTag := structTag.Get("extensions"); extensionsTag != "" { + structField.extensions = map[string]interface{}{} + for _, val := range strings.Split(extensionsTag, ",") { + parts := strings.SplitN(val, "=", 2) + if len(parts) == 2 { + structField.extensions[parts[0]] = parts[1] + } else { + structField.extensions[parts[0]] = true + } + } } - if headerName := ps.HeaderName(); len(headerName) > 0 { - schema.Extensions["header"] = headerName + if enumsTag := structTag.Get("enums"); enumsTag != "" { + enumType := structField.schemaType + if structField.schemaType == "array" { + enumType = structField.arrayType + } + + for _, e := range strings.Split(enumsTag, ",") { + value, err := defineType(enumType, e) + if err != nil { + return nil, err + } + structField.enums = append(structField.enums, value) + } } - if pathName := ps.PathName(); len(pathName) > 0 { - schema.Extensions["path"] = pathName + if defaultTag := structTag.Get("default"); defaultTag != "" { + value, err := defineType(structField.schemaType, defaultTag) + if err != nil { + return nil, err + } + structField.defaultValue = value } - return map[string]spec.Schema{fieldName: *schema}, tagRequired, nil -} - -func getFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) { - switch fieldType := field.(type) { - case *ast.Ident: - return fieldType.Name, nil - case *ast.SelectorExpr: - packageName, err := getFieldType(file, fieldType.X, genericParamTypeDefs) + if IsNumericType(structField.schemaType) || IsNumericType(structField.arrayType) { + maximum, err := getFloatTag(structTag, "maximum") if err != nil { - return "", err + return nil, err } + structField.maximum = maximum - return fullTypeName(packageName, fieldType.Sel.Name), nil - case *ast.StarExpr: - fullName, err := getFieldType(file, fieldType.X, genericParamTypeDefs) + minimum, err := getFloatTag(structTag, "minimum") if err != nil { - return "", err + return nil, err } - - return fullName, nil - default: - return getGenericFieldType(file, field, genericParamTypeDefs) + structField.minimum = minimum } -} + if structField.schemaType == "string" || structField.arrayType == "string" { + maxLength, err := getIntTag(structTag, "maxLength") + if err != nil { + return nil, err + } + structField.maxLength = maxLength -func (parser *Parser) getUnderlyingSchema(schema *spec.Schema) *spec.Schema { - if schema == nil { - return nil + minLength, err := getIntTag(structTag, "minLength") + if err != nil { + return nil, err + } + structField.minLength = minLength + } + if readOnly := structTag.Get("readonly"); readOnly != "" { + structField.readOnly = readOnly == "true" } - if url := schema.Ref.GetURL(); url != nil { - if pos := strings.LastIndexByte(url.Fragment, '/'); pos >= 0 { - name := url.Fragment[pos+1:] - if schema, ok := parser.swagger.Definitions[name]; ok { - return &schema - } + // perform this after setting everything else (min, max, etc...) + if hasStringTag { + + // @encoding/json: "It applies only to fields of string, floating point, integer, or boolean types." + defaultValues := map[string]string{ + // Zero Values as string + "string": "", + "integer": "0", + "boolean": "false", + "number": "0", } - } - if len(schema.AllOf) > 0 { - merged := &spec.Schema{} - MergeSchema(merged, schema) - for _, s := range schema.AllOf { - MergeSchema(merged, parser.getUnderlyingSchema(&s)) + if defaultValue, ok := defaultValues[structField.schemaType]; ok { + structField.schemaType = "string" + + if structField.exampleValue == nil { + // if exampleValue is not defined by the user, + // we will force an example with a correct value + // (eg: int->"0", bool:"false") + structField.exampleValue = defaultValue + } } - return merged } - return nil + + return structField, nil } -// GetSchemaTypePath get path of schema type. -func (parser *Parser) GetSchemaTypePath(schema *spec.Schema, depth int) []string { - if schema == nil || depth == 0 { - return nil +func replaceLastTag(slice []spec.Tag, element spec.Tag) { + slice = slice[:len(slice)-1] + slice = append(slice, element) +} + +func getFloatTag(structTag reflect.StructTag, tagName string) (*float64, error) { + strValue := structTag.Get(tagName) + if strValue == "" { + return nil, nil } - if underlying := parser.getUnderlyingSchema(schema); underlying != nil { - return parser.GetSchemaTypePath(underlying, depth) + value, err := strconv.ParseFloat(strValue, 64) + if err != nil { + return nil, fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err) } - if len(schema.Type) > 0 { - switch schema.Type[0] { - case ARRAY: - depth-- + return &value, nil +} + +func getIntTag(structTag reflect.StructTag, tagName string) (*int64, error) { + strValue := structTag.Get(tagName) + if strValue == "" { + return nil, nil + } - s := []string{schema.Type[0]} + value, err := strconv.ParseInt(strValue, 10, 64) + if err != nil { + return nil, fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err) + } - return append(s, parser.GetSchemaTypePath(schema.Items.Schema, depth)...) - case OBJECT: - if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { - // for map - depth-- + return &value, nil +} - s := []string{schema.Type[0]} +func toSnakeCase(in string) string { + runes := []rune(in) + length := len(runes) - return append(s, parser.GetSchemaTypePath(schema.AdditionalProperties.Schema, depth)...) - } + var out []rune + for i := 0; i < length; i++ { + if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) { + out = append(out, '_') } - - return []string{schema.Type[0]} + out = append(out, unicode.ToLower(runes[i])) } - - return []string{ANY} + return string(out) } -func replaceLastTag(slice []spec.Tag, element spec.Tag) { - slice = append(slice[:len(slice)-1], element) +func toLowerCamelCase(in string) string { + runes := []rune(in) + + var out []rune + flag := false + for i, curr := range runes { + if (i == 0 && unicode.IsUpper(curr)) || (flag && unicode.IsUpper(curr)) { + out = append(out, unicode.ToLower(curr)) + flag = true + } else { + out = append(out, curr) + flag = false + } + } + + return string(out) } -// defineTypeOfExample example value define the type (object and array unsupported). +// defineTypeOfExample example value define the type (object and array unsupported) func defineTypeOfExample(schemaType, arrayType, exampleValue string) (interface{}, error) { switch schemaType { - case STRING: + case "string": return exampleValue, nil - case NUMBER: + case "number": v, err := strconv.ParseFloat(exampleValue, 64) if err != nil { return nil, fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err) } - return v, nil - case INTEGER: + case "integer": v, err := strconv.Atoi(exampleValue) if err != nil { return nil, fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err) } - return v, nil - case BOOLEAN: + case "boolean": v, err := strconv.ParseBool(exampleValue) if err != nil { return nil, fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err) } - return v, nil - case ARRAY: + case "array": values := strings.Split(exampleValue, ",") result := make([]interface{}, 0) for _, value := range values { @@ -1647,85 +1486,31 @@ func defineTypeOfExample(schemaType, arrayType, exampleValue string) (interface{ if err != nil { return nil, err } - result = append(result, v) } - - return result, nil - case OBJECT: - if arrayType == "" { - return nil, fmt.Errorf("%s is unsupported type in example value `%s`", schemaType, exampleValue) - } - - values := strings.Split(exampleValue, ",") - - result := map[string]interface{}{} - - for _, value := range values { - mapData := strings.SplitN(value, ":", 2) - - if len(mapData) == 2 { - v, err := defineTypeOfExample(arrayType, "", mapData[1]) - if err != nil { - return nil, err - } - - result[mapData[0]] = v - - continue - } - - return nil, fmt.Errorf("example value %s should format: key:value", exampleValue) - } - return result, nil + default: + return nil, fmt.Errorf("%s is unsupported type in example value", schemaType) } - - return nil, fmt.Errorf("%s is unsupported type in example value %s", schemaType, exampleValue) } // GetAllGoFileInfo gets all Go source files information for given searchDir. -func (parser *Parser) getAllGoFileInfo(packageDir, searchDir string) error { - if parser.skipPackageByPrefix(packageDir) { - return nil // ignored by user-defined package path prefixes - } - return filepath.Walk(searchDir, func(path string, f os.FileInfo, _ error) error { - err := parser.Skip(path, f) - if err != nil { - return err - } - - if f.IsDir() { - return nil - } - - relPath, err := filepath.Rel(searchDir, path) - if err != nil { - return err - } - - return parser.parseFile(filepath.ToSlash(filepath.Dir(filepath.Clean(filepath.Join(packageDir, relPath)))), path, nil, ParseAll) - }) +func (parser *Parser) getAllGoFileInfo(searchDir string) error { + return filepath.Walk(searchDir, parser.visit) } -func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg, parseFlag ParseFlag) error { +func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg) error { ignoreInternal := pkg.Internal && !parser.ParseInternal if ignoreInternal || !pkg.Resolved { // ignored internal and not resolved dependencies return nil } - if pkg.Raw != nil && parser.skipPackageByPrefix(pkg.Raw.ImportPath) { - return nil // ignored by user-defined package path prefixes - } - // Skip cgo if pkg.Raw == nil && pkg.Name == "C" { return nil } - srcDir := pkg.Raw.Dir - - files, err := os.ReadDir(srcDir) // only parsing files in the dir(don't contain sub dir files) + files, err := ioutil.ReadDir(srcDir) // only parsing files in the dir(don't contains sub dir files) if err != nil { return err } @@ -1736,13 +1521,13 @@ func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg, parseFlag ParseFl } path := filepath.Join(srcDir, f.Name()) - if err := parser.parseFile(pkg.Name, path, nil, parseFlag); err != nil { + if err := parser.parseFile(path); err != nil { return err } } for i := 0; i < len(pkg.Deps); i++ { - if err := parser.getAllGoFileInfoFromDeps(&pkg.Deps[i], parseFlag); err != nil { + if err := parser.getAllGoFileInfoFromDeps(&pkg.Deps[i]); err != nil { return err } } @@ -1750,86 +1535,46 @@ func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg, parseFlag ParseFl return nil } -func (parser *Parser) parseFile(packageDir, path string, src interface{}, flag ParseFlag) error { - if strings.HasSuffix(strings.ToLower(path), "_test.go") || filepath.Ext(path) != ".go" { - return nil +func (parser *Parser) visit(path string, f os.FileInfo, err error) error { + if err := parser.Skip(path, f); err != nil { + return err } - - return parser.packages.ParseFile(packageDir, path, src, flag) + return parser.parseFile(path) } -func (parser *Parser) checkOperationIDUniqueness() error { - // operationsIds contains all operationId annotations to check it's unique - operationsIds := make(map[string]string) - - for path, item := range parser.swagger.Paths.Paths { - var method, id string - - for method = range allMethod { - op := refRouteMethodOp(&item, method) - if *op != nil { - id = (**op).ID - - break - } - } - - if id == "" { - continue - } - - current := fmt.Sprintf("%s %s", method, path) - - previous, ok := operationsIds[id] - if ok { - return fmt.Errorf( - "duplicated @id annotation '%s' found in '%s', previously declared in: '%s'", - id, current, previous) +func (parser *Parser) parseFile(path string) error { + if ext := filepath.Ext(path); ext == ".go" { + fset := token.NewFileSet() // positions are relative to fset + astFile, err := goparser.ParseFile(fset, path, nil, goparser.ParseComments) + if err != nil { + return fmt.Errorf("ParseFile error:%+v", err) } - operationsIds[id] = current + parser.files[path] = astFile } - return nil } -// Skip returns filepath.SkipDir error if match vendor and hidden folder. +// Skip returns filepath.SkipDir error if match vendor and hidden folder func (parser *Parser) Skip(path string, f os.FileInfo) error { - return walkWith(parser.excludes, parser.ParseVendor)(path, f) -} + if f.IsDir() { + if !parser.ParseVendor && f.Name() == "vendor" || //ignore "vendor" + f.Name() == "docs" || //exclude docs + len(f.Name()) > 1 && f.Name()[0] == '.' { // exclude all hidden folder + return filepath.SkipDir + } -func walkWith(excludes map[string]struct{}, parseVendor bool) func(path string, fileInfo os.FileInfo) error { - return func(path string, f os.FileInfo) error { - if f.IsDir() { - if !parseVendor && f.Name() == "vendor" || // ignore "vendor" - f.Name() == "docs" || // exclude docs - len(f.Name()) > 1 && f.Name()[0] == '.' && f.Name() != ".." { // exclude all hidden folder + if parser.excludes != nil { + if _, ok := parser.excludes[path]; ok { return filepath.SkipDir } - - if excludes != nil { - if _, ok := excludes[path]; ok { - return filepath.SkipDir - } - } } - - return nil } + + return nil } // GetSwagger returns *spec.Swagger which is the root document object for the API specification. func (parser *Parser) GetSwagger() *spec.Swagger { return parser.swagger } - -// addTestType just for tests. -func (parser *Parser) addTestType(typename string) { - typeDef := &TypeSpecDef{} - parser.packages.uniqueDefinitions[typename] = typeDef - parser.parsedSchemas[typeDef] = &Schema{ - PkgPath: "", - Name: typename, - Schema: PrimitiveSchema(OBJECT), - } -} diff --git a/vendor/github.com/swaggo/swag/property.go b/vendor/github.com/swaggo/swag/property.go new file mode 100644 index 0000000000..6d45632737 --- /dev/null +++ b/vendor/github.com/swaggo/swag/property.go @@ -0,0 +1,139 @@ +package swag + +import ( + "errors" + "fmt" + "go/ast" + "strings" +) + +// ErrFailedConvertPrimitiveType Failed to convert for swag to interpretable type +var ErrFailedConvertPrimitiveType = errors.New("swag property: failed convert primitive type") + +type propertyName struct { + SchemaType string + ArrayType string + CrossPkg string +} + +type propertyNewFunc func(schemeType string, crossPkg string) propertyName + +func newArrayProperty(schemeType string, crossPkg string) propertyName { + return propertyName{ + SchemaType: "array", + ArrayType: schemeType, + CrossPkg: crossPkg, + } +} + +func newProperty(schemeType string, crossPkg string) propertyName { + return propertyName{ + SchemaType: schemeType, + ArrayType: "string", + CrossPkg: crossPkg, + } +} + +func convertFromSpecificToPrimitive(typeName string) (string, error) { + typeName = strings.ToUpper(typeName) + switch typeName { + case "TIME", "OBJECTID", "UUID": + return "string", nil + case "DECIMAL": + return "number", nil + } + return "", ErrFailedConvertPrimitiveType +} + +func parseFieldSelectorExpr(astTypeSelectorExpr *ast.SelectorExpr, parser *Parser, propertyNewFunc propertyNewFunc) propertyName { + if primitiveType, err := convertFromSpecificToPrimitive(astTypeSelectorExpr.Sel.Name); err == nil { + return propertyNewFunc(primitiveType, "") + } + + if pkgName, ok := astTypeSelectorExpr.X.(*ast.Ident); ok { + if typeDefinitions, ok := parser.TypeDefinitions[pkgName.Name][astTypeSelectorExpr.Sel.Name]; ok { + if expr, ok := typeDefinitions.Type.(*ast.SelectorExpr); ok { + if primitiveType, err := convertFromSpecificToPrimitive(expr.Sel.Name); err == nil { + return propertyNewFunc(primitiveType, "") + } + } + parser.ParseDefinition(pkgName.Name, astTypeSelectorExpr.Sel.Name, typeDefinitions) + return propertyNewFunc(astTypeSelectorExpr.Sel.Name, pkgName.Name) + } + if aliasedNames, ok := parser.ImportAliases[pkgName.Name]; ok { + for aliasedName := range aliasedNames { + if typeDefinitions, ok := parser.TypeDefinitions[aliasedName][astTypeSelectorExpr.Sel.Name]; ok { + if expr, ok := typeDefinitions.Type.(*ast.SelectorExpr); ok { + if primitiveType, err := convertFromSpecificToPrimitive(expr.Sel.Name); err == nil { + return propertyNewFunc(primitiveType, "") + } + } + parser.ParseDefinition(aliasedName, astTypeSelectorExpr.Sel.Name, typeDefinitions) + return propertyNewFunc(astTypeSelectorExpr.Sel.Name, aliasedName) + } + } + } + name := fmt.Sprintf("%s.%v", pkgName, astTypeSelectorExpr.Sel.Name) + if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[name]; isCustomType { + return propertyName{SchemaType: actualPrimitiveType, ArrayType: actualPrimitiveType} + } + } + return propertyName{SchemaType: "string", ArrayType: "string"} +} + +// getPropertyName returns the string value for the given field if it exists +// allowedValues: array, boolean, integer, null, number, object, string +func getPropertyName(pkgName string, expr ast.Expr, parser *Parser) (propertyName, error) { + switch tp := expr.(type) { + case *ast.SelectorExpr: + return parseFieldSelectorExpr(tp, parser, newProperty), nil + case *ast.StarExpr: + return getPropertyName(pkgName, tp.X, parser) + case *ast.ArrayType: + return getArrayPropertyName(pkgName, tp.Elt, parser), nil + case *ast.MapType, *ast.StructType, *ast.InterfaceType: + return propertyName{SchemaType: "object", ArrayType: "object"}, nil + case *ast.FuncType: + return propertyName{SchemaType: "func", ArrayType: ""}, nil + case *ast.Ident: + name := tp.Name + // check if it is a custom type + if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[fullTypeName(pkgName, name)]; isCustomType { + return propertyName{SchemaType: actualPrimitiveType, ArrayType: actualPrimitiveType}, nil + } + + name = TransToValidSchemeType(name) + return propertyName{SchemaType: name, ArrayType: name}, nil + default: + return propertyName{}, errors.New("not supported" + fmt.Sprint(expr)) + } +} + +func getArrayPropertyName(pkgName string, astTypeArrayElt ast.Expr, parser *Parser) propertyName { + switch elt := astTypeArrayElt.(type) { + case *ast.StructType, *ast.MapType, *ast.InterfaceType: + return propertyName{SchemaType: "array", ArrayType: "object"} + case *ast.ArrayType: + return propertyName{SchemaType: "array", ArrayType: "array"} + case *ast.StarExpr: + return getArrayPropertyName(pkgName, elt.X, parser) + case *ast.SelectorExpr: + return parseFieldSelectorExpr(elt, parser, newArrayProperty) + case *ast.Ident: + name := elt.Name + if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[fullTypeName(pkgName, name)]; isCustomType { + name = actualPrimitiveType + } else { + name = TransToValidSchemeType(elt.Name) + } + return propertyName{SchemaType: "array", ArrayType: name} + default: + name := fmt.Sprintf("%s", astTypeArrayElt) + if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[fullTypeName(pkgName, name)]; isCustomType { + name = actualPrimitiveType + } else { + name = TransToValidSchemeType(name) + } + return propertyName{SchemaType: "array", ArrayType: name} + } +} diff --git a/vendor/github.com/swaggo/swag/schema.go b/vendor/github.com/swaggo/swag/schema.go index b3a5b38c16..539340fae9 100644 --- a/vendor/github.com/swaggo/swag/schema.go +++ b/vendor/github.com/swaggo/swag/schema.go @@ -1,101 +1,65 @@ package swag import ( - "errors" "fmt" - "github.com/go-openapi/spec" + "go/ast" + "strings" ) -const ( - // ARRAY represent a array value. - ARRAY = "array" - // OBJECT represent a object value. - OBJECT = "object" - // PRIMITIVE represent a primitive value. - PRIMITIVE = "primitive" - // BOOLEAN represent a boolean value. - BOOLEAN = "boolean" - // INTEGER represent a integer value. - INTEGER = "integer" - // NUMBER represent a number value. - NUMBER = "number" - // STRING represent a string value. - STRING = "string" - // FUNC represent a function value. - FUNC = "func" - // ERROR represent a error value. - ERROR = "error" - // INTERFACE represent a interface value. - INTERFACE = "interface{}" - // ANY represent a any value. - ANY = "any" - // NIL represent a empty value. - NIL = "nil" - - // IgnoreNameOverridePrefix Prepend to model to avoid renaming based on comment. - IgnoreNameOverridePrefix = '$' -) - -// CheckSchemaType checks if typeName is not a name of primitive type. +// CheckSchemaType checks if typeName is not a name of primitive type func CheckSchemaType(typeName string) error { if !IsPrimitiveType(typeName) { return fmt.Errorf("%s is not basic types", typeName) } - return nil } -// IsSimplePrimitiveType determine whether the type name is a simple primitive type. +// IsSimplePrimitiveType determine whether the type name is a simple primitive type func IsSimplePrimitiveType(typeName string) bool { switch typeName { - case STRING, NUMBER, INTEGER, BOOLEAN: + case "string", "number", "integer", "boolean": return true + default: + return false } - - return false } -// IsPrimitiveType determine whether the type name is a primitive type. +// IsPrimitiveType determine whether the type name is a primitive type func IsPrimitiveType(typeName string) bool { switch typeName { - case STRING, NUMBER, INTEGER, BOOLEAN, ARRAY, OBJECT, FUNC: + case "string", "number", "integer", "boolean", "array", "object", "func": return true + default: + return false } - - return false -} - -// IsInterfaceLike determines whether the swagger type name is an go named interface type like error type. -func IsInterfaceLike(typeName string) bool { - return typeName == ERROR || typeName == ANY } -// IsNumericType determines whether the swagger type name is a numeric type. +// IsNumericType determines whether the swagger type name is a numeric type func IsNumericType(typeName string) bool { - return typeName == INTEGER || typeName == NUMBER + return typeName == "integer" || typeName == "number" } // TransToValidSchemeType indicates type will transfer golang basic type to swagger supported type. func TransToValidSchemeType(typeName string) string { switch typeName { case "uint", "int", "uint8", "int8", "uint16", "int16", "byte": - return INTEGER + return "integer" case "uint32", "int32", "rune": - return INTEGER + return "integer" case "uint64", "int64": - return INTEGER + return "integer" case "float32", "float64": - return NUMBER + return "number" case "bool": - return BOOLEAN + return "boolean" case "string": - return STRING + return "string" + default: + return typeName // to support user defined types } - - return typeName } -// IsGolangPrimitiveType determine whether the type name is a golang primitive type. +// IsGolangPrimitiveType determine whether the type name is a golang primitive type func IsGolangPrimitiveType(typeName string) bool { switch typeName { case "uint", @@ -115,179 +79,39 @@ func IsGolangPrimitiveType(typeName string) bool { "bool", "string": return true + default: + return false } - - return false } -// TransToValidCollectionFormat determine valid collection format. +// TransToValidCollectionFormat determine valid collection format func TransToValidCollectionFormat(format string) string { switch format { case "csv", "multi", "pipes", "tsv", "ssv": return format + default: + return "" } - - return "" -} - -func ignoreNameOverride(name string) bool { - return len(name) != 0 && name[0] == IgnoreNameOverridePrefix -} - -// IsComplexSchema whether a schema is complex and should be a ref schema -func IsComplexSchema(schema *spec.Schema) bool { - // a enum type should be complex - if len(schema.Enum) > 0 { - return true - } - - // a deep array type is complex, how to determine deep? here more than 2 ,for example: [][]object,[][][]int - if len(schema.Type) > 2 { - return true - } - - //Object included, such as Object or []Object - for _, st := range schema.Type { - if st == OBJECT { - return true - } - } - return false -} - -// IsRefSchema whether a schema is a reference schema. -func IsRefSchema(schema *spec.Schema) bool { - return schema.Ref.Ref.GetURL() != nil -} - -// RefSchema build a reference schema. -func RefSchema(refType string) *spec.Schema { - return spec.RefSchema("#/definitions/" + refType) } -// PrimitiveSchema build a primitive schema. -func PrimitiveSchema(refType string) *spec.Schema { - return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{refType}}} -} - -// BuildCustomSchema build custom schema specified by tag swaggertype. -func BuildCustomSchema(types []string) (*spec.Schema, error) { - if len(types) == 0 { - return nil, nil - } - - switch types[0] { - case PRIMITIVE: - if len(types) == 1 { - return nil, errors.New("need primitive type after primitive") +// TypeDocName get alias from comment '// @name ', otherwise the original type name to display in doc +func TypeDocName(pkgName string, spec *ast.TypeSpec) string { + if spec != nil { + if spec.Comment != nil { + for _, comment := range spec.Comment.List { + text := strings.TrimSpace(comment.Text) + text = strings.TrimLeft(text, "//") + text = strings.TrimSpace(text) + texts := strings.Split(text, " ") + if len(texts) > 1 && strings.ToLower(texts[0]) == "@name" { + return texts[1] + } + } } - - return BuildCustomSchema(types[1:]) - case ARRAY: - if len(types) == 1 { - return nil, errors.New("need array item type after array") - } - - schema, err := BuildCustomSchema(types[1:]) - if err != nil { - return nil, err - } - - return spec.ArrayProperty(schema), nil - case OBJECT: - if len(types) == 1 { - return PrimitiveSchema(types[0]), nil - } - - schema, err := BuildCustomSchema(types[1:]) - if err != nil { - return nil, err - } - - return spec.MapProperty(schema), nil - default: - err := CheckSchemaType(types[0]) - if err != nil { - return nil, err + if spec.Name != nil { + return fullTypeName(strings.Split(pkgName, ".")[0], spec.Name.Name) } - - return PrimitiveSchema(types[0]), nil } -} -// MergeSchema merge schemas -func MergeSchema(dst *spec.Schema, src *spec.Schema) *spec.Schema { - if len(src.Type) > 0 { - dst.Type = src.Type - } - if len(src.Properties) > 0 { - dst.Properties = src.Properties - } - if src.Items != nil { - dst.Items = src.Items - } - if src.AdditionalProperties != nil { - dst.AdditionalProperties = src.AdditionalProperties - } - if len(src.Description) > 0 { - dst.Description = src.Description - } - if src.Nullable { - dst.Nullable = src.Nullable - } - if len(src.Format) > 0 { - dst.Format = src.Format - } - if src.Default != nil { - dst.Default = src.Default - } - if src.Example != nil { - dst.Example = src.Example - } - if len(src.Extensions) > 0 { - dst.Extensions = src.Extensions - } - if src.Maximum != nil { - dst.Maximum = src.Maximum - } - if src.Minimum != nil { - dst.Minimum = src.Minimum - } - if src.ExclusiveMaximum { - dst.ExclusiveMaximum = src.ExclusiveMaximum - } - if src.ExclusiveMinimum { - dst.ExclusiveMinimum = src.ExclusiveMinimum - } - if src.MaxLength != nil { - dst.MaxLength = src.MaxLength - } - if src.MinLength != nil { - dst.MinLength = src.MinLength - } - if len(src.Pattern) > 0 { - dst.Pattern = src.Pattern - } - if src.MaxItems != nil { - dst.MaxItems = src.MaxItems - } - if src.MinItems != nil { - dst.MinItems = src.MinItems - } - if src.UniqueItems { - dst.UniqueItems = src.UniqueItems - } - if src.MultipleOf != nil { - dst.MultipleOf = src.MultipleOf - } - if len(src.Enum) > 0 { - dst.Enum = src.Enum - } - if len(src.Extensions) > 0 { - dst.Extensions = src.Extensions - } - if len(src.ExtraProps) > 0 { - dst.ExtraProps = src.ExtraProps - } - return dst + return pkgName } diff --git a/vendor/github.com/swaggo/swag/spec.go b/vendor/github.com/swaggo/swag/spec.go deleted file mode 100644 index c18a365b0c..0000000000 --- a/vendor/github.com/swaggo/swag/spec.go +++ /dev/null @@ -1,64 +0,0 @@ -package swag - -import ( - "bytes" - "encoding/json" - "strings" - "text/template" -) - -// Spec holds exported Swagger Info so clients can modify it. -type Spec struct { - Version string - Host string - BasePath string - Schemes []string - Title string - Description string - InfoInstanceName string - SwaggerTemplate string - LeftDelim string - RightDelim string -} - -// ReadDoc parses SwaggerTemplate into swagger document. -func (i *Spec) ReadDoc() string { - i.Description = strings.ReplaceAll(i.Description, "\n", "\\n") - - tpl := template.New("swagger_info").Funcs(template.FuncMap{ - "marshal": func(v interface{}) string { - a, _ := json.Marshal(v) - - return string(a) - }, - "escape": func(v interface{}) string { - // escape tabs - var str = strings.ReplaceAll(v.(string), "\t", "\\t") - // replace " with \", and if that results in \\", replace that with \\\" - str = strings.ReplaceAll(str, "\"", "\\\"") - - return strings.ReplaceAll(str, "\\\\\"", "\\\\\\\"") - }, - }) - - if i.LeftDelim != "" && i.RightDelim != "" { - tpl = tpl.Delims(i.LeftDelim, i.RightDelim) - } - - parsed, err := tpl.Parse(i.SwaggerTemplate) - if err != nil { - return i.SwaggerTemplate - } - - var doc bytes.Buffer - if err = parsed.Execute(&doc, i); err != nil { - return i.SwaggerTemplate - } - - return doc.String() -} - -// InstanceName returns Spec instance name. -func (i *Spec) InstanceName() string { - return i.InfoInstanceName -} diff --git a/vendor/github.com/swaggo/swag/swagger.go b/vendor/github.com/swaggo/swag/swagger.go index 74c162c284..38c5ebfc60 100644 --- a/vendor/github.com/swaggo/swag/swagger.go +++ b/vendor/github.com/swaggo/swag/swagger.go @@ -2,7 +2,6 @@ package swag import ( "errors" - "fmt" "sync" ) @@ -11,10 +10,10 @@ const Name = "swagger" var ( swaggerMu sync.RWMutex - swags map[string]Swagger + swag Swagger ) -// Swagger is an interface to read swagger document. +// Swagger is a interface to read swagger document. type Swagger interface { ReadDoc() string } @@ -23,50 +22,20 @@ type Swagger interface { func Register(name string, swagger Swagger) { swaggerMu.Lock() defer swaggerMu.Unlock() - if swagger == nil { panic("swagger is nil") } - if swags == nil { - swags = make(map[string]Swagger) - } - - if _, ok := swags[name]; ok { + if swag != nil { panic("Register called twice for swag: " + name) } - - swags[name] = swagger -} - -// GetSwagger returns the swagger instance for given name. -// If not found, returns nil. -func GetSwagger(name string) Swagger { - swaggerMu.RLock() - defer swaggerMu.RUnlock() - - return swags[name] + swag = swagger } -// ReadDoc reads swagger document. An optional name parameter can be passed to read a specific document. -// The default name is "swagger". -func ReadDoc(optionalName ...string) (string, error) { - swaggerMu.RLock() - defer swaggerMu.RUnlock() - - if swags == nil { - return "", errors.New("no swag has yet been registered") - } - - name := Name - if len(optionalName) != 0 && optionalName[0] != "" { - name = optionalName[0] +// ReadDoc reads swagger document. +func ReadDoc() (string, error) { + if swag != nil { + return swag.ReadDoc(), nil } - - swag, ok := swags[name] - if !ok { - return "", fmt.Errorf("no swag named \"%s\" was registered", name) - } - - return swag.ReadDoc(), nil + return "", errors.New("not yet registered swag") } diff --git a/vendor/github.com/swaggo/swag/types.go b/vendor/github.com/swaggo/swag/types.go deleted file mode 100644 index 0076a6b40e..0000000000 --- a/vendor/github.com/swaggo/swag/types.go +++ /dev/null @@ -1,105 +0,0 @@ -package swag - -import ( - "go/ast" - "go/token" - "regexp" - "strings" - - "github.com/go-openapi/spec" -) - -// Schema parsed schema. -type Schema struct { - *spec.Schema // - PkgPath string // package import path used to rename Name of a definition int case of conflict - Name string // Name in definitions -} - -// TypeSpecDef the whole information of a typeSpec. -type TypeSpecDef struct { - // ast file where TypeSpec is - File *ast.File - - // the TypeSpec of this type definition - TypeSpec *ast.TypeSpec - - Enums []EnumValue - - // path of package starting from under ${GOPATH}/src or from module path in go.mod - PkgPath string - ParentSpec ast.Decl - - NotUnique bool -} - -// Name the name of the typeSpec. -func (t *TypeSpecDef) Name() string { - if t.TypeSpec != nil && t.TypeSpec.Name != nil { - return t.TypeSpec.Name.Name - } - - return "" -} - -// TypeName the type name of the typeSpec. -func (t *TypeSpecDef) TypeName() string { - if ignoreNameOverride(t.TypeSpec.Name.Name) { - return t.TypeSpec.Name.Name[1:] - } else if t.TypeSpec.Comment != nil { - // get alias from comment '// @name ' - const regexCaseInsensitive = "(?i)" - reTypeName, err := regexp.Compile(regexCaseInsensitive + `^@name\s+(\S+)`) - if err != nil { - panic(err) - } - for _, comment := range t.TypeSpec.Comment.List { - trimmedComment := strings.TrimSpace(strings.TrimLeft(comment.Text, "/")) - texts := reTypeName.FindStringSubmatch(trimmedComment) - if len(texts) > 1 { - return texts[1] - } - } - } - - var names []string - if t.NotUnique { - pkgPath := strings.Map(func(r rune) rune { - if r == '\\' || r == '/' || r == '.' { - return '_' - } - return r - }, t.PkgPath) - names = append(names, pkgPath) - } else if t.File != nil { - names = append(names, t.File.Name.Name) - } - if parentFun, ok := (t.ParentSpec).(*ast.FuncDecl); ok && parentFun != nil { - names = append(names, parentFun.Name.Name) - } - names = append(names, t.TypeSpec.Name.Name) - return fullTypeName(names...) -} - -// FullPath return the full path of the typeSpec. -func (t *TypeSpecDef) FullPath() string { - return t.PkgPath + "." + t.Name() -} - -// AstFileInfo information of an ast.File. -type AstFileInfo struct { - //FileSet the FileSet object which is used to parse this go source file - FileSet *token.FileSet - - // File ast.File - File *ast.File - - // Path the path of the ast.File - Path string - - // PackagePath package import path of the ast.File - PackagePath string - - // ParseFlag determine what to parse - ParseFlag ParseFlag -} diff --git a/vendor/github.com/swaggo/swag/utils.go b/vendor/github.com/swaggo/swag/utils.go deleted file mode 100644 index df31ff2e16..0000000000 --- a/vendor/github.com/swaggo/swag/utils.go +++ /dev/null @@ -1,55 +0,0 @@ -package swag - -import "unicode" - -// FieldsFunc split a string s by a func splitter into max n parts -func FieldsFunc(s string, f func(rune2 rune) bool, n int) []string { - // A span is used to record a slice of s of the form s[start:end]. - // The start index is inclusive and the end index is exclusive. - type span struct { - start int - end int - } - spans := make([]span, 0, 32) - - // Find the field start and end indices. - // Doing this in a separate pass (rather than slicing the string s - // and collecting the result substrings right away) is significantly - // more efficient, possibly due to cache effects. - start := -1 // valid span start if >= 0 - for end, rune := range s { - if f(rune) { - if start >= 0 { - spans = append(spans, span{start, end}) - // Set start to a negative value. - // Note: using -1 here consistently and reproducibly - // slows down this code by a several percent on amd64. - start = ^start - } - } else { - if start < 0 { - start = end - if n > 0 && len(spans)+1 >= n { - break - } - } - } - } - - // Last field might end at EOF. - if start >= 0 { - spans = append(spans, span{start, len(s)}) - } - - // Create strings from recorded field indices. - a := make([]string, len(spans)) - for i, span := range spans { - a[i] = s[span.start:span.end] - } - return a -} - -// FieldsByAnySpace split a string s by any space character into max n parts -func FieldsByAnySpace(s string, n int) []string { - return FieldsFunc(s, unicode.IsSpace, n) -} diff --git a/vendor/github.com/swaggo/swag/version.go b/vendor/github.com/swaggo/swag/version.go index ff2810e61d..d322f013e8 100644 --- a/vendor/github.com/swaggo/swag/version.go +++ b/vendor/github.com/swaggo/swag/version.go @@ -1,4 +1,4 @@ package swag -// Version of swag. -const Version = "v1.16.3" +// Version of swag +const Version = "v1.6.7" diff --git a/vendor/modules.txt b/vendor/modules.txt index ae5fad4862..a07f3a6ce1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -207,6 +207,9 @@ github.com/gabriel-vasile/mimetype github.com/gabriel-vasile/mimetype/internal/charset github.com/gabriel-vasile/mimetype/internal/json github.com/gabriel-vasile/mimetype/internal/magic +# github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 +## explicit +github.com/ghodss/yaml # github.com/github/gh-ost v1.1.3-0.20210727153850-e484824bbd68 ## explicit; go 1.16 github.com/github/gh-ost/go/base @@ -643,8 +646,8 @@ github.com/spf13/pflag # github.com/stretchr/testify v1.8.4 ## explicit; go 1.20 github.com/stretchr/testify/assert -# github.com/swaggo/swag v1.16.3 -## explicit; go 1.18 +# github.com/swaggo/swag v1.16.3 => github.com/swaggo/swag v1.6.7 +## explicit; go 1.13 github.com/swaggo/swag github.com/swaggo/swag/gen # github.com/tjfoc/gmsm v1.4.1 @@ -899,9 +902,6 @@ gorm.io/gorm/logger gorm.io/gorm/migrator gorm.io/gorm/schema gorm.io/gorm/utils -# sigs.k8s.io/yaml v1.3.0 -## explicit; go 1.12 -sigs.k8s.io/yaml # vitess.io/vitess v0.12.0 ## explicit; go 1.17 vitess.io/vitess/go/bytes2 @@ -920,4 +920,5 @@ vitess.io/vitess/go/vt/vtgate/evalengine # github.com/labstack/echo/v4 => github.com/labstack/echo/v4 v4.6.1 # github.com/pingcap/log => github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9 # github.com/pingcap/parser => github.com/sjjian/parser v0.0.0-20240305095250-688ad439ef31 +# github.com/swaggo/swag => github.com/swaggo/swag v1.6.7 # google.golang.org/grpc => google.golang.org/grpc v1.29.0 diff --git a/vendor/sigs.k8s.io/yaml/.travis.yml b/vendor/sigs.k8s.io/yaml/.travis.yml deleted file mode 100644 index 54ed8f9cb9..0000000000 --- a/vendor/sigs.k8s.io/yaml/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go -arch: arm64 -dist: focal -go: 1.15.x -script: - - diff -u <(echo -n) <(gofmt -d *.go) - - diff -u <(echo -n) <(golint $(go list -e ./...) | grep -v YAMLToJSON) - - GO111MODULE=on go vet . - - GO111MODULE=on go test -v -race ./... - - git diff --exit-code -install: - - GO111MODULE=off go get golang.org/x/lint/golint diff --git a/vendor/sigs.k8s.io/yaml/CONTRIBUTING.md b/vendor/sigs.k8s.io/yaml/CONTRIBUTING.md deleted file mode 100644 index de47115137..0000000000 --- a/vendor/sigs.k8s.io/yaml/CONTRIBUTING.md +++ /dev/null @@ -1,31 +0,0 @@ -# Contributing Guidelines - -Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: - -_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ - -## Getting Started - -We have full documentation on how to get started contributing here: - - - -- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests -- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing) -- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers - -## Mentorship - -- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! - - diff --git a/vendor/sigs.k8s.io/yaml/OWNERS b/vendor/sigs.k8s.io/yaml/OWNERS deleted file mode 100644 index 325b40b076..0000000000 --- a/vendor/sigs.k8s.io/yaml/OWNERS +++ /dev/null @@ -1,27 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners - -approvers: -- dims -- lavalamp -- smarterclayton -- deads2k -- sttts -- liggitt -- caesarxuchao -reviewers: -- dims -- thockin -- lavalamp -- smarterclayton -- wojtek-t -- deads2k -- derekwaynecarr -- caesarxuchao -- mikedanese -- liggitt -- gmarek -- sttts -- ncdc -- tallclair -labels: -- sig/api-machinery diff --git a/vendor/sigs.k8s.io/yaml/RELEASE.md b/vendor/sigs.k8s.io/yaml/RELEASE.md deleted file mode 100644 index 6b642464e5..0000000000 --- a/vendor/sigs.k8s.io/yaml/RELEASE.md +++ /dev/null @@ -1,9 +0,0 @@ -# Release Process - -The `yaml` Project is released on an as-needed basis. The process is as follows: - -1. An issue is proposing a new release with a changelog since the last release -1. All [OWNERS](OWNERS) must LGTM this release -1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION` -1. The release issue is closed -1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released` diff --git a/vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS b/vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS deleted file mode 100644 index 0648a8ebff..0000000000 --- a/vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS +++ /dev/null @@ -1,17 +0,0 @@ -# Defined below are the security contacts for this repo. -# -# They are the contact point for the Product Security Team to reach out -# to for triaging and handling of incoming issues. -# -# The below names agree to abide by the -# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) -# and will be removed and replaced if they violate that agreement. -# -# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE -# INSTRUCTIONS AT https://kubernetes.io/security/ - -cjcullen -jessfraz -liggitt -philips -tallclair diff --git a/vendor/sigs.k8s.io/yaml/code-of-conduct.md b/vendor/sigs.k8s.io/yaml/code-of-conduct.md deleted file mode 100644 index 0d15c00cf3..0000000000 --- a/vendor/sigs.k8s.io/yaml/code-of-conduct.md +++ /dev/null @@ -1,3 +0,0 @@ -# Kubernetes Community Code of Conduct - -Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)