diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7dad15e83..995e23575 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: test: strategy: matrix: - go: [ '1.18.x', '1.19.x', '1.20.x' ] + go: [ '1.19.x', '1.20.x', '1.21.x', '1.22.x' ] platform: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: @@ -22,8 +22,7 @@ jobs: - name: deps run: make deps - name: static program analysis - run: | - make fmt-check lint vet + run: make fmt-check vet - name: build run: make build - name: test diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a7e77a6cc..9be444b25 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,19 +5,23 @@ on: tags: - 'v*' +permissions: + contents: read + packages: write + jobs: docker-build: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to Github Packages - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -25,18 +29,20 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ghcr.io/swaggo/swag - name: Build image and push to GitHub Container Registry - uses: docker/build-push-action@v2 + id: docker_build + uses: docker/build-push-action@v5 with: context: . + platforms: linux/amd64,linux/arm64 push: true tags: | - ghcr.io/swaggo/swag:latest - ghcr.io/swaggo/swag:${{github.ref_name}} + ghcr.io/${{ github.repository }}:latest + ghcr.io/${{ github.repository }}:${{github.ref_name}} labels: ${{ steps.meta.outputs.labels }} - name: Image digest diff --git a/Dockerfile b/Dockerfile index 170d0c699..5ea913434 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # Dockerfile References: https://docs.docker.com/engine/reference/builder/ # Start from the latest golang base image -FROM golang:1.18.3-alpine as builder +FROM --platform=$BUILDPLATFORM golang:1.21-alpine as builder # Set the Current Working Directory inside the container WORKDIR /app @@ -15,14 +15,22 @@ RUN go mod download # Copy the source from the current directory to the Working Directory inside the container COPY . . +# Configure go compiler target platform +ARG TARGETOS +ARG TARGETARCH +ENV GOARCH=$TARGETARCH \ + GOOS=$TARGETOS + # Build the Go app RUN CGO_ENABLED=0 GOOS=linux go build -v -a -installsuffix cgo -o swag cmd/swag/main.go ######## Start a new stage from scratch ####### -FROM scratch +FROM --platform=$TARGETPLATFORM scratch -WORKDIR /root/ +WORKDIR /code/ # Copy the Pre-built binary file from the previous stage -COPY --from=builder /app/swag . +COPY --from=builder /app/swag /bin/swag + +ENTRYPOINT ["/bin/swag"] diff --git a/Makefile b/Makefile index 089c65be6..126c75890 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ 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 @@ -16,8 +17,6 @@ BINARY_NAME:=swag PACKAGES:=$(shell $(GOLIST) github.com/swaggo/swag/v2 github.com/swaggo/swag/v2/cmd/swag github.com/swaggo/swag/v2/gen github.com/swaggo/swag/v2/format) GOFILES:=$(shell find . -name "*.go" -type f) -export GO111MODULE := on - all: test build .PHONY: build @@ -54,25 +53,10 @@ clean: .PHONY: deps deps: - $(GOGET) github.com/swaggo/cli - $(GOGET) sigs.k8s.io/yaml - $(GOGET) github.com/KyleBanks/depth - $(GOGET) github.com/go-openapi/jsonreference - $(GOGET) github.com/go-openapi/spec - $(GOGET) github.com/stretchr/testify/assert - $(GOGET) golang.org/x/tools/go/loader - -.PHONY: devel-deps -devel-deps: - GO111MODULE=off $(GOGET) -v -u \ - golang.org/x/lint/golint - -.PHONY: lint -lint: devel-deps - for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; + $(GOMODTIDY) .PHONY: vet -vet: deps devel-deps +vet: deps $(GOVET) $(PACKAGES) .PHONY: fmt diff --git a/README.md b/README.md index f8f0640fa..1cd185b48 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie - [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 headers in request](#add-request-headers) + - [Add headers in response](#add-a-headers-in-response) - [Use multiple path params](#use-multiple-path-params) - [Add multiple paths](#add-multiple-paths) - [Example value of struct](#example-value-of-struct) @@ -56,6 +57,7 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie - [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) - [Change the default Go Template action delimiters](#change-the-default-go-template-action-delimiters) - [About the Project](#about-the-project) - [Contributors](#contributors) @@ -67,11 +69,16 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie 1. Add comments to your API source code, See [Declarative Comments Format](#declarative-comments-format). -2. Download swag by using: +2. Install swag by using: ```sh go install github.com/swaggo/swag/v2/cmd/swag@latest ``` -To build from source you need [Go](https://golang.org/dl/) (1.18 or newer). +To build from source you need [Go](https://golang.org/dl/) (1.19 or newer). + +Alternatively you can run the docker image: +```sh +docker run --rm -v $(pwd):/code ghcr.io/swaggo/swag:latest +``` Or download a pre-compiled binary from the [release page](https://github.com/swaggo/swag/releases). @@ -81,6 +88,9 @@ 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 ``` @@ -112,6 +122,7 @@ OPTIONS: --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) + --parseDependencyLevel, --pdl Enhancement of '--parseDependency', parse go files inside dependency folder, 0 disabled, 1 only parse models, 2 only parse operations, 3 parse all (default: 0) --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) @@ -125,6 +136,9 @@ OPTIONS: --tags value, -t value A comma-separated list of tags to filter the APIs for which the documentation is generated.Special case if the tag is prefixed with the '!' character then the APIs with that tag will be excluded --v3.1 Generate OpenAPI V3.1 spec (default: false) --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 + --parseFuncBody Parse API info within body of functions in go files, disabled by default (default: false) --packageName --output A package name of docs.go, using output directory name by default (check --output option) --collectionFormat value, --cf value Set default collection format (default: "csv") --help, -h show help @@ -163,6 +177,7 @@ OPTIONS: 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 @@ -443,25 +458,26 @@ The following annotations are only available if you set the -v3.1 flag in the CL [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]` | -| 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. 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. | @@ -663,7 +679,14 @@ type DeepObject struct { //in `proto` package } @success 200 {object} jsonresult.JSONResult{data1=proto.Order{data=proto.DeepObject},data2=[]proto.Order{data=[]proto.DeepObject}} "desc" ``` -### Add a headers in response +### Add response request + +```go +// @Param X-MyHeader header string true "MyHeader must be set for valid response" +// @Param X-API-VERSION header string true "API version eg.: 1.0" +``` + +### Add response headers ```go // @Success 200 {string} string "ok" @@ -939,6 +962,19 @@ By default `swag` command generates Swagger specification in three different fil 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) @@ -951,6 +987,17 @@ swag init -g http/api.go -td "[[,]]" ``` The new delimiter is a string with the format "``,``". +### Parse Internal and Dependency Packages + +If the struct is defined in a dependency package, use `--parseDependency`. + +If the struct is defined in your main project, use `--parseInternal`. + +if you want to include both internal and from dependencies use both flags +``` +swag init --parseDependency --parseInternal +``` + ## 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/README_pt.md b/README_pt.md index 16f021e49..060b2c970 100644 --- a/README_pt.md +++ b/README_pt.md @@ -43,6 +43,7 @@ Swag converte anotações Go para Documentação Swagger 2.0. Criámos uma varie - [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 @@ -53,7 +54,7 @@ Swag converte anotações Go para Documentação Swagger 2.0. Criámos uma varie ```sh go install github.com/swaggo/swag/cmd/swag@latest ``` -Para construir a partir da fonte é necessário [Go](https://golang.org/dl/) (1.17 ou mais recente). +Para construir a partir da fonte é necessário [Go](https://golang.org/dl/) (1.19 ou mais recente). Ou descarregar um binário pré-compilado a partir da [página de lançamento](https://github.com/swaggo/swag/releases). @@ -95,7 +96,7 @@ OPÇÕES: --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) @@ -418,7 +419,7 @@ Quando uma pequena sequência na sua documentação é insuficiente, ou precisa | 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` |. +| 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. | @@ -905,6 +906,30 @@ Por defeito, o comando `swag` gera especificação Swagger em três tipos difere 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). diff --git a/README_zh-CN.md b/README_zh-CN.md index fc5c4e188..24a219c9f 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -50,7 +50,7 @@ Swag将Go的注释转换为Swagger2.0文档。我们为流行的 [Go Web Framewo go install github.com/swaggo/swag/cmd/swag@latest ``` -从源码开始构建的话,需要有Go环境(1.18及以上版本)。 +从源码开始构建的话,需要有Go环境(1.19及以上版本)。 或者从github的release页面下载预编译好的二进制文件。 @@ -90,6 +90,7 @@ OPTIONS: --output value, -o value 文件(swagger.json, swagger.yaml and doc.go)输出目录 (默认: "./docs") --parseVendor 是否解析vendor目录里的go源文件,默认不 --parseDependency 是否解析依赖目录中的go源文件,默认不 + --parseDependencyLevel, --pdl 对'--parseDependency'参数进行增强, 是否解析依赖目录中的go源文件, 0 不解析, 1 只解析对象模型, 2 只解析API, 3 对象模型和API都解析 (default: 0) --markdownFiles value, --md value 指定API的描述信息所使用的markdown文件所在的目录 --generatedTime 是否输出时间到输出文件docs.go的顶部,默认是 --codeExampleFiles value, --cef value 解析包含用于 x-codeSamples 扩展的代码示例文件的文件夹,默认禁用 @@ -378,23 +379,25 @@ 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`的文件中读取。 | +| id | 用于标识操作的唯一字符串。在所有API操作中必须唯一。 | +| tags | 每个API操作的标签列表,以逗号分隔。 | | summary | 该操作的简短摘要。 | -| accept | API 可以使用的 MIME 类型列表。 请注意,Accept 仅影响具有请求正文的操作,例如 POST、PUT 和 PATCH。 值必须如“[Mime类型](#mime类型)”中所述。 | -| produce | API可以生成的MIME类型的列表。值必须如“[Mime类型](#mime类型)”中所述。 | +| accept | API 可以使用的 MIME 类型列表。 请注意,Accept 仅影响具有请求正文的操作,例如 POST、PUT 和 PATCH。 值必须如“[Mime类型](#mime类型)”中所述。 | +| produce | API可以生成的MIME类型的列表。值必须如“[Mime类型](#mime类型)”中所述。 | | 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]` | -| x-name | 扩展字段必须以`x-`开头,并且只能使用json值。 | +| 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 | ## Mime类型 diff --git a/cmd/swag/main.go b/cmd/swag/main.go index 57a2f460f..8480c4ec8 100644 --- a/cmd/swag/main.go +++ b/cmd/swag/main.go @@ -15,30 +15,35 @@ import ( ) const ( - searchDirFlag = "dir" - excludeFlag = "exclude" - generalInfoFlag = "generalInfo" - propertyStrategyFlag = "propertyStrategy" - outputFlag = "output" - outputTypesFlag = "outputTypes" - parseVendorFlag = "parseVendor" - parseDependencyFlag = "parseDependency" - markdownFilesFlag = "markdownFiles" - codeExampleFilesFlag = "codeExampleFiles" - parseInternalFlag = "parseInternal" - generatedTimeFlag = "generatedTime" - requiredByDefaultFlag = "requiredByDefault" - parseDepthFlag = "parseDepth" - instanceNameFlag = "instanceName" - overridesFileFlag = "overridesFile" - parseGoListFlag = "parseGoList" - quietFlag = "quiet" - tagsFlag = "tags" - parseExtensionFlag = "parseExtension" - templateDelimsFlag = "templateDelims" - openAPIVersionFlag = "v3.1" - packageName = "packageName" - collectionFormatFlag = "collectionFormat" + searchDirFlag = "dir" + excludeFlag = "exclude" + generalInfoFlag = "generalInfo" + pipeFlag = "pipe" + propertyStrategyFlag = "propertyStrategy" + outputFlag = "output" + outputTypesFlag = "outputTypes" + parseVendorFlag = "parseVendor" + parseDependencyFlag = "parseDependency" + parseDependencyLevelFlag = "parseDependencyLevel" + markdownFilesFlag = "markdownFiles" + codeExampleFilesFlag = "codeExampleFiles" + parseInternalFlag = "parseInternal" + generatedTimeFlag = "generatedTime" + requiredByDefaultFlag = "requiredByDefault" + parseDepthFlag = "parseDepth" + instanceNameFlag = "instanceName" + overridesFileFlag = "overridesFile" + parseGoListFlag = "parseGoList" + quietFlag = "quiet" + tagsFlag = "tags" + parseExtensionFlag = "parseExtension" + templateDelimsFlag = "templateDelims" + openAPIVersionFlag = "v3.1" + packageName = "packageName" + collectionFormatFlag = "collectionFormat" + packagePrefixFlag = "packagePrefix" + stateFlag = "state" + parseFuncBodyFlag = "parseFuncBody" ) var initFlags = []cli.Flag{ @@ -85,6 +90,11 @@ var initFlags = []cli.Flag{ Name: parseVendorFlag, Usage: "Parse go files in 'vendor' folder, disabled by default", }, + &cli.IntFlag{ + Name: parseDependencyLevelFlag, + Aliases: []string{"pdl"}, + Usage: "Parse go files inside dependency folder, 0 disabled, 1 only parse models, 2 only parse operations, 3 parse all", + }, &cli.BoolFlag{ Name: parseDependencyFlag, Aliases: []string{"pd"}, @@ -156,6 +166,12 @@ var initFlags = []cli.Flag{ Value: "", Usage: "Provide custom delimeters for Go template generation. The format is leftDelim,rightDelim. For example: \"[[,]]\"", }, + &cli.StringFlag{ + Name: templateDelimsFlag, + Aliases: []string{"td"}, + Value: "", + Usage: "Provide custom delimiters for Go template generation. The format is leftDelim,rightDelim. For example: \"[[,]]\"", + }, &cli.StringFlag{ Name: packageName, Value: "", @@ -167,6 +183,21 @@ var initFlags = []cli.Flag{ Value: "csv", Usage: "Set default collection format", }, + &cli.StringFlag{ + Name: packagePrefixFlag, + Value: "", + Usage: "Parse only packages whose import path match the given prefix, comma separated", + }, + &cli.StringFlag{ + Name: stateFlag, + Value: "", + Usage: "Set host state for swagger.json", + }, + &cli.BoolFlag{ + Name: parseFuncBodyFlag, + // Value: false, + Usage: "Parse API info within body of functions in go files, disabled by default (default: false)", + }, } func initAction(ctx *cli.Context) error { @@ -183,11 +214,17 @@ func initAction(ctx *cli.Context) error { if ctx.IsSet(templateDelimsFlag) { delims := strings.Split(ctx.String(templateDelimsFlag), ",") if len(delims) != 2 { - return fmt.Errorf("exactly two template delimeters must be provided, comma separated") + return fmt.Errorf( + "exactly two template delimiters must be provided, comma separated", + ) } else if delims[0] == delims[1] { return fmt.Errorf("template delimiters must be different") } - leftDelim, rightDelim = strings.TrimSpace(delims[0]), strings.TrimSpace(delims[1]) + leftDelim, rightDelim = strings.TrimSpace( + delims[0], + ), strings.TrimSpace( + delims[1], + ) } outputTypes := strings.Split(ctx.String(outputTypesFlag), ",") @@ -199,11 +236,22 @@ func initAction(ctx *cli.Context) error { logger = log.New(io.Discard, "", log.LstdFlags) } - collectionFormat := swag.TransToValidCollectionFormat(ctx.String(collectionFormatFlag)) + collectionFormat := swag.TransToValidCollectionFormat( + ctx.String(collectionFormatFlag), + ) if collectionFormat == "" { - return fmt.Errorf("not supported %s collectionFormat", ctx.String(collectionFormat)) + return fmt.Errorf( + "not supported %s collectionFormat", + ctx.String(collectionFormat), + ) } + var pdv = ctx.Int(parseDependencyLevelFlag) + if pdv == 0 { + if ctx.Bool(parseDependencyFlag) { + pdv = 1 + } + } return gen.New().Build(&gen.Config{ SearchDir: ctx.String(searchDirFlag), Excludes: ctx.String(excludeFlag), @@ -213,7 +261,7 @@ func initAction(ctx *cli.Context) error { OutputDir: ctx.String(outputFlag), OutputTypes: outputTypes, ParseVendor: ctx.Bool(parseVendorFlag), - ParseDependency: ctx.Bool(parseDependencyFlag), + ParseDependency: pdv, MarkdownFilesDir: ctx.String(markdownFilesFlag), ParseInternal: ctx.Bool(parseInternalFlag), GeneratedTime: ctx.Bool(generatedTimeFlag), @@ -230,6 +278,9 @@ func initAction(ctx *cli.Context) error { Debugger: logger, GenerateOpenAPI3Doc: ctx.Bool(openAPIVersionFlag), CollectionFormat: collectionFormat, + PackagePrefix: ctx.String(packagePrefixFlag), + State: ctx.String(stateFlag), + ParseFuncBody: ctx.Bool(parseFuncBodyFlag), }) } @@ -251,6 +302,11 @@ func main() { Aliases: []string{"f"}, Usage: "format swag comments", Action: func(c *cli.Context) error { + + if c.Bool(pipeFlag) { + return format.New().Run(os.Stdin, os.Stdout) + } + searchDir := c.String(searchDirFlag) excludeDir := c.String(excludeFlag) mainFile := c.String(generalInfoFlag) @@ -278,6 +334,12 @@ func main() { Value: "main.go", Usage: "Go file path in which 'swagger general API Info' is written", }, + &cli.BoolFlag{ + Name: "pipe", + Aliases: []string{"p"}, + Value: false, + Usage: "Read from stdin, write to stdout.", + }, }, }, } diff --git a/const.go b/const.go index 1353b6d1d..83755103b 100644 --- a/const.go +++ b/const.go @@ -6,6 +6,7 @@ import ( "reflect" "strconv" "strings" + "unicode/utf8" ) // ConstVariable a model to record a const variable @@ -60,7 +61,7 @@ func EvaluateEscapedString(text string) string { i++ char, err := strconv.ParseInt(text[i:i+4], 16, 32) if err == nil { - result = AppendUtf8Rune(result, rune(char)) + result = utf8.AppendRune(result, rune(char)) } i += 3 } else if c, ok := escapedChars[text[i]]; ok { @@ -404,7 +405,7 @@ func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interfa 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 := CanIntegerValue{reflect.ValueOf(y)} + yValue := reflect.ValueOf(y) if yValue.CanUint() { rightOperand = yValue.Uint() } else if yValue.CanInt() { @@ -467,8 +468,8 @@ func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Exp evalType = ytype } - xValue := CanIntegerValue{reflect.ValueOf(x)} - yValue := CanIntegerValue{reflect.ValueOf(y)} + xValue := reflect.ValueOf(x) + yValue := reflect.ValueOf(y) if xValue.Kind() == reflect.String && yValue.Kind() == reflect.String { return xValue.String() + yValue.String(), evalType } diff --git a/enums_test.go b/enums_test.go index f730dd647..c7b6f36b5 100644 --- a/enums_test.go +++ b/enums_test.go @@ -1,29 +1,38 @@ package swag import ( + "encoding/json" + "math/bits" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestParseGlobalEnums(t *testing.T) { searchDir := "testdata/enums" + expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) + assert.NoError(t, err) p := New() - err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) - require.NoError(t, err) - - const constsPath = "github.com/swaggo/swag/v2/testdata/enums/consts" - table := p.packages.packages[constsPath].ConstTable - require.NotNil(t, table, "const table must not be nil") - - assert.Equal(t, 64, table["uintSize"].Value) - assert.Equal(t, int32(62), table["maxBase"].Value) - assert.Equal(t, 8, table["shlByLen"].Value) - assert.Equal(t, 255, table["hexnum"].Value) - assert.Equal(t, 15, table["octnum"].Value) - assert.Equal(t, `aa\nbb\u8888cc`, table["nonescapestr"].Value) - assert.Equal(t, "aa\nbb\u8888cc", table["escapestr"].Value) - assert.Equal(t, '\u8888', table["escapechar"].Value) + err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) + assert.NoError(t, err) + + b, err := json.MarshalIndent(p.swagger, "", " ") + assert.NoError(t, err) + assert.Equal(t, string(expected), string(b)) + + constsPath := "github.com/swaggo/swag/v2/testdata/enums/consts" + + assert.Equal(t, bits.UintSize, p.packages.packages[constsPath].ConstTable["uintSize"].Value) + assert.Equal(t, int32(62), p.packages.packages[constsPath].ConstTable["maxBase"].Value) + assert.Equal(t, 8, p.packages.packages[constsPath].ConstTable["shlByLen"].Value) + assert.Equal(t, 255, p.packages.packages[constsPath].ConstTable["hexnum"].Value) + assert.Equal(t, 15, p.packages.packages[constsPath].ConstTable["octnum"].Value) + assert.Equal(t, `aa\nbb\u8888cc`, p.packages.packages[constsPath].ConstTable["nonescapestr"].Value) + assert.Equal(t, "aa\nbb\u8888cc", p.packages.packages[constsPath].ConstTable["escapestr"].Value) + assert.Equal(t, 1_000_000, p.packages.packages[constsPath].ConstTable["underscored"].Value) + assert.Equal(t, 0b10001000, p.packages.packages[constsPath].ConstTable["binaryInteger"].Value) + } diff --git a/example/celler/docs/docs.go b/example/celler/docs/docs.go index 31cc65906..e5c794d07 100644 --- a/example/celler/docs/docs.go +++ b/example/celler/docs/docs.go @@ -997,7 +997,7 @@ const docTemplate = `{ "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information" + "admin": "Grants read and write access to administrative information" } }, "OAuth2Application": { @@ -1005,8 +1005,8 @@ const docTemplate = `{ "flow": "application", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Implicit": { @@ -1014,8 +1014,8 @@ const docTemplate = `{ "flow": "implicit", "authorizationUrl": "https://example.com/oauth/authorize", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Password": { @@ -1023,9 +1023,9 @@ const docTemplate = `{ "flow": "password", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "read": " Grants read access", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "read": "Grants read access", + "write": "Grants write access" } } } diff --git a/example/celler/go.mod b/example/celler/go.mod index 401c72846..720ab65b9 100644 --- a/example/celler/go.mod +++ b/example/celler/go.mod @@ -1,9 +1,9 @@ module github.com/swaggo/swag/example/celler -go 1.17 +go 1.18 require ( - github.com/gin-gonic/gin v1.7.7 + github.com/gin-gonic/gin v1.9.1 github.com/gofrs/uuid v4.2.0+incompatible github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 github.com/swaggo/gin-swagger v1.4.2 @@ -14,27 +14,36 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // 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/swag v0.19.15 // indirect - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/go-playground/validator/v10 v10.4.1 // indirect - github.com/golang/protobuf v1.3.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.9 // indirect - github.com/leodido/go-urn v1.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect - github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect - github.com/ugorji/go/codec v1.1.7 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/example/celler/go.sum b/example/celler/go.sum index 95318051d..ba415cc55 100644 --- a/example/celler/go.sum +++ b/example/celler/go.sum @@ -7,19 +7,28 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k= github.com/gin-contrib/gzip v0.0.3/go.mod h1:YxxswVZIqOvcHEQpsSn+QF5guQtO1dCfy0shBPy4jFc= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 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= @@ -30,43 +39,56 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= @@ -75,6 +97,8 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -82,11 +106,18 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM= github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/gin-swagger v1.4.2 h1:qDs1YrBOTnurDG/JVMc8678KhoS1B1okQGPtIqVz4YU= @@ -94,36 +125,34 @@ github.com/swaggo/gin-swagger v1.4.2/go.mod h1:hmJ1vPn+XjUvnbzjCdUAxVqgraxELxk8x github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU= github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI= github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -133,29 +162,30 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= @@ -166,5 +196,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/example/go-module-support/go.mod b/example/go-module-support/go.mod index a61d05362..49a3efe2c 100644 --- a/example/go-module-support/go.mod +++ b/example/go-module-support/go.mod @@ -1,9 +1,9 @@ module github.com/swaggo/swag/example/go-module-support -go 1.17 +go 1.18 require ( - github.com/gin-gonic/gin v1.7.7 + github.com/gin-gonic/gin v1.9.1 github.com/swaggo/examples v0.0.0-20190624100559-f57286ab550c github.com/swaggo/swag v1.8.1 ) @@ -12,27 +12,36 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // 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/swag v0.19.15 // indirect - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/go-playground/validator/v10 v10.4.1 // indirect - github.com/golang/protobuf v1.3.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.9 // indirect - github.com/leodido/go-urn v1.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/ugorji/go/codec v1.1.7 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.1.12 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/example/go-module-support/go.sum b/example/go-module-support/go.sum index 712b1ee6a..13269ece5 100644 --- a/example/go-module-support/go.sum +++ b/example/go-module-support/go.sum @@ -1,4 +1,3 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -6,17 +5,22 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= -github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= -github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 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= @@ -27,135 +31,104 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= -github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/examples v0.0.0-20190624100559-f57286ab550c h1:wgBp6VweQ9dML4cKbjh0sV0xxvxgqCrCRiHG6losLv4= github.com/swaggo/examples v0.0.0-20190624100559-f57286ab550c/go.mod h1:U21M3+8BIXRyR/pwjJ7X0D36sVTzFMiOyUAdgvVfUVI= github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI= github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/example/markdown/go.mod b/example/markdown/go.mod index d59492431..991bb2228 100644 --- a/example/markdown/go.mod +++ b/example/markdown/go.mod @@ -1,6 +1,6 @@ module github.com/swaggo/swag/example/markdown -go 1.17 +go 1.18 require ( github.com/gorilla/mux v1.8.0 @@ -17,8 +17,8 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/tools v0.1.10 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/example/markdown/go.sum b/example/markdown/go.sum index cb3034a03..07d4866a0 100644 --- a/example/markdown/go.sum +++ b/example/markdown/go.sum @@ -69,19 +69,18 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -89,15 +88,14 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/example/object-map-example/go.mod b/example/object-map-example/go.mod index 57995e45f..86d58ad0c 100644 --- a/example/object-map-example/go.mod +++ b/example/object-map-example/go.mod @@ -1,9 +1,9 @@ module github.com/swaggo/swag/example/object-map-example -go 1.17 +go 1.18 require ( - github.com/gin-gonic/gin v1.7.7 + github.com/gin-gonic/gin v1.9.1 github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 github.com/swaggo/gin-swagger v1.4.2 github.com/swaggo/swag v1.8.1 @@ -11,26 +11,31 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/spec v0.20.5 // indirect github.com/go-openapi/swag v0.19.15 // indirect - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/go-playground/validator/v10 v10.4.1 // indirect - github.com/golang/protobuf v1.3.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.9 // indirect - github.com/leodido/go-urn v1.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/ugorji/go/codec v1.1.13 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/example/object-map-example/go.sum b/example/object-map-example/go.sum index bac00b11c..9a7f482e9 100644 --- a/example/object-map-example/go.sum +++ b/example/object-map-example/go.sum @@ -5,19 +5,28 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k= github.com/gin-contrib/gzip v0.0.3/go.mod h1:YxxswVZIqOvcHEQpsSn+QF5guQtO1dCfy0shBPy4jFc= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 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= @@ -30,43 +39,54 @@ github.com/go-openapi/spec v0.20.5/go.mod h1:QbfOSIVt3/sac+a1wzmKbbcLXm5NdZnyBZY github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= @@ -75,6 +95,8 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -82,11 +104,18 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM= github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/gin-swagger v1.4.2 h1:qDs1YrBOTnurDG/JVMc8678KhoS1B1okQGPtIqVz4YU= @@ -94,38 +123,39 @@ github.com/swaggo/gin-swagger v1.4.2/go.mod h1:hmJ1vPn+XjUvnbzjCdUAxVqgraxELxk8x github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU= github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI= github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q= github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -135,19 +165,19 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -157,6 +187,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= @@ -167,5 +199,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/field_parser.go b/field_parser.go index 988d09105..5b8761fb2 100644 --- a/field_parser.go +++ b/field_parser.go @@ -69,44 +69,59 @@ func (ps *tagBaseFieldParser) ShouldSkip() bool { return false } -func (ps *tagBaseFieldParser) FieldName() (string, error) { - var name string +func (ps *tagBaseFieldParser) FieldNames() ([]string, error) { + if len(ps.field.Names) <= 1 { + // if embedded but with a json/form name ?? + if ps.field.Tag != nil { + // json:"tag,hoge" + name := strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0]) + if name != "" { + return []string{name}, nil + } - 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 []string{name}, nil + } } - - // use "form" tag over json tag - name = ps.FormName() - if name != "" { - return name, nil + if len(ps.field.Names) == 0 { + return nil, 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 + var names = make([]string, 0, len(ps.field.Names)) + for _, name := range ps.field.Names { + switch ps.p.PropNamingStrategy { + case SnakeCase: + names = append(names, toSnakeCase(name.Name)) + case PascalCase: + names = append(names, name.Name) + default: + names = append(names, toLowerCamelCase(name.Name)) + } } + return names, nil } -func (ps *tagBaseFieldParser) FormName() string { +func (ps *tagBaseFieldParser) firstTagValue(tag string) string { if ps.field.Tag != nil { - return strings.TrimSpace(strings.Split(ps.tag.Get(formTag), ",")[0]) + 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) @@ -161,6 +176,7 @@ func (ps *tagBaseFieldParser) CustomSchema() (*spec.Schema, error) { } type structField struct { + title string schemaType string arrayType string formatType string @@ -267,6 +283,7 @@ func (ps *tagBaseFieldParser) complementSchema(schema *spec.Schema, types []stri field := &structField{ schemaType: types[0], formatType: ps.tag.Get(formatTag), + title: ps.tag.Get(titleTag), } if len(types) > 1 && (types[0] == ARRAY || types[0] == OBJECT) { @@ -412,6 +429,7 @@ func (ps *tagBaseFieldParser) complementSchema(schema *spec.Schema, types []stri if field.schemaType != ARRAY { schema.Format = field.formatType } + schema.Title = field.title extensionsTagValue := ps.tag.Get(extensionsTag) if extensionsTagValue != "" { diff --git a/field_parser_test.go b/field_parser_test.go index 6f4bfdef3..12d00c756 100644 --- a/field_parser_test.go +++ b/field_parser_test.go @@ -60,6 +60,21 @@ func TestDefaultFieldParser(t *testing.T) { assert.Equal(t, "csv", schema.Format) }) + t.Run("Title tag", func(t *testing.T) { + t.Parallel() + + schema := spec.Schema{} + schema.Type = []string{"string"} + err := newTagBaseFieldParser( + &Parser{}, + &ast.Field{Tag: &ast.BasicLit{ + Value: `json:"test" title:"myfield"`, + }}, + ).ComplementSchema(&schema) + assert.NoError(t, err) + assert.Equal(t, "myfield", schema.Title) + }) + t.Run("Required tag", func(t *testing.T) { t.Parallel() @@ -661,14 +676,57 @@ func TestValidTags(t *testing.T) { schema.Type = []string{"integer"} err = newTagBaseFieldParser( &Parser{}, - &ast.Field{Tag: &ast.BasicLit{ - Value: `json:"test" validate:"required,oneof=one two"`, - }}, + &ast.Field{ + Names: []*ast.Ident{{Name: "Test"}}, + Tag: &ast.BasicLit{ + Value: `json:"test" validate:"required,oneof=one two"`, + }}, ).ComplementSchema(&schema) assert.NoError(t, err) assert.Empty(t, schema.Enum) }) + t.Run("Form Filed Name", func(t *testing.T) { + t.Parallel() + + filednames, err := newTagBaseFieldParser( + &Parser{}, + &ast.Field{ + Names: []*ast.Ident{{Name: "Test"}}, + Tag: &ast.BasicLit{ + Value: `form:"test[]"`, + }}, + ).FieldNames() + assert.NoError(t, err) + assert.Equal(t, "test", filednames[0]) + + filednames, err = newTagBaseFieldParser( + &Parser{}, + &ast.Field{ + Names: []*ast.Ident{{Name: "Test"}}, + Tag: &ast.BasicLit{ + Value: `form:"test"`, + }}, + ).FieldNames() + assert.NoError(t, err) + assert.Equal(t, "test", filednames[0]) + }) + + t.Run("Two Names", func(t *testing.T) { + t.Parallel() + + fieldnames, err := newTagBaseFieldParser( + &Parser{}, + &ast.Field{ + Names: []*ast.Ident{{Name: "X"}, {Name: "Y"}}, + }, + ).FieldNames() + assert.NoError(t, err) + assert.Equal(t, 2, len(fieldnames)) + assert.Equal(t, "x", fieldnames[0]) + assert.Equal(t, "y", fieldnames[1]) + }) + t.Run("Pattern tag", func(t *testing.T) { t.Parallel() diff --git a/format/format.go b/format/format.go index 2d0aec410..25551cad1 100644 --- a/format/format.go +++ b/format/format.go @@ -1,7 +1,9 @@ package format import ( + "bytes" "fmt" + "io" "os" "path/filepath" "strings" @@ -80,7 +82,8 @@ func (f *Format) visit(path string, fileInfo os.FileInfo, err error) error { func (f *Format) excludeDir(path string) bool { return f.exclude[path] || - filepath.Base(path)[0] == '.' && len(filepath.Base(path)) > 1 // exclude hidden folders + filepath.Base(path)[0] == '.' && + len(filepath.Base(path)) > 1 // exclude hidden folders } func (f *Format) excludeFile(path string) bool { @@ -98,10 +101,18 @@ func (f *Format) format(path string) error { if err != nil { return err } + if bytes.Equal(contents, formatted) { + // Skip write if no change + return nil + } return write(path, formatted) } func write(path string, contents []byte) error { + originalFileInfo, err := os.Stat(path) + if err != nil { + return err + } f, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)) if err != nil { return err @@ -113,5 +124,25 @@ func write(path string, contents []byte) error { if err := f.Close(); err != nil { return err } + if err := os.Chmod(f.Name(), originalFileInfo.Mode()); err != nil { + return err + } return os.Rename(f.Name(), path) } + +// Run the format on src and write the result to dst. +func (f *Format) Run(src io.Reader, dst io.Writer) error { + contents, err := io.ReadAll(src) + if err != nil { + return err + } + result, err := f.formatter.Format("", contents) + if err != nil { + return err + } + r := bytes.NewReader(result) + if _, err := io.Copy(dst, r); err != nil { + return err + } + return nil +} diff --git a/format/format_test.go b/format/format_test.go index e9eee51b3..d1afd2b1e 100644 --- a/format/format_test.go +++ b/format/format_test.go @@ -16,6 +16,19 @@ func TestFormat_Format(t *testing.T) { assert.True(t, fx.isFormatted("api/api.go")) } +func TestFormat_PermissionsPreserved(t *testing.T) { + fx := setup(t) + + originalFileInfo, err := os.Stat(filepath.Join(fx.basedir, "main.go")) + if err != nil { + t.Fatal(err) + } + + assert.NoError(t, New().Build(&Config{SearchDir: fx.basedir})) + assert.True(t, permissionsEqual(t, filepath.Join(fx.basedir, "main.go"), originalFileInfo.Mode())) + assert.True(t, permissionsEqual(t, filepath.Join(fx.basedir, "api/api.go"), originalFileInfo.Mode())) +} + func TestFormat_ExcludeDir(t *testing.T) { fx := setup(t) assert.NoError(t, New().Build(&Config{ @@ -89,13 +102,21 @@ func setup(t *testing.T) *fixture { } func (fx *fixture) isFormatted(file string) bool { - contents, err := os.ReadFile(filepath.Join(fx.basedir, filepath.Clean(file))) + contents, err := os.ReadFile(filepath.Join(fx.basedir, file)) if err != nil { fx.t.Fatal(err) } return !bytes.Equal(testFiles[file], contents) } +func permissionsEqual(t *testing.T, path string, expectedMode os.FileMode) bool { + fileInfo, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + return expectedMode == fileInfo.Mode() +} + var testFiles = map[string][]byte{ "api/api.go": []byte(`package api diff --git a/gen/gen.go b/gen/gen.go index 58fcfb1a1..d47d2db59 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -22,6 +22,8 @@ import ( v3 "github.com/sv-tools/openapi/spec" "github.com/swaggo/swag/v2" + "golang.org/x/text/cases" + "golang.org/x/text/language" "sigs.k8s.io/yaml" ) @@ -111,8 +113,8 @@ type Config struct { // ParseVendor whether swag should be parse vendor folder ParseVendor bool - // ParseDependencies whether swag should be parse outside dependency folder - ParseDependency bool + // ParseDependencies whether swag should be parse outside dependency folder: 0 none, 1 models, 2 operations, 3 all + ParseDependency int // ParseInternal whether swag should parse internal packages ParseInternal bool @@ -149,6 +151,15 @@ type Config struct { // 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 + + // ParseFuncBody whether swag should parse api info inside of funcs + ParseFuncBody bool } // Build builds swagger json file for given searchDir and mainAPIFile. Returns json. @@ -209,12 +220,15 @@ func (g *Gen) Build(config *Config) error { swag.SetTags(config.Tags), swag.GenerateOpenAPI3Doc(config.GenerateOpenAPI3Doc), swag.SetCollectionFormat(config.CollectionFormat), + swag.SetPackagePrefix(config.PackagePrefix), ) p.PropNamingStrategy = config.PropNamingStrategy p.ParseVendor = config.ParseVendor p.ParseInternal = config.ParseInternal p.RequiredByDefault = config.RequiredByDefault + p.HostState = config.State + p.ParseFuncBody = config.ParseFuncBody if err := p.ParseAPIMultiSearchDir(searchDirs, config.MainAPIFile, config.ParseDepth); err != nil { return err @@ -249,6 +263,10 @@ func (g *Gen) writeOpenAPI(config *Config, doc interface{}) error { func (g *Gen) writeDoc(config *Config, doc interface{}) error { var filename = "docs.go" + if config.State != "" { + filename = config.State + "_" + filename + } + if config.InstanceName != swag.Name { filename = config.InstanceName + "_" + filename } @@ -296,6 +314,10 @@ func (g *Gen) writeDoc(config *Config, doc interface{}) error { func (g *Gen) writeJSON(config *Config, spec interface{}) error { var filename = "swagger.json" + if config.State != "" { + filename = config.State + "_" + filename + } + if config.InstanceName != swag.Name { filename = config.InstanceName + "_" + filename } @@ -320,6 +342,10 @@ func (g *Gen) writeJSON(config *Config, spec interface{}) error { func (g *Gen) writeYAML(config *Config, swagger interface{}) error { var filename = "swagger.yaml" + if config.State != "" { + filename = config.State + "_" + filename + } + if config.InstanceName != swag.Name { filename = config.InstanceName + "_" + filename } @@ -463,6 +489,11 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *v2.Swagg 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 { @@ -474,6 +505,7 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *v2.Swagg Title string Description string Version string + State string InstanceName string Schemes []string GeneratedTime bool @@ -490,6 +522,7 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *v2.Swagg Title: swagger.Info.Title, Description: swagger.Info.Description, Version: swagger.Info.Version, + State: state, InstanceName: config.InstanceName, LeftTemplateDelim: config.LeftTemplateDelim, RightTemplateDelim: config.RightTemplateDelim, diff --git a/gen/gen_test.go b/gen/gen_test.go index 55a40b4a8..5f3da2fa5 100644 --- a/gen/gen_test.go +++ b/gen/gen_test.go @@ -619,7 +619,7 @@ func TestGen_cgoImports(t *testing.T) { OutputDir: "../testdata/simple_cgo/docs", OutputTypes: outputTypes, PropNamingStrategy: "", - ParseDependency: true, + ParseDependency: 1, } assert.NoError(t, New().Build(config)) @@ -853,3 +853,81 @@ func TestGen_ErrorAndInterface(t *testing.T) { assert.JSONEq(t, string(expectedJSON), string(jsonOutput)) } + +func TestGen_StateAdmin(t *testing.T) { + config := &Config{ + SearchDir: "../testdata/state", + MainAPIFile: "./main.go", + OutputDir: "../testdata/state/docs", + OutputTypes: outputTypes, + PropNamingStrategy: "", + State: "admin", + } + + assert.NoError(t, New().Build(config)) + + expectedFiles := []string{ + filepath.Join(config.OutputDir, "admin_docs.go"), + filepath.Join(config.OutputDir, "admin_swagger.json"), + filepath.Join(config.OutputDir, "admin_swagger.yaml"), + } + t.Cleanup(func() { + for _, expectedFile := range expectedFiles { + _ = os.Remove(expectedFile) + } + }) + + // check files + for _, expectedFile := range expectedFiles { + if _, err := os.Stat(expectedFile); os.IsNotExist(err) { + require.NoError(t, err) + } + } + + // check content + jsonOutput, err := os.ReadFile(filepath.Join(config.OutputDir, "admin_swagger.json")) + require.NoError(t, err) + expectedJSON, err := os.ReadFile(filepath.Join(config.SearchDir, "admin_expected.json")) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedJSON), string(jsonOutput)) +} + +func TestGen_StateUser(t *testing.T) { + config := &Config{ + SearchDir: "../testdata/state", + MainAPIFile: "./main.go", + OutputDir: "../testdata/state/docs", + OutputTypes: outputTypes, + PropNamingStrategy: "", + State: "user", + } + + assert.NoError(t, New().Build(config)) + + expectedFiles := []string{ + filepath.Join(config.OutputDir, "user_docs.go"), + filepath.Join(config.OutputDir, "user_swagger.json"), + filepath.Join(config.OutputDir, "user_swagger.yaml"), + } + t.Cleanup(func() { + for _, expectedFile := range expectedFiles { + _ = os.Remove(expectedFile) + } + }) + + // check files + for _, expectedFile := range expectedFiles { + if _, err := os.Stat(expectedFile); os.IsNotExist(err) { + require.NoError(t, err) + } + } + + // check content + jsonOutput, err := os.ReadFile(filepath.Join(config.OutputDir, "user_swagger.json")) + require.NoError(t, err) + expectedJSON, err := os.ReadFile(filepath.Join(config.SearchDir, "user_expected.json")) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedJSON), string(jsonOutput)) +} diff --git a/generics.go b/generics.go index 5f46b4d45..82afba6a4 100644 --- a/generics.go +++ b/generics.go @@ -11,9 +11,8 @@ import ( ) type genericTypeSpec struct { - ArrayDepth int - TypeSpec *TypeSpecDef - Name string + TypeSpec *TypeSpecDef + Name string } type formalParamType struct { @@ -28,6 +27,89 @@ func (t *genericTypeSpec) TypeName() string { 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, + SchemaName: "array_" + typeSpecDef.SchemaName, + 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, + SchemaName: "map_" + parts[0] + "_" + typeSpecDef.SchemaName, + NotUnique: false, + } + } + if IsGolangPrimitiveType(genericParam) { + return &TypeSpecDef{ + TypeSpec: &ast.TypeSpec{ + Name: ast.NewIdent(genericParam), + Type: ast.NewIdent(genericParam), + }, + SchemaName: 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 @@ -55,45 +137,44 @@ func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, origi genericParamTypeDefs := map[string]*genericTypeSpec{} for i, genericParam := range genericParams { - arrayDepth := 0 - for { - if len(genericParam) <= 2 || genericParam[:2] != "[]" { - break - } - genericParam = genericParam[2:] - arrayDepth++ - } - - typeDef := pkgDefs.FindTypeSpec(genericParam, file) - if typeDef != nil { - genericParam = typeDef.TypeName() - if _, ok := pkgDefs.uniqueDefinitions[genericParam]; !ok { - pkgDefs.uniqueDefinitions[genericParam] = typeDef + 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{ - ArrayDepth: arrayDepth, - TypeSpec: typeDef, - Name: genericParam, + TypeSpec: typeDef, + Name: genericParam, } } name = fmt.Sprintf("%s%s-", string(IgnoreNameOverridePrefix), original.TypeName()) + schemaName := fmt.Sprintf("%s-", original.SchemaName) + var nameParts []string + var schemaNameParts []string + for _, def := range formals { if specDef, ok := genericParamTypeDefs[def.Name]; ok { - var prefix = "" - if specDef.ArrayDepth == 1 { - prefix = "array_" - } else if specDef.ArrayDepth > 1 { - prefix = fmt.Sprintf("array%d_", specDef.ArrayDepth) + nameParts = append(nameParts, specDef.Name) + + schemaNamePart := specDef.Name + + if specDef.TypeSpec != nil { + schemaNamePart = specDef.TypeSpec.SchemaName } - nameParts = append(nameParts, prefix+specDef.TypeName()) + + schemaNameParts = append(schemaNameParts, schemaNamePart) } } - name += strings.Replace(strings.Join(nameParts, "-"), ".", "_", -1) + name += normalizeGenericTypeName(strings.Join(nameParts, "-")) + schemaName += normalizeGenericTypeName(strings.Join(schemaNameParts, "-")) if typeSpec, ok := pkgDefs.uniqueDefinitions[name]; ok { return typeSpec @@ -111,6 +192,7 @@ func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, origi Doc: original.TypeSpec.Doc, Assign: original.TypeSpec.Assign, }, + SchemaName: schemaName, } pkgDefs.uniqueDefinitions[name] = parametrizedTypeSpec @@ -177,11 +259,7 @@ func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast. switch astExpr := expr.(type) { case *ast.Ident: if genTypeSpec, ok := genericParamTypeDefs[astExpr.Name]; ok { - retType := pkgDefs.getParametrizedType(genTypeSpec) - for i := 0; i < genTypeSpec.ArrayDepth; i++ { - retType = &ast.ArrayType{Elt: retType} - } - return retType + return pkgDefs.getParametrizedType(genTypeSpec) } case *ast.ArrayType: return &ast.ArrayType{ diff --git a/generics_test.go b/generics_test.go index bd79f93de..74dad3f9a 100644 --- a/generics_test.go +++ b/generics_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "go/ast" - "io/fs" "os" "path/filepath" "testing" @@ -52,6 +51,7 @@ func TestParseGenericsArrays(t *testing.T) { err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) b, err := json.MarshalIndent(p.swagger, "", " ") + assert.NoError(t, err) assert.Equal(t, string(expected), string(b)) } @@ -97,7 +97,6 @@ func TestParseGenericsProperty(t *testing.T) { err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) b, err := json.MarshalIndent(p.swagger, "", " ") - os.WriteFile(searchDir+"/expected.json", b, fs.ModePerm) assert.NoError(t, err) assert.Equal(t, string(expected), string(b)) } @@ -124,7 +123,7 @@ func TestParseGenericsPackageAlias(t *testing.T) { expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) assert.NoError(t, err) - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) b, err := json.MarshalIndent(p.swagger, "", " ") @@ -132,6 +131,22 @@ func TestParseGenericsPackageAlias(t *testing.T) { assert.Equal(t, string(expected), string(b)) } +func TestParseGenericsFunctionScoped(t *testing.T) { + t.Parallel() + + searchDir := "testdata/generics_function_scoped" + expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) + assert.NoError(t, err) + + p := New() + err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) + assert.NoError(t, err) + b, err := json.MarshalIndent(p.swagger, "", " ") + + assert.NoError(t, err) + assert.Equal(t, string(expected), string(b)) +} + func TestParametrizeStruct(t *testing.T) { pd := PackagesDefinitions{ packages: make(map[string]*PackageDefinitions), diff --git a/go.mod b/go.mod index 8f6be0f9f..12f3b265c 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/text v0.14.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 16966338a..dc6e74588 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/golist.go b/golist.go index ca916a8ea..fa0b2cd9e 100644 --- a/golist.go +++ b/golist.go @@ -47,16 +47,20 @@ func listPackages(ctx context.Context, dir string, env []string, args ...string) return pkgs, nil } -func (parser *Parser) getAllGoFileInfoFromDepsByList(pkg *build.Package) error { +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, ParseModels) + err = parser.parseFile(pkg.ImportPath, filepath.Join(srcDir, pkg.GoFiles[i]), nil, parseFlag) if err != nil { return err } @@ -64,7 +68,7 @@ func (parser *Parser) getAllGoFileInfoFromDepsByList(pkg *build.Package) error { // parse .go source files that import "C" for i := range pkg.CgoFiles { - err = parser.parseFile(pkg.ImportPath, filepath.Join(srcDir, pkg.CgoFiles[i]), nil, ParseModels) + err = parser.parseFile(pkg.ImportPath, filepath.Join(srcDir, pkg.CgoFiles[i]), nil, parseFlag) if err != nil { return err } diff --git a/golist_test.go b/golist_test.go index cd0a550f2..21d92cbd4 100644 --- a/golist_test.go +++ b/golist_test.go @@ -105,7 +105,7 @@ func TestGetAllGoFileInfoFromDepsByList(t *testing.T) { p.ParseInternal = false } c.buildPackage.Dir = filepath.Join(pwd, c.buildPackage.Dir) - err := p.getAllGoFileInfoFromDepsByList(c.buildPackage) + err := p.getAllGoFileInfoFromDepsByList(c.buildPackage, ParseModels) if c.except != nil { assert.NotNil(t, err) } else { diff --git a/operation.go b/operation.go index 7569ee790..c01278787 100644 --- a/operation.go +++ b/operation.go @@ -23,6 +23,7 @@ import ( type RouteProperties struct { HTTPMethod string Path string + Deprecated bool } // Operation describes a single API operation on a path. @@ -32,6 +33,7 @@ type Operation struct { codeExampleFilesDir string spec.Operation RouterProperties []RouteProperties + State string } var mimeTypeAliases = map[string]string{ @@ -120,6 +122,8 @@ func (operation *Operation) ParseComment(comment string, astFile *ast.File) erro lineRemainder = fields[1] } switch lowerAttribute { + case stateAttr: + operation.ParseStateComment(lineRemainder) case descriptionAttr: operation.ParseDescriptionComment(lineRemainder) case descriptionMarkdownAttr: @@ -146,7 +150,9 @@ func (operation *Operation) ParseComment(comment string, astFile *ast.File) erro case headerAttr: return operation.ParseResponseHeaderComment(lineRemainder, astFile) case routerAttr: - return operation.ParseRouterComment(lineRemainder) + return operation.ParseRouterComment(lineRemainder, false) + case deprecatedRouterAttr: + return operation.ParseRouterComment(lineRemainder, true) case securityAttr: return operation.ParseSecurityComment(lineRemainder) case deprecatedAttr: @@ -160,7 +166,7 @@ func (operation *Operation) ParseComment(comment string, astFile *ast.File) erro return nil } -// ParseCodeSample godoc. +// ParseCodeSample parse code sample. func (operation *Operation) ParseCodeSample(attribute, _, lineRemainder string) error { log.Println("line remainder:", lineRemainder) @@ -196,7 +202,12 @@ func (operation *Operation) ParseCodeSample(attribute, _, lineRemainder string) return operation.ParseMetadata(attribute, strings.ToLower(attribute), lineRemainder) } -// ParseDescriptionComment godoc. +// ParseStateComment parse state comment. +func (operation *Operation) ParseStateComment(lineRemainder string) { + operation.State = lineRemainder +} + +// ParseDescriptionComment parse description comment. func (operation *Operation) ParseDescriptionComment(lineRemainder string) { if operation.Description == "" { operation.Description = lineRemainder @@ -207,7 +218,7 @@ func (operation *Operation) ParseDescriptionComment(lineRemainder string) { operation.Description += "\n" + lineRemainder } -// ParseMetadata godoc. +// ParseMetadata parse metadata. func (operation *Operation) ParseMetadata(attribute, lowerAttribute, lineRemainder string) error { // parsing specific meta data extensions if strings.HasPrefix(lowerAttribute, "@x-") { @@ -283,21 +294,12 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F requiredText := strings.ToLower(matches[4]) required := requiredText == "true" || requiredText == requiredLabel - description := matches[5] + description := strings.Join(strings.Split(matches[5], "\\n"), "\n") param := createParameter(paramType, description, name, objectType, refType, required, enums, operation.parser.collectionFormatInQuery) switch paramType { - case "path", "header": - switch objectType { - case ARRAY: - if !IsPrimitiveType(refType) { - return fmt.Errorf("%s is not supported array type for %s", refType, paramType) - } - case OBJECT: - return fmt.Errorf("%s is not supported type for %s", refType, paramType) - } - case "query", "formData": + case "path", "header", "query", "formData": switch objectType { case ARRAY: if !IsPrimitiveType(refType) && !(refType == "file" && paramType == "formData") { @@ -326,11 +328,14 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F } } - var formName = name - if item.Schema.Extensions != nil { - if nameVal, ok := item.Schema.Extensions[formTag]; ok { - formName = nameVal.(string) - } + 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) } switch { @@ -342,16 +347,19 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F if len(itemSchema.Type) == 0 { itemSchema = operation.parser.getUnderlyingSchema(prop.Items.Schema) } + if itemSchema == nil { + continue + } if len(itemSchema.Type) == 0 { continue } if !IsSimplePrimitiveType(itemSchema.Type[0]) { continue } - param = createParameter(paramType, prop.Description, formName, prop.Type[0], itemSchema.Type[0], findInSlice(schema.Required, name), itemSchema.Enum, operation.parser.collectionFormatInQuery) + 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, formName, PRIMITIVE, prop.Type[0], findInSlice(schema.Required, name), nil, operation.parser.collectionFormatInQuery) + 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) continue @@ -394,7 +402,8 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F return fmt.Errorf("%s is not supported paramType", paramType) } - err := operation.parseParamAttribute(commentLine, objectType, refType, ¶m) + err := operation.parseParamAttribute(commentLine, objectType, refType, paramType, ¶m) + if err != nil { return err } @@ -407,12 +416,15 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F const ( formTag = "form" jsonTag = "json" + uriTag = "uri" + headerTag = "header" bindingTag = "binding" defaultTag = "default" enumsTag = "enums" exampleTag = "example" schemaExampleTag = "schemaExample" formatTag = "format" + titleTag = "title" validateTag = "validate" minimumTag = "minimum" maximumTag = "maximum" @@ -450,7 +462,7 @@ var regexAttributes = map[string]*regexp.Regexp{ schemaExampleTag: regexp.MustCompile(`(?i)\s+schemaExample\(.*\)`), } -func (operation *Operation) parseParamAttribute(comment, objectType, schemaType string, param *spec.Parameter) error { +func (operation *Operation) parseParamAttribute(comment, objectType, schemaType, paramType string, param *spec.Parameter) error { schemaType = TransToValidSchemeType(schemaType) for attrKey, re := range regexAttributes { @@ -461,7 +473,7 @@ func (operation *Operation) parseParamAttribute(comment, objectType, schemaType switch attrKey { case enumsTag: - err = setEnumParam(param, attr, objectType, schemaType) + err = setEnumParam(param, attr, objectType, schemaType, paramType) case minimumTag, maximumTag: err = setNumberParam(param, attrKey, schemaType, attr, comment) case defaultTag: @@ -540,7 +552,7 @@ func setNumberParam(param *spec.Parameter, name, schemaType, attr, commentLine s } } -func setEnumParam(param *spec.Parameter, attr, objectType, schemaType string) error { +func setEnumParam(param *spec.Parameter, attr, objectType, schemaType, paramType string) error { for _, e := range strings.Split(attr, ",") { e = strings.TrimSpace(e) @@ -553,7 +565,12 @@ func setEnumParam(param *spec.Parameter, attr, objectType, schemaType string) er case ARRAY: param.Items.Enum = append(param.Items.Enum, value) default: - param.Enum = append(param.Enum, value) + switch paramType { + case "body": + param.Schema.Enum = append(param.Schema.Enum, value) + default: + param.Enum = append(param.Enum, value) + } } } @@ -704,10 +721,10 @@ func parseMimeTypeList(mimeTypeList string, typeList *[]string, format string) e return nil } -var routerPattern = regexp.MustCompile(`^(/[\w./\-{}+:$]*)[[:blank:]]+\[(\w+)]`) +var routerPattern = regexp.MustCompile(`^(/[\w./\-{}\(\)+:$]*)[[:blank:]]+\[(\w+)]`) // ParseRouterComment parses comment for given `router` comment string. -func (operation *Operation) ParseRouterComment(commentLine string) error { +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) @@ -716,6 +733,7 @@ func (operation *Operation) ParseRouterComment(commentLine string) error { signature := RouteProperties{ Path: matches[1], HTTPMethod: strings.ToUpper(matches[2]), + Deprecated: deprecated, } if _, ok := allMethod[signature.HTTPMethod]; !ok { @@ -729,6 +747,11 @@ func (operation *Operation) ParseRouterComment(commentLine string) error { // ParseSecurityComment parses comment for given `security` comment string. func (operation *Operation) ParseSecurityComment(commentLine string) error { + if len(commentLine) == 0 { + operation.Security = []map[string][]string{} + return nil + } + var ( securityMap = make(map[string][]string) securitySource = commentLine[strings.Index(commentLine, "@Security")+1:] @@ -832,9 +855,9 @@ func parseObjectSchema(parser *Parser, refType string, astFile *ast.File) (*spec case refType == NIL: return nil, nil case refType == INTERFACE: - return PrimitiveSchema(OBJECT), nil + return &spec.Schema{}, nil case refType == ANY: - return PrimitiveSchema(OBJECT), nil + return &spec.Schema{}, nil case IsGolangPrimitiveType(refType): refType = TransToValidSchemeType(refType) @@ -921,6 +944,10 @@ func parseCombinedObjectSchema(parser *Parser, refType string, astFile *ast.File return nil, err } + if schema == nil { + schema = PrimitiveSchema(OBJECT) + } + props[keyVal[0]] = *schema } } diff --git a/operation_test.go b/operation_test.go index 87bcae9ce..4034bea75 100644 --- a/operation_test.go +++ b/operation_test.go @@ -2,9 +2,12 @@ package swag import ( "encoding/json" + "fmt" "go/ast" goparser "go/parser" "go/token" + "os" + "path/filepath" "testing" "github.com/go-openapi/spec" @@ -173,6 +176,18 @@ func TestParseRouterCommentWithDollarSign(t *testing.T) { assert.Equal(t, "POST", operation.RouterProperties[0].HTTPMethod) } +func TestParseRouterCommentWithParens(t *testing.T) { + t.Parallel() + + comment := `/@Router /customer({id}) [get]` + operation := NewOperation(nil) + err := operation.ParseComment(comment, nil) + assert.NoError(t, err) + assert.Len(t, operation.RouterProperties, 1) + assert.Equal(t, "/customer({id})", operation.RouterProperties[0].Path) + assert.Equal(t, "GET", operation.RouterProperties[0].HTTPMethod) +} + func TestParseRouterCommentNoDollarSignAtPathStartErr(t *testing.T) { t.Parallel() @@ -1176,11 +1191,17 @@ func TestOperation_ParseParamComment(t *testing.T) { t.Parallel() for _, paramType := range []string{"header", "path", "query", "formData"} { t.Run(paramType, func(t *testing.T) { + // unknown object returns error assert.Error(t, NewOperation(nil).ParseComment(`@Param some_object `+paramType+` main.Object true "Some Object"`, nil)) + + // verify objects are supported here + o := NewOperation(nil) + o.parser.addTestType("main.TestObject") + err := o.ParseComment(`@Param some_object `+paramType+` main.TestObject true "Some Object"`, nil) + assert.NoError(t, err) }) } }) - } // Test ParseParamComment Query Params @@ -1314,6 +1335,27 @@ func TestParseParamCommentByID(t *testing.T) { assert.Equal(t, expected, string(b)) } +func TestParseParamCommentWithMultilineDescriptions(t *testing.T) { + t.Parallel() + + comment := `@Param some_id query int true "First line\nSecond line\nThird line"` + operation := NewOperation(nil) + err := operation.ParseComment(comment, nil) + + assert.NoError(t, err) + b, _ := json.MarshalIndent(operation.Parameters, "", " ") + expected := `[ + { + "type": "integer", + "description": "First line\nSecond line\nThird line", + "name": "some_id", + "in": "query", + "required": true + } +]` + assert.Equal(t, expected, string(b)) +} + func TestParseParamCommentByQueryType(t *testing.T) { t.Parallel() @@ -1384,6 +1426,36 @@ func TestParseParamCommentByBodyTextPlain(t *testing.T) { assert.Equal(t, expected, string(b)) } +// TODO: fix this +func TestParseParamCommentByBodyEnumsText(t *testing.T) { + t.Parallel() + + comment := `@Param text body string true "description" Enums(ENUM1, ENUM2, ENUM3)` + operation := NewOperation(nil) + + err := operation.ParseComment(comment, nil) + + assert.NoError(t, err) + b, _ := json.MarshalIndent(operation.Parameters, "", " ") + expected := `[ + { + "description": "description", + "name": "text", + "in": "body", + "required": true, + "schema": { + "type": "string", + "enum": [ + "ENUM1", + "ENUM2", + "ENUM3" + ] + } + } +]` + assert.Equal(t, expected, string(b)) +} + func TestParseParamCommentByBodyTypeWithDeepNestedFields(t *testing.T) { t.Parallel() @@ -1969,6 +2041,7 @@ func TestParseAndExtractionParamAttribute(t *testing.T) { " default(1) maximum(100) minimum(0) format(csv)", "", NUMBER, + "", &numberParam, ) assert.NoError(t, err) @@ -1977,10 +2050,10 @@ func TestParseAndExtractionParamAttribute(t *testing.T) { assert.Equal(t, "csv", numberParam.SimpleSchema.Format) assert.Equal(t, float64(1), numberParam.Default) - err = op.parseParamAttribute(" minlength(1)", "", NUMBER, nil) + err = op.parseParamAttribute(" minlength(1)", "", NUMBER, "", nil) assert.Error(t, err) - err = op.parseParamAttribute(" maxlength(1)", "", NUMBER, nil) + err = op.parseParamAttribute(" maxlength(1)", "", NUMBER, "", nil) assert.Error(t, err) stringParam := spec.Parameter{} @@ -1988,27 +2061,28 @@ func TestParseAndExtractionParamAttribute(t *testing.T) { " default(test) maxlength(100) minlength(0) format(csv)", "", STRING, + "", &stringParam, ) assert.NoError(t, err) assert.Equal(t, int64(0), *stringParam.MinLength) assert.Equal(t, int64(100), *stringParam.MaxLength) assert.Equal(t, "csv", stringParam.SimpleSchema.Format) - err = op.parseParamAttribute(" minimum(0)", "", STRING, nil) + err = op.parseParamAttribute(" minimum(0)", "", STRING, "", nil) assert.Error(t, err) - err = op.parseParamAttribute(" maximum(0)", "", STRING, nil) + err = op.parseParamAttribute(" maximum(0)", "", STRING, "", nil) assert.Error(t, err) arrayParram := spec.Parameter{} - err = op.parseParamAttribute(" collectionFormat(tsv)", ARRAY, STRING, &arrayParram) + err = op.parseParamAttribute(" collectionFormat(tsv)", ARRAY, STRING, "", &arrayParram) assert.Equal(t, "tsv", arrayParram.CollectionFormat) assert.NoError(t, err) - err = op.parseParamAttribute(" collectionFormat(tsv)", STRING, STRING, nil) + err = op.parseParamAttribute(" collectionFormat(tsv)", STRING, STRING, "", nil) assert.Error(t, err) - err = op.parseParamAttribute(" default(0)", "", ARRAY, nil) + err = op.parseParamAttribute(" default(0)", "", ARRAY, "", nil) assert.NoError(t, err) } @@ -2034,6 +2108,146 @@ func TestParseParamCommentByExtensions(t *testing.T) { assert.Equal(t, expected, string(b)) } +func TestParseParamStructCodeExample(t *testing.T) { + t.Parallel() + + fset := token.NewFileSet() + ast, err := goparser.ParseFile(fset, "operation_test.go", `package swag + import structs "github.com/swaggo/swag/testdata/param_structs" + `, goparser.ParseComments) + assert.NoError(t, err) + + parser := New() + err = parser.parseFile("github.com/swaggo/swag/testdata/param_structs", "testdata/param_structs/structs.go", nil, ParseModels) + assert.NoError(t, err) + _, err = parser.packages.ParseTypes() + assert.NoError(t, err) + + validateParameters := func(operation *Operation, params ...spec.Parameter) { + assert.Equal(t, len(params), len(operation.Parameters)) + + for _, param := range params { + found := false + for _, p := range operation.Parameters { + if p.Name == param.Name { + assert.Equal(t, param.ParamProps, p.ParamProps) + assert.Equal(t, param.CommonValidations, p.CommonValidations) + assert.Equal(t, param.SimpleSchema, p.SimpleSchema) + found = true + break + } + } + assert.True(t, found, "found parameter %s", param.Name) + } + } + + // values used in validation checks + max := float64(10) + maxLen := int64(10) + min := float64(0) + + // query and form behave the same + for _, param := range []string{"query", "formData"} { + t.Run(param+" struct", func(t *testing.T) { + operation := NewOperation(parser) + comment := fmt.Sprintf(`@Param model %s structs.FormModel true "query params"`, param) + err = operation.ParseComment(comment, ast) + assert.NoError(t, err) + + validateParameters(operation, + spec.Parameter{ + ParamProps: spec.ParamProps{ + Name: "f", + Description: "", + In: param, + Required: true, + }, + CommonValidations: spec.CommonValidations{ + MaxLength: &maxLen, + }, + SimpleSchema: spec.SimpleSchema{ + Type: "string", + }, + }, + spec.Parameter{ + ParamProps: spec.ParamProps{ + Name: "b", + Description: "B is another field", + In: param, + }, + SimpleSchema: spec.SimpleSchema{ + Type: "boolean", + }, + }) + }) + } + + t.Run("header struct", func(t *testing.T) { + operation := NewOperation(parser) + comment := `@Param auth header structs.AuthHeader true "auth header"` + err = operation.ParseComment(comment, ast) + assert.NoError(t, err) + + validateParameters(operation, + spec.Parameter{ + ParamProps: spec.ParamProps{ + Name: "X-Auth-Token", + Description: "Token is the auth token", + In: "header", + Required: true, + }, + SimpleSchema: spec.SimpleSchema{ + Type: "string", + }, + }, spec.Parameter{ + ParamProps: spec.ParamProps{ + Name: "anotherHeader", + Description: "AnotherHeader is another header", + In: "header", + }, + CommonValidations: spec.CommonValidations{ + Maximum: &max, + Minimum: &min, + }, + SimpleSchema: spec.SimpleSchema{ + Type: "integer", + }, + }) + }) + + t.Run("path struct", func(t *testing.T) { + operation := NewOperation(parser) + comment := `@Param path path structs.PathModel true "path params"` + err = operation.ParseComment(comment, ast) + assert.NoError(t, err) + + validateParameters(operation, + spec.Parameter{ + ParamProps: spec.ParamProps{ + Name: "id", + Description: "ID is the id", + In: "path", + Required: true, + }, + SimpleSchema: spec.SimpleSchema{ + Type: "integer", + }, + }, spec.Parameter{ + ParamProps: spec.ParamProps{ + Name: "name", + Description: "", + In: "path", + }, + CommonValidations: spec.CommonValidations{ + MaxLength: &maxLen, + }, + SimpleSchema: spec.SimpleSchema{ + Type: "string", + }, + }) + }) +} + func TestParseIdComment(t *testing.T) { t.Parallel() @@ -2268,15 +2482,16 @@ func TestParseObjectSchema(t *testing.T) { schema, err := operation.parseObjectSchema("interface{}", nil) assert.NoError(t, err) - assert.Equal(t, schema, PrimitiveSchema(OBJECT)) + assert.Equal(t, schema, &spec.Schema{}) schema, err = operation.parseObjectSchema("any", nil) assert.NoError(t, err) - assert.Equal(t, schema, PrimitiveSchema(OBJECT)) + assert.Equal(t, schema, &spec.Schema{}) schema, err = operation.parseObjectSchema("any{data=string}", nil) assert.NoError(t, err) - assert.Equal(t, schema, PrimitiveSchema(OBJECT).SetProperty("data", *PrimitiveSchema("string"))) + assert.Equal(t, schema, + (&spec.Schema{}).WithAllOf(spec.Schema{}, *PrimitiveSchema(OBJECT).SetProperty("data", *PrimitiveSchema("string")))) schema, err = operation.parseObjectSchema("int", nil) assert.NoError(t, err) @@ -2387,3 +2602,16 @@ func TestParseCodeSamples(t *testing.T) { assert.Error(t, err, "no error should be thrown") }) } + +func TestParseDeprecatedRouter(t *testing.T) { + p := New() + searchDir := "./testdata/deprecated_router" + if err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth); err != nil { + t.Error("Failed to parse api: " + err.Error()) + } + + b, _ := json.MarshalIndent(p.swagger, "", " ") + expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) + assert.NoError(t, err) + assert.Equal(t, expected, b) +} diff --git a/package.go b/package.go index 6b4e3bd0d..487da300e 100644 --- a/package.go +++ b/package.go @@ -109,12 +109,22 @@ func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr panic(err) } } - - //octet - if len(valueExpr.Value) > 1 && valueExpr.Value[0] == '0' { - if x, err := strconv.ParseInt(valueExpr.Value[1:], 8, 64); err == nil { + 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[1:], 8, 64); err == nil { + } else if x, err := strconv.ParseUint(valueExpr.Value[start:], base, 64); err == nil { return x, nil } else { panic(err) diff --git a/packages.go b/packages.go index 4bf31b751..2f157b00f 100644 --- a/packages.go +++ b/packages.go @@ -19,7 +19,7 @@ type PackagesDefinitions struct { files map[*ast.File]*AstFileInfo packages map[string]*PackageDefinitions uniqueDefinitions map[string]*TypeSpecDef - parseDependency bool + parseDependency ParseFlag debug Debugger } @@ -93,7 +93,7 @@ func (pkgDefs *PackagesDefinitions) RangeFiles(handle func(info *AstFileInfo) er 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()) { + if strings.HasPrefix(info.PackagePath, "vendor") || (runtime.GOROOT() != "" && strings.HasPrefix(info.Path, runtime.GOROOT()+string(filepath.Separator))) { continue } sortedFiles = append(sortedFiles, info) @@ -133,7 +133,6 @@ func (pkgDefs *PackagesDefinitions) parseTypesFromFile(astFile *ast.File, packag if !ok { continue } - if generalDeclaration.Tok == token.TYPE { for _, astSpec := range generalDeclaration.Specs { if typeSpec, ok := astSpec.(*ast.TypeSpec); ok { @@ -167,6 +166,8 @@ func (pkgDefs *PackagesDefinitions) parseTypesFromFile(astFile *ast.File, packag pkgDefs.uniqueDefinitions[fullName] = nil anotherTypeDef.NotUnique = true pkgDefs.uniqueDefinitions[anotherTypeDef.TypeName()] = anotherTypeDef + anotherTypeDef.SetSchemaName() + typeSpecDef.NotUnique = true fullName = typeSpecDef.TypeName() pkgDefs.uniqueDefinitions[fullName] = typeSpecDef @@ -175,6 +176,8 @@ func (pkgDefs *PackagesDefinitions) parseTypesFromFile(astFile *ast.File, packag pkgDefs.uniqueDefinitions[fullName] = typeSpecDef } + typeSpecDef.SetSchemaName() + 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 { @@ -193,6 +196,7 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as for _, astDeclaration := range astFile.Decls { funcDeclaration, ok := astDeclaration.(*ast.FuncDecl) if ok && funcDeclaration.Body != nil { + functionScopedTypes := make(map[string]*TypeSpecDef) 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 { @@ -213,12 +217,31 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as } } + fullName := typeSpecDef.TypeName() + if structType, ok := typeSpecDef.TypeSpec.Type.(*ast.StructType); ok { + for _, field := range structType.Fields.List { + var idt *ast.Ident + var ok bool + switch field.Type.(type) { + case *ast.Ident: + idt, ok = field.Type.(*ast.Ident) + case *ast.StarExpr: + idt, ok = field.Type.(*ast.StarExpr).X.(*ast.Ident) + case *ast.ArrayType: + idt, ok = field.Type.(*ast.ArrayType).Elt.(*ast.Ident) + } + if ok && !IsGolangPrimitiveType(idt.Name) { + if functype, ok := functionScopedTypes[idt.Name]; ok { + idt.Name = functype.TypeName() + } + } + } + } + if pkgDefs.uniqueDefinitions == nil { pkgDefs.uniqueDefinitions = make(map[string]*TypeSpecDef) } - fullName := typeSpecDef.TypeName() - anotherTypeDef, ok := pkgDefs.uniqueDefinitions[fullName] if ok { if anotherTypeDef == nil { @@ -229,14 +252,19 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as pkgDefs.uniqueDefinitions[fullName] = nil anotherTypeDef.NotUnique = true pkgDefs.uniqueDefinitions[anotherTypeDef.TypeName()] = anotherTypeDef + anotherTypeDef.SetSchemaName() + typeSpecDef.NotUnique = true fullName = typeSpecDef.TypeName() pkgDefs.uniqueDefinitions[fullName] = typeSpecDef } } else { pkgDefs.uniqueDefinitions[fullName] = typeSpecDef + functionScopedTypes[typeSpec.Name.Name] = typeSpecDef } + typeSpecDef.SetSchemaName() + 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 { @@ -326,7 +354,7 @@ func (pkgDefs *PackagesDefinitions) EvaluateConstValueByName(file *ast.File, pkg } } } - if pkgDefs.parseDependency { + if pkgDefs.parseDependency > 0 { for _, pkgPath := range externalPkgPaths { if err := pkgDefs.loadExternalPackage(pkgPath); err == nil { if pkg, ok := pkgDefs.packages[pkgPath]; ok { @@ -355,8 +383,8 @@ func (pkgDefs *PackagesDefinitions) collectConstEnums(parsedSchemas map[*TypeSpe continue } - //delete it from parsed schemas, and will parse it again - if _, ok := parsedSchemas[typeDef]; ok { + // delete it from parsed schemas, and will parse it again + if _, ok = parsedSchemas[typeDef]; ok { delete(parsedSchemas, typeDef) } @@ -365,7 +393,7 @@ func (pkgDefs *PackagesDefinitions) collectConstEnums(parsedSchemas map[*TypeSpe } name := constVar.Name.Name - if _, ok := constVar.Value.(ast.Expr); ok { + if _, ok = constVar.Value.(ast.Expr); ok { continue } @@ -471,7 +499,7 @@ func (pkgDefs *PackagesDefinitions) findPackagePathFromImports(pkg string, file } break } else if imp.Name.Name == "_" && len(pkg) > 0 { - //for unused types + // for unused types pd, ok := pkgDefs.packages[path] if ok { if pd.Name == pkg { @@ -508,14 +536,7 @@ func (pkgDefs *PackagesDefinitions) findPackagePathFromImports(pkg string, file } func (pkgDefs *PackagesDefinitions) findTypeSpecFromPackagePaths(matchedPkgPaths, externalPkgPaths []string, name string) (typeDef *TypeSpecDef) { - for _, pkgPath := range matchedPkgPaths { - typeDef = pkgDefs.findTypeSpec(pkgPath, name) - if typeDef != nil { - return typeDef - } - } - - if pkgDefs.parseDependency { + if pkgDefs.parseDependency > 0 { for _, pkgPath := range externalPkgPaths { if err := pkgDefs.loadExternalPackage(pkgPath); err == nil { typeDef = pkgDefs.findTypeSpec(pkgPath, name) @@ -526,6 +547,13 @@ func (pkgDefs *PackagesDefinitions) findTypeSpecFromPackagePaths(matchedPkgPaths } } + for _, pkgPath := range matchedPkgPaths { + typeDef = pkgDefs.findTypeSpec(pkgPath, name) + if typeDef != nil { + return typeDef + } + } + return typeDef } @@ -544,24 +572,28 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File parts := strings.Split(strings.Split(typeName, "[")[0], ".") if len(parts) > 1 { - typeDef, ok := pkgDefs.uniqueDefinitions[typeName] - if ok { - return typeDef - } - pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports(parts[0], file) - - if len(pkgPaths) == 0 && len(externalPkgPaths) == 0 { - pkgDefinition := pkgDefs.packages["pkg/"+parts[0]] - if pkgDefinition == nil { - return pkgDefs.findTypeSpec("", parts[1]) + if len(externalPkgPaths) == 0 || pkgDefs.parseDependency == ParseNone { + typeDef, ok := pkgDefs.uniqueDefinitions[typeName] + if ok { + return typeDef } - - typeDef = pkgDefinition.TypeDefinitions[parts[1]] - } else { - typeDef = pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, parts[1]) } - + typeDef := pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, parts[1]) + /* + TODO : remove + if len(pkgPaths) == 0 && len(externalPkgPaths) == 0 { + pkgDefinition := pkgDefs.packages["pkg/"+parts[0]] + if pkgDefinition == nil { + return pkgDefs.findTypeSpec("", parts[1]) + } + + typeDef = pkgDefinition.TypeDefinitions[parts[1]] + } else { + typeDef = pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, parts[1]) + } + + */ return pkgDefs.parametrizeGenericType(file, typeDef, typeName) } @@ -570,12 +602,6 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File 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 { @@ -593,7 +619,22 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File } } - return pkgDefs.parametrizeGenericType(file, typeDef, typeName) + if typeDef != nil { + return pkgDefs.parametrizeGenericType(file, typeDef, typeName) + } + + // in case that comment //@name renamed the type with a name without a dot + for k, v := range pkgDefs.uniqueDefinitions { + if v == nil { + pkgDefs.debug.Printf("%s TypeSpecDef is nil", k) + continue + } + if v.SchemaName == typeName { + return v + } + } + + return nil } func isAliasPkgName(file *ast.File, pkgName string) bool { diff --git a/parser.go b/parser.go index d37f64ebb..7a71f1026 100644 --- a/parser.go +++ b/parser.go @@ -35,16 +35,18 @@ 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" + 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" @@ -70,6 +72,7 @@ const ( xCodeSamplesAttr = "@x-codesamples" xCodeSamplesAttrOriginal = "@x-codeSamples" scopeAttrPrefix = "@scope." + stateAttr = "@state" ) // ParseFlag determine what to parse @@ -78,10 +81,10 @@ type ParseFlag int const ( // ParseNone parse nothing ParseNone ParseFlag = 0x00 - // ParseOperations parse operations - ParseOperations = 0x01 // ParseModels parse models - ParseModels = 0x02 + ParseModels = 0x01 + // ParseOperations parse operations + ParseOperations = 0x02 // ParseAll parse operations and models ParseAll = ParseOperations | ParseModels ) @@ -139,8 +142,8 @@ type Parser struct { // ParseVendor parse vendor folder ParseVendor bool - // ParseDependencies whether swag should be parse outside dependency folder - ParseDependency bool + // ParseDependencies whether swag should be parse outside dependency folder: 0 none, 1 models, 2 operations, 3 all + ParseDependency ParseFlag // ParseInternal whether swag should parse internal packages ParseInternal bool @@ -166,6 +169,10 @@ type Parser struct { // 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 @@ -187,6 +194,12 @@ type Parser struct { // tags to filter the APIs after tags map[string]struct{} + // HostState is the state of the host + HostState string + + // ParseFuncBody whether swag should parse api info inside of funcs + ParseFuncBody bool + // use new openAPI version openAPIVersion bool } @@ -197,8 +210,10 @@ type FieldParserFactory func(ps *Parser, field *ast.Field) FieldParser // FieldParser parse struct field. type FieldParser interface { ShouldSkip() bool - FieldName() (string, error) + FieldNames() ([]string, error) FormName() string + HeaderName() string + PathName() string CustomSchema() (*spec.Schema, error) ComplementSchema(schema *spec.Schema) error IsRequired() (bool, error) @@ -270,11 +285,11 @@ func New(options ...func(*Parser)) *Parser { } // SetParseDependency sets whether to parse the dependent packages. -func SetParseDependency(parseDependency bool) func(*Parser) { +func SetParseDependency(parseDependency int) func(*Parser) { return func(p *Parser) { - p.ParseDependency = parseDependency + p.ParseDependency = ParseFlag(parseDependency) if p.packages != nil { - p.packages.parseDependency = parseDependency + p.packages.parseDependency = p.ParseDependency } } } @@ -306,6 +321,20 @@ func SetExcludedDirsAndFiles(excludes string) func(*Parser) { } } +// 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) { @@ -383,6 +412,20 @@ func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string, parseDepth return parser.ParseAPIMultiSearchDir([]string{searchDir}, mainAPIFile, parseDepth) } +// 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 + } + } + 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 { @@ -405,8 +448,41 @@ func (parser *Parser) ParseAPIMultiSearchDir(searchDirs []string, mainAPIFile st } // Use 'go list' command instead of depth.Resolve() - if parser.ParseDependency { - parser.parseDeps(absMainAPIFilePath, parseDepth) + 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 { + 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) @@ -443,7 +519,7 @@ func (parser *Parser) parseDeps(absMainAPIFilePath string, parseDepth int) error length := len(pkgs) for i := 0; i < length; i++ { - err := parser.getAllGoFileInfoFromDepsByList(pkgs[i]) + err := parser.getAllGoFileInfoFromDepsByList(pkgs[i], parser.ParseDependency) if err != nil { return err } @@ -463,7 +539,7 @@ func (parser *Parser) parseDeps(absMainAPIFilePath string, parseDepth int) error } for i := 0; i < len(t.Root.Deps); i++ { - if err := parser.getAllGoFileInfoFromDeps(&t.Root.Deps[i]); err != nil { + if err := parser.getAllGoFileInfoFromDeps(&t.Root.Deps[i], parser.ParseDependency); err != nil { return errors.Wrap(err, "could not parse dependencies") } } @@ -541,7 +617,7 @@ func (parser *Parser) ParseGeneralAPIInfo(mainAPIFile string) error { func parseGeneralAPIInfo(parser *Parser, comments []string) error { previousAttribute := "" - + var tag *spec.Tag // parsing classic meta data model for line := 0; line < len(comments); line++ { commentLine := comments[line] @@ -578,6 +654,14 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { case "@host": parser.swagger.Host = value + case "@hoststate": + fields = FieldsByAnySpace(commentLine, 3) + if len(fields) != 3 { + return fmt.Errorf("%s needs 3 arguments", attribute) + } + if parser.HostState == fields[1] { + parser.swagger.Host = fields[2] + } case "@basepath": parser.swagger.BasePath = value @@ -594,42 +678,43 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { 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, - }, - }) + if parser.matchTag(value) { + parser.swagger.Tags = append(parser.swagger.Tags, spec.Tag{ + TagProps: spec.TagProps{ + Name: value, + }, + }) + tag = &parser.swagger.Tags[len(parser.swagger.Tags)-1] + } else { + tag = nil + } case "@tag.description": - tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] - tag.TagProps.Description = value - replaceLastTag(parser.swagger.Tags, tag) + if tag != nil { + tag.TagProps.Description = value + } case "@tag.description.markdown": - tag := parser.swagger.Tags[len(parser.swagger.Tags)-1] + if tag != nil { + commentInfo, err := getMarkdownForTag(tag.TagProps.Name, parser.markdownFileDir) + if err != nil { + return err + } - commentInfo, err := getMarkdownForTag(tag.TagProps.Name, parser.markdownFileDir) - if err != nil { - return err + tag.TagProps.Description = string(commentInfo) } - - 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: "", + if tag != nil { + 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) + if tag != nil { + if tag.TagProps.ExternalDocs == nil { + return fmt.Errorf("%s needs to come after a @tags.docs.url", attribute) + } + tag.TagProps.ExternalDocs.Description = value + } case secBasicAttr, secAPIKeyAttr, secApplicationAttr, secImplicitAttr, secPasswordAttr, secAccessCodeAttr: scheme, err := parseSecAttributes(attribute, comments, &line) if err != nil { @@ -638,6 +723,9 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { parser.swagger.SecurityDefinitions[value] = scheme + case securityAttr: + parser.swagger.Security = append(parser.swagger.Security, parseSecurity(value)) + case "@query.collection.format": parser.collectionFormatInQuery = TransToValidCollectionFormat(value) @@ -768,6 +856,7 @@ func parseSecAttributes(context string, lines []string, index *int) (*spec.Secur 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 { @@ -785,28 +874,29 @@ func parseSecAttributes(context string, lines []string, index *int) (*spec.Secur for _, findTerm := range search { if securityAttr == findTerm { attrMap[securityAttr] = value - - break + continue loopline } } - isExists, err := isExistsScope(securityAttr) - if err != nil { + if isExists, err := isExistsScope(securityAttr); err != nil { return nil, err - } - - if isExists { - scopes[securityAttr[len(scopeAttrPrefix):]] = v[len(securityAttr):] + } 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 } // Not mandatory field if securityAttr == descriptionAttr { - description = value + if description != "" { + description += "\n" + } + description += value } // next securityDefinitions @@ -850,6 +940,34 @@ func parseSecAttributes(context string, lines []string, index *int) (*spec.Secur return scheme, 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{} + } + } + + return securityMap +} + func initIfEmpty(license *spec.License) *spec.License { if license == nil { return new(spec.License) @@ -887,6 +1005,13 @@ func isGeneralAPIComment(comment string) bool { } func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) { + if tagName == "" { + // this happens when parsing the @description.markdown attribute + // it will be called properly another time with tagName="api" + // so we can safely return an empty byte slice here + return make([]byte, 0), nil + } + dirEntries, err := os.ReadDir(dirPath) if err != nil { return nil, err @@ -899,11 +1024,12 @@ func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) { fileName := entry.Name() - if !strings.Contains(fileName, ".md") { - continue + expectedFileName := tagName + if !strings.HasSuffix(tagName, ".md") { + expectedFileName = tagName + ".md" } - if strings.Contains(fileName, tagName) { + if fileName == expectedFileName { fullPath := filepath.Join(dirPath, fileName) commentInfo, err := os.ReadFile(fullPath) @@ -921,14 +1047,14 @@ func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) { func isExistsScope(scope string) (bool, error) { s := strings.Fields(scope) for _, v := range s { - if strings.Contains(v, scopeAttrPrefix) { + if strings.HasPrefix(v, scopeAttrPrefix) { if strings.Contains(v, ",") { return false, fmt.Errorf("@scope can't use comma(,) get=" + v) } } } - return strings.Contains(scope, scopeAttrPrefix), nil + return strings.HasPrefix(scope, scopeAttrPrefix), nil } func getTagsFromComment(comment string) (tags []string) { @@ -949,20 +1075,51 @@ func getTagsFromComment(comment string) (tags []string) { } +func (parser *Parser) matchTag(tag string) bool { + if len(parser.tags) == 0 { + return true + } + + if _, has := parser.tags["!"+tag]; has { + return false + } + if _, has := parser.tags[tag]; has { + return true + } + + // If all tags are negation then we should return true + for key := range parser.tags { + if key[0] != '!' { + return false + } + } + return true +} + func (parser *Parser) matchTags(comments []*ast.Comment) (match bool) { - if len(parser.tags) != 0 { - for _, comment := range comments { - for _, tag := range getTagsFromComment(comment.Text) { - if _, has := parser.tags["!"+tag]; has { - return false - } + if len(parser.tags) == 0 { + return true + } - if _, has := parser.tags[tag]; has { - match = true // keep iterating as it may contain a tag that is excluded - } + 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 } } - return } return true @@ -990,30 +1147,51 @@ func matchExtension(extensionToMatch string, comments []*ast.Comment) (match boo // ParseRouterAPIInfo parses router api info for given astFile. func (parser *Parser) ParseRouterAPIInfo(fileInfo *AstFileInfo) error { - for _, astDescription := range fileInfo.File.Decls { - if (fileInfo.ParseFlag & ParseOperations) == ParseNone { - continue + if (fileInfo.ParseFlag & ParseOperations) == ParseNone { + return nil + } + + // parse File.Comments instead of File.Decls.Doc if ParseFuncBody flag set to "true" + if parser.ParseFuncBody { + for _, astComments := range fileInfo.File.Comments { + if astComments.List != nil { + if err := parser.parseRouterAPIInfoComment(astComments.List, fileInfo); err != nil { + return err + } + } } + return nil + } + + for _, astDescription := range fileInfo.File.Decls { 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) - } - } - err := processRouterOperation(parser, operation) - if err != nil { - return err - } + if err := parser.parseRouterAPIInfoComment(astDeclaration.Doc.List, fileInfo); err != nil { + return err + } + } + } + return nil +} + +func (parser *Parser) parseRouterAPIInfoComment(comments []*ast.Comment, fileInfo *AstFileInfo) error { + if parser.matchTags(comments) && matchExtension(parser.parseExtension, comments) { + // for per 'function' comment, create a new 'Operation' object + operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir)) + for _, comment := range comments { + 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 { + return nil + } + } + err := processRouterOperation(parser, operation) + if err != nil { + return err } } @@ -1065,7 +1243,25 @@ func processRouterOperation(parser *Parser, operation *Operation) error { parser.debug.Printf("warning: %s\n", err) } - *op = &operation.Operation + 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 routeProperties.Deprecated { + (*op).Deprecated = routeProperties.Deprecated + } parser.swagger.Paths.Paths[routeProperties.Path] = pathItem } @@ -1142,7 +1338,7 @@ func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) ( if err == ErrRecursiveParseStruct && ref { return parser.getRefTypeSchema(typeSpecDef, schema), nil } - return nil, err + return nil, fmt.Errorf("%s: %w", typeName, err) } } @@ -1219,7 +1415,10 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error) } if definition.Description == "" { - fillDefinitionDescription(definition, typeSpecDef.File, typeSpecDef) + err = parser.fillDefinitionDescription(definition, typeSpecDef.File, typeSpecDef) + if err != nil { + return nil, err + } } if len(typeSpecDef.Enums) > 0 { @@ -1241,8 +1440,14 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error) } } + schemaName := typeName + + if typeSpecDef.SchemaName != "" { + schemaName = typeSpecDef.SchemaName + } + sch := Schema{ - Name: typeName, + Name: schemaName, PkgPath: typeSpecDef.PkgPath, Schema: definition, } @@ -1263,7 +1468,10 @@ func fullTypeName(parts ...string) string { // 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) { +func (parser *Parser) fillDefinitionDescription(definition *spec.Schema, file *ast.File, typeSpecDef *TypeSpecDef) (err error) { + if file == nil { + return + } for _, astDeclaration := range file.Decls { generalDeclaration, ok := astDeclaration.(*ast.GenDecl) if !ok || generalDeclaration.Tok != token.TYPE { @@ -1275,16 +1483,23 @@ func fillDefinitionDescription(definition *spec.Schema, file *ast.File, typeSpec if !ok || typeSpec != typeSpecDef.TypeSpec { continue } - - definition.Description = - extractDeclarationDescription(typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc) + var typeName string + if typeSpec.Name != nil { + typeName = typeSpec.Name.Name + } + definition.Description, err = + parser.extractDeclarationDescription(typeName, typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc) + if err != nil { + return + } } } + return nil } // extractDeclarationDescription gets first description // from attribute descriptionAttr in commentGroups (ast.CommentGroup) -func extractDeclarationDescription(commentGroups ...*ast.CommentGroup) string { +func (parser *Parser) extractDeclarationDescription(typeName string, commentGroups ...*ast.CommentGroup) (string, error) { var description string for _, commentGroup := range commentGroups { @@ -1299,9 +1514,23 @@ func extractDeclarationDescription(commentGroups ...*ast.CommentGroup) string { if len(commentText) == 0 { continue } - attribute := FieldsByAnySpace(commentText, 2)[0] + fields := FieldsByAnySpace(commentText, 2) + attribute := fields[0] - if strings.ToLower(attribute) != descriptionAttr { + if attr := strings.ToLower(attribute); attr == descriptionMarkdownAttr { + if len(fields) > 1 { + typeName = fields[1] + } + if typeName == "" { + continue + } + desc, err := getMarkdownForTag(typeName, parser.markdownFileDir) + if err != nil { + return "", err + } + // if found markdown description, we will only use the markdown file content + return string(desc), nil + } else if attr != descriptionAttr { if !isHandlingDescription { continue } @@ -1314,7 +1543,7 @@ func extractDeclarationDescription(commentGroups ...*ast.CommentGroup) string { } } - return strings.TrimLeft(description, " ") + return strings.TrimLeft(description, " "), nil } // parseTypeExpr parses given type expression that corresponds to the type under @@ -1376,7 +1605,7 @@ func (parser *Parser) parseStruct(file *ast.File, fields *ast.FieldList) (*spec. for _, field := range fields.List { fieldProps, requiredFromAnon, err := parser.parseStructField(file, field) if err != nil { - if err == ErrFuncTypeField || err == ErrSkippedField { + if errors.Is(err, ErrFuncTypeField) || errors.Is(err, ErrSkippedField) { continue } @@ -1419,12 +1648,12 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st return nil, nil, nil } - fieldName, err := ps.FieldName() + fieldNames, err := ps.FieldNames() if err != nil { return nil, nil, err } - if fieldName == "" { + if len(fieldNames) == 0 { typeName, err := getFieldType(file, field.Type, nil) if err != nil { return nil, nil, err @@ -1454,7 +1683,7 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st schema, err := ps.CustomSchema() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("%v: %w", fieldNames, err) } if schema == nil { @@ -1468,34 +1697,43 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st } if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("%v: %w", fieldNames, err) } } err = ps.ComplementSchema(schema) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("%v: %w", fieldNames, err) } var tagRequired []string required, err := ps.IsRequired() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("%v: %w", fieldNames, err) } if required { - tagRequired = append(tagRequired, fieldName) + tagRequired = append(tagRequired, fieldNames...) } + if schema.Extensions == nil { + schema.Extensions = make(spec.Extensions) + } if formName := ps.FormName(); len(formName) > 0 { - if schema.Extensions == nil { - schema.Extensions = make(spec.Extensions) - } - schema.Extensions[formTag] = formName + schema.Extensions["formData"] = formName } - - return map[string]spec.Schema{fieldName: *schema}, tagRequired, nil + if headerName := ps.HeaderName(); len(headerName) > 0 { + schema.Extensions["header"] = headerName + } + if pathName := ps.PathName(); len(pathName) > 0 { + schema.Extensions["path"] = pathName + } + fields := make(map[string]spec.Schema) + for _, name := range fieldNames { + fields[name] = *schema + } + return fields, tagRequired, nil } func getFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) { @@ -1581,10 +1819,6 @@ func (parser *Parser) GetSchemaTypePath(schema *spec.Schema, depth int) []string return []string{ANY} } -func replaceLastTag(slice []spec.Tag, element spec.Tag) { - slice = append(slice[:len(slice)-1], element) -} - // defineTypeOfExample example value define the type (object and array unsupported). func defineTypeOfExample(schemaType, arrayType, exampleValue string) (interface{}, error) { switch schemaType { @@ -1660,6 +1894,9 @@ func defineTypeOfExample(schemaType, arrayType, exampleValue string) (interface{ // 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 { @@ -1679,12 +1916,16 @@ func (parser *Parser) getAllGoFileInfo(packageDir, searchDir string) error { }) } -func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg) error { +func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg, parseFlag ParseFlag) 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 @@ -1703,13 +1944,13 @@ func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg) error { } path := filepath.Join(srcDir, f.Name()) - if err := parser.parseFile(pkg.Name, path, nil, ParseModels); err != nil { + if err := parser.parseFile(pkg.Name, path, nil, parseFlag); err != nil { return err } } for i := 0; i < len(pkg.Deps); i++ { - if err := parser.getAllGoFileInfoFromDeps(&pkg.Deps[i]); err != nil { + if err := parser.getAllGoFileInfoFromDeps(&pkg.Deps[i], parseFlag); err != nil { return err } } diff --git a/parser_test.go b/parser_test.go index 92cb0624d..11b2994c0 100644 --- a/parser_test.go +++ b/parser_test.go @@ -219,7 +219,7 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) { "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information" + "admin": "Grants read and write access to administrative information" }, "x-tokenname": "id_token" }, @@ -228,8 +228,8 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) { "flow": "application", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Implicit": { @@ -237,8 +237,8 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) { "flow": "implicit", "authorizationUrl": "https://example.com/oauth/authorize", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" }, "x-google-audiences": "some_audience.google.com" }, @@ -247,9 +247,9 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) { "flow": "password", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "read": " Grants read access", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "read": "Grants read access", + "write": "Grants write access" } } }, @@ -310,7 +310,7 @@ func TestParser_ParseGeneralApiInfoTemplated(t *testing.T) { "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information" + "admin": "Grants read and write access to administrative information" } }, "OAuth2Application": { @@ -318,8 +318,8 @@ func TestParser_ParseGeneralApiInfoTemplated(t *testing.T) { "flow": "application", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Implicit": { @@ -327,8 +327,8 @@ func TestParser_ParseGeneralApiInfoTemplated(t *testing.T) { "flow": "implicit", "authorizationUrl": "https://example.com/oauth/authorize", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Password": { @@ -336,9 +336,9 @@ func TestParser_ParseGeneralApiInfoTemplated(t *testing.T) { "flow": "password", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "read": " Grants read access", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "read": "Grants read access", + "write": "Grants write access" } } }, @@ -593,6 +593,41 @@ func TestParser_ParseGeneralAPITagDocs(t *testing.T) { assert.Equal(t, expected, string(b)) } +func TestParser_ParseGeneralAPITagDocsWithTagFilters(t *testing.T) { + t.Parallel() + + filterTags := []string{"test1", "!test2"} + + comments := []string{ + "@tag.name test1", + "@tag.description A test1 Tag", + "@tag.docs.url https://example1.com", + "@tag.docs.description Best example1 documentation", + "@tag.name test2", + "@tag.description A test2 Tag", + "@tag.docs.url https://example2.com", + "@tag.docs.description Best example2 documentation"} + + expected := `[ + { + "description": "A test1 Tag", + "name": "test1", + "externalDocs": { + "description": "Best example1 documentation", + "url": "https://example1.com" + } + } +]` + + for _, tag := range filterTags { + parser := New(SetTags(tag)) + err := parseGeneralAPIInfo(parser, comments) + assert.NoError(t, err) + b, _ := json.MarshalIndent(parser.GetSwagger().Tags, "", " ") + assert.Equal(t, expected, string(b)) + } +} + func TestParser_ParseGeneralAPISecurity(t *testing.T) { t.Run("ApiKey", func(t *testing.T) { t.Parallel() @@ -635,7 +670,7 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) { "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " foo" + "admin": "foo" } } }` @@ -1336,7 +1371,7 @@ func TestParseSimpleApi_ForSnakecase(t *testing.T) { "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information" + "admin": "Grants read and write access to administrative information" } }, "OAuth2Application": { @@ -1344,8 +1379,8 @@ func TestParseSimpleApi_ForSnakecase(t *testing.T) { "flow": "application", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Implicit": { @@ -1353,8 +1388,8 @@ func TestParseSimpleApi_ForSnakecase(t *testing.T) { "flow": "implicit", "authorizationUrl": "https://example.com/oauth/authorize", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Password": { @@ -1362,9 +1397,9 @@ func TestParseSimpleApi_ForSnakecase(t *testing.T) { "flow": "password", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "read": " Grants read access", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "read": "Grants read access", + "write": "Grants write access" } } } @@ -1792,7 +1827,7 @@ func TestParseSimpleApi_ForLowerCamelcase(t *testing.T) { "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information" + "admin": "Grants read and write access to administrative information" } }, "OAuth2Application": { @@ -1800,8 +1835,8 @@ func TestParseSimpleApi_ForLowerCamelcase(t *testing.T) { "flow": "application", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Implicit": { @@ -1809,8 +1844,8 @@ func TestParseSimpleApi_ForLowerCamelcase(t *testing.T) { "flow": "implicit", "authorizationUrl": "https://example.com/oauth/authorize", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Password": { @@ -1818,9 +1853,9 @@ func TestParseSimpleApi_ForLowerCamelcase(t *testing.T) { "flow": "password", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "read": " Grants read access", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "read": "Grants read access", + "write": "Grants write access" } } } @@ -2157,11 +2192,26 @@ func TestParseTypeOverrides(t *testing.T) { assert.Equal(t, string(expected), string(b)) } +func TestGlobalSecurity(t *testing.T) { + t.Parallel() + + searchDir := "testdata/global_security" + p := New() + err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) + assert.NoError(t, err) + + expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) + assert.NoError(t, err) + + b, _ := json.MarshalIndent(p.swagger, "", " ") + assert.Equal(t, string(expected), string(b)) +} + func TestParseNested(t *testing.T) { t.Parallel() searchDir := "testdata/nested" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) @@ -2176,7 +2226,7 @@ func TestParseDuplicated(t *testing.T) { t.Parallel() searchDir := "testdata/duplicated" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.Errorf(t, err, "duplicated @id declarations successfully found") } @@ -2185,7 +2235,7 @@ func TestParseDuplicatedOtherMethods(t *testing.T) { t.Parallel() searchDir := "testdata/duplicated2" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.Errorf(t, err, "duplicated @id declarations successfully found") } @@ -2194,7 +2244,7 @@ func TestParseDuplicatedFunctionScoped(t *testing.T) { t.Parallel() searchDir := "testdata/duplicated_function_scoped" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.Errorf(t, err, "duplicated @id declarations successfully found") } @@ -2203,7 +2253,7 @@ func TestParseConflictSchemaName(t *testing.T) { t.Parallel() searchDir := "testdata/conflict_name" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") @@ -2215,7 +2265,7 @@ func TestParseConflictSchemaName(t *testing.T) { func TestParseExternalModels(t *testing.T) { searchDir := "testdata/external_models/main" mainAPIFile := "main.go" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") @@ -2227,7 +2277,7 @@ func TestParseExternalModels(t *testing.T) { func TestParseGoList(t *testing.T) { mainAPIFile := "main.go" - p := New(ParseUsingGoList(true), SetParseDependency(true)) + p := New(ParseUsingGoList(true), SetParseDependency(1)) go111moduleEnv := os.Getenv("GO111MODULE") cases := []struct { @@ -2439,7 +2489,7 @@ type ResponseWrapper struct { } } }` - parser := New(SetParseDependency(true)) + parser := New(SetParseDependency(1)) _ = parser.packages.ParseFile("api", "api/api.go", src, ParseAll) @@ -2905,6 +2955,40 @@ func Test3(){ assert.NotNil(t, val.Delete) } +func TestParser_ParseRouterApiMultiplePathsWithMultipleParams(t *testing.T) { + t.Parallel() + + src := ` +package test + +// @Success 200 +// @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 [get] +// @Router /examples/user/{user_id}/address [get] +func Test(){ +} +` + p := New() + err := p.packages.ParseFile("api", "api/api.go", src, ParseAll) + assert.NoError(t, err) + + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + ps := p.swagger.Paths.Paths + + val, ok := ps["/examples/groups/{group_id}/user/{user_id}/address"] + + assert.True(t, ok) + assert.Equal(t, 2, len(val.Get.Parameters)) + + val, ok = ps["/examples/user/{user_id}/address"] + + assert.True(t, ok) + assert.Equal(t, 1, len(val.Get.Parameters)) +} + // func TestParseDeterministic(t *testing.T) { // mainAPIFile := "main.go" // for _, searchDir := range []string{ @@ -3073,7 +3157,7 @@ func TestParseOutsideDependencies(t *testing.T) { searchDir := "testdata/pare_outside_dependencies" mainAPIFile := "cmd/main.go" - p := New(SetParseDependency(true)) + p := New(SetParseDependency(1)) if err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth); err != nil { t.Error("Failed to parse api: " + err.Error()) } @@ -3249,20 +3333,44 @@ func Fun() { assert.Equal(t, "#/definitions/Teacher", ref.String()) } +func TestParseTabFormattedRenamedStructDefinition(t *testing.T) { + t.Parallel() + + src := "package main\n" + + "\n" + + "type Child struct {\n" + + "\tName string\n" + + "}\t//\t@name\tPupil\n" + + "\n" + + "// @Success 200 {object} Pupil\n" + + "func Fun() { }" + + p := New() + _ = p.packages.ParseFile("api", "api/api.go", src, ParseAll) + _, err := p.packages.ParseTypes() + assert.NoError(t, err) + + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + _, ok := p.swagger.Definitions["Pupil"] + assert.True(t, ok) +} + func TestParseFunctionScopedStructDefinition(t *testing.T) { t.Parallel() src := ` package main -// @Param request body main.Fun.request true "query params" +// @Param request body main.Fun.request true "query params" // @Success 200 {object} main.Fun.response // @Router /test [post] func Fun() { type request struct { Name string } - + type response struct { Name string Child string @@ -3281,7 +3389,7 @@ func Fun() { assert.True(t, ok) } -func TestParseFunctionScopedStructRequestResponseJSON(t *testing.T) { +func TestParseFunctionScopedComplexStructDefinition(t *testing.T) { t.Parallel() src := ` @@ -3295,6 +3403,63 @@ func Fun() { Name string } + type grandChild struct { + Name string + } + + type pointerChild struct { + Name string + } + + type arrayChild struct { + Name string + } + + type child struct { + GrandChild grandChild + PointerChild *pointerChild + ArrayChildren []arrayChild + } + + type response struct { + Children []child + } +} +` + p := New() + _ = p.packages.ParseFile("api", "api/api.go", src, ParseAll) + _, err := p.packages.ParseTypes() + assert.NoError(t, err) + + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + _, ok := p.swagger.Definitions["main.Fun.response"] + assert.True(t, ok) + _, ok = p.swagger.Definitions["main.Fun.child"] + assert.True(t, ok) + _, ok = p.swagger.Definitions["main.Fun.grandChild"] + assert.True(t, ok) + _, ok = p.swagger.Definitions["main.Fun.pointerChild"] + assert.True(t, ok) + _, ok = p.swagger.Definitions["main.Fun.arrayChild"] + assert.True(t, ok) +} + +func TestParseFunctionScopedStructRequestResponseJSON(t *testing.T) { + t.Parallel() + + src := ` +package main + +// @Param request body main.Fun.request true "query params" +// @Success 200 {object} main.Fun.response +// @Router /test [post] +func Fun() { + type request struct { + Name string + } + type response struct { Name string Child string @@ -3366,6 +3531,130 @@ func Fun() { assert.Equal(t, expected, string(b)) } +func TestParseFunctionScopedComplexStructRequestResponseJSON(t *testing.T) { + t.Parallel() + + src := ` +package main + +type PublicChild struct { + Name string +} + +// @Param request body main.Fun.request true "query params" +// @Success 200 {object} main.Fun.response +// @Router /test [post] +func Fun() { + type request struct { + Name string + } + + type grandChild struct { + Name string + } + + type child struct { + GrandChild grandChild + } + + type response struct { + Children []child + PublicChild PublicChild + } +} +` + expected := `{ + "info": { + "contact": {} + }, + "paths": { + "/test": { + "post": { + "parameters": [ + { + "description": "query params", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/main.Fun.request" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.Fun.response" + } + } + } + } + } + }, + "definitions": { + "main.Fun.child": { + "type": "object", + "properties": { + "grandChild": { + "$ref": "#/definitions/main.Fun.grandChild" + } + } + }, + "main.Fun.grandChild": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "main.Fun.request": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "main.Fun.response": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/main.Fun.child" + } + }, + "publicChild": { + "$ref": "#/definitions/main.PublicChild" + } + } + }, + "main.PublicChild": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } +}` + + p := New() + _ = p.packages.ParseFile("api", "api/api.go", src, ParseAll) + + _, err := p.packages.ParseTypes() + assert.NoError(t, err) + + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + b, _ := json.MarshalIndent(p.swagger, "", " ") + assert.Equal(t, expected, string(b)) +} + func TestPackagesDefinitions_CollectAstFileInit(t *testing.T) { t.Parallel() @@ -3756,6 +4045,24 @@ func TestTryAddDescription(t *testing.T) { }, }, }, + { + name: "added description with multiline", + lines: []string{ + "\t@securitydefinitions.apikey test", + "\t@in header", + "\t@name x-api-key", + "\t@description line1", + "\t@description line2", + }, + want: &spec.SecurityScheme{ + SecuritySchemeProps: spec.SecuritySchemeProps{ + Name: "x-api-key", + Type: "apiKey", + In: "header", + Description: "line1\nline2", + }, + }, + }, { name: "no description", lines: []string{ @@ -3975,3 +4282,107 @@ func TestParser_collectionFormat(t *testing.T) { }) } } + +func TestParser_skipPackageByPrefix(t *testing.T) { + t.Parallel() + + parser := New() + + assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag")) + assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/cmd")) + assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/gen")) + + parser = New(SetPackagePrefix("github.com/swaggo/swag/cmd")) + + assert.True(t, parser.skipPackageByPrefix("github.com/swaggo/swag")) + assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/cmd")) + assert.True(t, parser.skipPackageByPrefix("github.com/swaggo/swag/gen")) + + parser = New(SetPackagePrefix("github.com/swaggo/swag/cmd,github.com/swaggo/swag/gen")) + + assert.True(t, parser.skipPackageByPrefix("github.com/swaggo/swag")) + assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/cmd")) + assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/gen")) +} + +func TestParser_ParseRouterApiInFuncBody(t *testing.T) { + t.Parallel() + + src := ` +package test + +func Test(){ + // @Router /api/{id} [get] + _ = func() { + } +} +` + p := New() + p.ParseFuncBody = true + err := p.packages.ParseFile("api", "api/api.go", src, ParseAll) + assert.NoError(t, err) + + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + ps := p.swagger.Paths.Paths + + val, ok := ps["/api/{id}"] + + assert.True(t, ok) + assert.NotNil(t, val.Get) +} + +func TestParser_ParseRouterApiInfoInAndOutFuncBody(t *testing.T) { + t.Parallel() + + src := ` +package test + +// @Router /api/outside [get] +func otherRoute(){ +} + +func Test(){ + // @Router /api/inside [get] + _ = func() { + } +} +` + p := New() + p.ParseFuncBody = true + err := p.packages.ParseFile("api", "api/api.go", src, ParseAll) + assert.NoError(t, err) + + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + ps := p.swagger.Paths.Paths + + val1, ok := ps["/api/outside"] + assert.True(t, ok) + assert.NotNil(t, val1.Get) + + val2, ok := ps["/api/inside"] + assert.True(t, ok) + assert.NotNil(t, val2.Get) +} + +func TestParser_EmbeddedStructAsOtherAliasGoListNested(t *testing.T) { + t.Parallel() + + p := New(SetParseDependency(1), ParseUsingGoList(true)) + + p.parseGoList = true + + searchDir := "testdata/alias_nested" + expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) + assert.NoError(t, err) + + err = p.ParseAPI(searchDir, "cmd/main/main.go", 0) + assert.NoError(t, err) + + b, err := json.MarshalIndent(p.swagger, "", " ") + assert.NoError(t, err) + assert.Equal(t, string(expected), string(b)) +} diff --git a/parserv3.go b/parserv3.go index 14766f877..6e25e7b89 100644 --- a/parserv3.go +++ b/parserv3.go @@ -722,7 +722,7 @@ func (p *Parser) ParseDefinitionV3(typeSpecDef *TypeSpecDef) (*SchemaV3, error) } if definition.Spec.Description == "" { - fillDefinitionDescriptionV3(definition.Spec, typeSpecDef.File, typeSpecDef) + fillDefinitionDescriptionV3(p, definition.Spec, typeSpecDef.File, typeSpecDef) } if len(typeSpecDef.Enums) > 0 { @@ -764,7 +764,7 @@ func (p *Parser) ParseDefinitionV3(typeSpecDef *TypeSpecDef) (*SchemaV3, error) // fillDefinitionDescription additionally fills fields in definition (spec.Schema) // TODO: If .go file contains many types, it may work for a long time -func fillDefinitionDescriptionV3(definition *spec.Schema, file *ast.File, typeSpecDef *TypeSpecDef) { +func fillDefinitionDescriptionV3(parser *Parser, definition *spec.Schema, file *ast.File, typeSpecDef *TypeSpecDef) { for _, astDeclaration := range file.Decls { generalDeclaration, ok := astDeclaration.(*ast.GenDecl) if !ok || generalDeclaration.Tok != token.TYPE { @@ -777,8 +777,18 @@ func fillDefinitionDescriptionV3(definition *spec.Schema, file *ast.File, typeSp continue } - definition.Description = - extractDeclarationDescription(typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc) + var typeName string + if typeSpec.Name != nil { + typeName = typeSpec.Name.Name + } + + text, err := parser.extractDeclarationDescription(typeName, typeSpec.Comment, generalDeclaration.Doc) + if err != nil { + parser.debug.Printf("Error extracting declaration description: %s", err) + continue + } + + definition.Description = text } } } diff --git a/testdata/alias_nested/cmd/main/main.go b/testdata/alias_nested/cmd/main/main.go new file mode 100644 index 000000000..b6a5761ac --- /dev/null +++ b/testdata/alias_nested/cmd/main/main.go @@ -0,0 +1,9 @@ +package main + +import "github.com/swaggo/swag/v2/testdata/alias_nested/pkg/good" + +// @Success 200 {object} good.Gen +// @Router /api [get]. +func main() { + var _ good.Gen +} diff --git a/testdata/alias_nested/expected.json b/testdata/alias_nested/expected.json new file mode 100644 index 000000000..0f719dde0 --- /dev/null +++ b/testdata/alias_nested/expected.json @@ -0,0 +1,38 @@ +{ + "swagger": "2.0", + "info": { + "contact": {} + }, + "paths": { + "/api": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Gen" + } + } + } + } + } + }, + "definitions": { + "Gen": { + "type": "object", + "properties": { + "emb": { + "$ref": "#/definitions/github_com_swaggo_swag_v2_testdata_alias_nested_pkg_good.Emb" + } + } + }, + "github_com_swaggo_swag_v2_testdata_alias_nested_pkg_good.Emb": { + "type": "object", + "properties": { + "good": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/testdata/alias_nested/pkg/bad/data.go b/testdata/alias_nested/pkg/bad/data.go new file mode 100644 index 000000000..795993d15 --- /dev/null +++ b/testdata/alias_nested/pkg/bad/data.go @@ -0,0 +1,5 @@ +package bad + +type Emb struct { + Bad bool `json:"bad"` +} // @name Emb diff --git a/testdata/alias_nested/pkg/good/data.go b/testdata/alias_nested/pkg/good/data.go new file mode 100644 index 000000000..e340b7d38 --- /dev/null +++ b/testdata/alias_nested/pkg/good/data.go @@ -0,0 +1,9 @@ +package good + +type Gen struct { + Emb Emb `json:"emb"` +} // @name Gen + +type Emb struct { + Good bool `json:"good"` +} diff --git a/testdata/deprecated_router/api/api.go b/testdata/deprecated_router/api/api.go new file mode 100644 index 000000000..889613170 --- /dev/null +++ b/testdata/deprecated_router/api/api.go @@ -0,0 +1,17 @@ +package api + +import "net/http" + +// @Description add Foo +// @Deprecated +// @Success 200 {string} string +// @Router /testapi/foo1 [put] +// @Router /testapi/foo1 [post] +// @Router /test/api/foo1 [post] +func AddFoo(w http.ResponseWriter, r *http.Request) {} + +// @Description get Foo +// @Success 200 {string} string +// @Router /testapi/foo1 [get] +// @DeprecatedRouter /test/api/foo1 [get] +func GetFoo(w http.ResponseWriter, r *http.Request) {} diff --git a/testdata/deprecated_router/expected.json b/testdata/deprecated_router/expected.json new file mode 100644 index 000000000..4c473f36b --- /dev/null +++ b/testdata/deprecated_router/expected.json @@ -0,0 +1,75 @@ +{ + "swagger": "2.0", + "info": { + "description": "test data for deprecated router", + "title": "Swagger Example API", + "termsOfService": "http://swagger.io/terms/", + "contact": {}, + "version": "1.0" + }, + "paths": { + "/test/api/foo1": { + "get": { + "description": "get Foo", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "description": "add Foo", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/testapi/foo1": { + "get": { + "description": "get Foo", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "description": "add Foo", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "description": "add Foo", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/testdata/deprecated_router/main.go b/testdata/deprecated_router/main.go new file mode 100644 index 000000000..6f513d409 --- /dev/null +++ b/testdata/deprecated_router/main.go @@ -0,0 +1,9 @@ +package main + +// @title Swagger Example API +// @version 1.0 +// @description test data for deprecated router +// @termsOfService http://swagger.io/terms/ + +func main() { +} diff --git a/testdata/enums/consts/const.go b/testdata/enums/consts/const.go index 27bfb28f5..fd718d121 100644 --- a/testdata/enums/consts/const.go +++ b/testdata/enums/consts/const.go @@ -11,3 +11,4 @@ const nonescapestr = `aa\nbb\u8888cc` const escapestr = "aa\nbb\u8888cc" const escapechar = '\u8888' const underscored = 1_000_000 +const binaryInteger = 0b10001000 diff --git a/testdata/generics_basic/api/api.go b/testdata/generics_basic/api/api.go index bc58fc515..e2b5c01d3 100644 --- a/testdata/generics_basic/api/api.go +++ b/testdata/generics_basic/api/api.go @@ -39,6 +39,7 @@ type Foo = web.GenericResponseMulti[types.Post, types.Post] // @Success 204 {object} Response[string, types.Field[int]] // @Success 205 {object} Response[StringStruct, types.Field[int]] // @Success 206 {object} Response2[string, types.Field[int],string] +// @Success 207 {object} Response[[]map[string]string, map[string][]types.Field[int]] // @Success 222 {object} web.GenericResponseMulti[types.Post, types.Post] // @Failure 400 {object} web.APIError "We need ID!!" // @Failure 404 {object} web.APIError "Can not find ID" diff --git a/testdata/generics_basic/expected.json b/testdata/generics_basic/expected.json index 4aeeec88c..58689cf74 100644 --- a/testdata/generics_basic/expected.json +++ b/testdata/generics_basic/expected.json @@ -81,7 +81,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/web.GenericBodyMulti-array_types_Post-array2_types_Post" + "$ref": "#/definitions/web.GenericBodyMulti-array_types_Post-array_array_types_Post" } } ], @@ -101,7 +101,7 @@ "222": { "description": "", "schema": { - "$ref": "#/definitions/web.GenericResponseMulti-array_types_Post-array2_types_Post" + "$ref": "#/definitions/web.GenericResponseMulti-array_types_Post-array_array_types_Post" } } } @@ -171,6 +171,12 @@ "$ref": "#/definitions/api.Response2-string-types_Field-int-string" } }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/api.Response-array_map_string_string-map_string_array_types_Field-int" + } + }, "222": { "description": "", "schema": { @@ -222,6 +228,26 @@ } } }, + "api.Response-array_map_string_string-map_string_array_types_Field-int": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "meta": { + "$ref": "#/definitions/map_string_array_types.Field-int" + }, + "status": { + "type": "string" + } + } + }, "api.Response-string-types_Field-int": { "type": "object", "properties": { @@ -258,6 +284,15 @@ } } }, + "map_string_array_types.Field-int": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/types.Field-int" + } + } + }, "types.Field-int": { "type": "object", "properties": { @@ -408,7 +443,7 @@ } } }, - "web.GenericBodyMulti-array_types_Post-array2_types_Post": { + "web.GenericBodyMulti-array_types_Post-array_array_types_Post": { "type": "object", "properties": { "data": { @@ -511,7 +546,7 @@ } } }, - "web.GenericResponseMulti-array_types_Post-array2_types_Post": { + "web.GenericResponseMulti-array_types_Post-array_array_types_Post": { "type": "object", "properties": { "data": { diff --git a/testdata/generics_function_scoped/api/api.go b/testdata/generics_function_scoped/api/api.go new file mode 100644 index 000000000..6c0f82856 --- /dev/null +++ b/testdata/generics_function_scoped/api/api.go @@ -0,0 +1,75 @@ +package api + +import ( + "net/http" + + "github.com/swaggo/swag/v2/testdata/generics_function_scoped/types" +) + +// @Summary Generic Response +// @Produce json +// @Success 200 {object} types.GenericResponse[api.GetGeneric.User] +// @Success 201 {object} types.GenericResponse[api.GetGeneric.Post] +// @Router / [get] +func GetGeneric(w http.ResponseWriter, r *http.Request) { + type User struct { + Username int `json:"username"` + Email string `json:"email"` + } + type Post struct { + Slug int `json:"slug"` + Title string `json:"title"` + } + + _ = types.GenericResponse[any]{} +} + +// @Summary Generic Response With Custom Type Names +// @Produce json +// @Success 200 {object} types.GenericResponse[api.GetGenericRenamed.User] +// @Success 201 {object} types.GenericResponse[api.GetGenericRenamed.Post] +// @Router /renamed [get] +func GetGenericRenamed(w http.ResponseWriter, r *http.Request) { + type User struct { + Username int `json:"username"` + Email string `json:"email"` + } // @Name RenamedUserData + type Post struct { + Slug int `json:"slug"` + Title string `json:"title"` + } // @Name RenamedPostData + + _ = types.GenericResponse[any]{} +} + +// @Summary Multiple Generic Response +// @Produce json +// @Success 200 {object} types.GenericMultiResponse[api.GetGenericMulti.MyStructA, api.GetGenericMulti.MyStructB] +// @Success 201 {object} types.GenericMultiResponse[api.GetGenericMulti.MyStructB, api.GetGenericMulti.MyStructA] +// @Router /multi [get] +func GetGenericMulti(w http.ResponseWriter, r *http.Request) { + type MyStructA struct { + SomeFieldA string `json:"some_field_a"` + } + type MyStructB struct { + SomeFieldB string `json:"some_field_b"` + } + + _ = types.GenericMultiResponse[any, any]{} +} + +// @Summary Multiple Generic Response With Custom Type Names +// @Produce json +// @Success 200 {object} types.GenericMultiResponse[api.GetGenericMultiRenamed.MyStructA, api.GetGenericMultiRenamed.MyStructB] +// @Success 201 {object} types.GenericMultiResponse[api.GetGenericMultiRenamed.MyStructB, api.GetGenericMultiRenamed.MyStructA] +// @Router /multi-renamed [get] +func GetGenericMultiRenamed(w http.ResponseWriter, r *http.Request) { + type MyStructA struct { + SomeFieldA string `json:"some_field_a"` + } // @Name NameForMyStructA + type MyStructB struct { + SomeFieldB string `json:"some_field_b"` + } // @Name NameForMyStructB + + _ = types.GenericMultiResponse[any, any]{} +} diff --git a/testdata/generics_function_scoped/expected.json b/testdata/generics_function_scoped/expected.json new file mode 100644 index 000000000..3fb99fa01 --- /dev/null +++ b/testdata/generics_function_scoped/expected.json @@ -0,0 +1,279 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server.", + "title": "Swagger Example API", + "contact": {}, + "version": "1.0" + }, + "host": "localhost:8080", + "basePath": "/api", + "paths": { + "/": { + "get": { + "produces": [ + "application/json" + ], + "summary": "Generic Response", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.GenericResponse-api_GetGeneric_User" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/types.GenericResponse-api_GetGeneric_Post" + } + } + } + } + }, + "/multi": { + "get": { + "produces": [ + "application/json" + ], + "summary": "Multiple Generic Response", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.GenericMultiResponse-api_GetGenericMulti_MyStructA-api_GetGenericMulti_MyStructB" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/types.GenericMultiResponse-api_GetGenericMulti_MyStructB-api_GetGenericMulti_MyStructA" + } + } + } + } + }, + "/multi-renamed": { + "get": { + "produces": [ + "application/json" + ], + "summary": "Multiple Generic Response With Custom Type Names", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.GenericMultiResponse-NameForMyStructA-NameForMyStructB" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/types.GenericMultiResponse-NameForMyStructB-NameForMyStructA" + } + } + } + } + }, + "/renamed": { + "get": { + "produces": [ + "application/json" + ], + "summary": "Generic Response With Custom Type Names", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.GenericResponse-RenamedUserData" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/types.GenericResponse-RenamedPostData" + } + } + } + } + } + }, + "definitions": { + "NameForMyStructA": { + "type": "object", + "properties": { + "some_field_a": { + "type": "string" + } + } + }, + "NameForMyStructB": { + "type": "object", + "properties": { + "some_field_b": { + "type": "string" + } + } + }, + "RenamedPostData": { + "type": "object", + "properties": { + "slug": { + "type": "integer" + }, + "title": { + "type": "string" + } + } + }, + "RenamedUserData": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "username": { + "type": "integer" + } + } + }, + "api.GetGeneric.Post": { + "type": "object", + "properties": { + "slug": { + "type": "integer" + }, + "title": { + "type": "string" + } + } + }, + "api.GetGeneric.User": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "username": { + "type": "integer" + } + } + }, + "api.GetGenericMulti.MyStructA": { + "type": "object", + "properties": { + "some_field_a": { + "type": "string" + } + } + }, + "api.GetGenericMulti.MyStructB": { + "type": "object", + "properties": { + "some_field_b": { + "type": "string" + } + } + }, + "types.GenericMultiResponse-NameForMyStructA-NameForMyStructB": { + "type": "object", + "properties": { + "data_t": { + "$ref": "#/definitions/NameForMyStructA" + }, + "data_x": { + "$ref": "#/definitions/NameForMyStructB" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericMultiResponse-NameForMyStructB-NameForMyStructA": { + "type": "object", + "properties": { + "data_t": { + "$ref": "#/definitions/NameForMyStructB" + }, + "data_x": { + "$ref": "#/definitions/NameForMyStructA" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericMultiResponse-api_GetGenericMulti_MyStructA-api_GetGenericMulti_MyStructB": { + "type": "object", + "properties": { + "data_t": { + "$ref": "#/definitions/api.GetGenericMulti.MyStructA" + }, + "data_x": { + "$ref": "#/definitions/api.GetGenericMulti.MyStructB" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericMultiResponse-api_GetGenericMulti_MyStructB-api_GetGenericMulti_MyStructA": { + "type": "object", + "properties": { + "data_t": { + "$ref": "#/definitions/api.GetGenericMulti.MyStructB" + }, + "data_x": { + "$ref": "#/definitions/api.GetGenericMulti.MyStructA" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericResponse-RenamedPostData": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/RenamedPostData" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericResponse-RenamedUserData": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/RenamedUserData" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericResponse-api_GetGeneric_Post": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/api.GetGeneric.Post" + }, + "status": { + "type": "string" + } + } + }, + "types.GenericResponse-api_GetGeneric_User": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/api.GetGeneric.User" + }, + "status": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/testdata/generics_function_scoped/main.go b/testdata/generics_function_scoped/main.go new file mode 100644 index 000000000..52319d4df --- /dev/null +++ b/testdata/generics_function_scoped/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "net/http" + + "github.com/swaggo/swag/v2/testdata/generics_function_scoped/api" +) + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server. +// @host localhost:8080 +// @basePath /api +func main() { + http.HandleFunc("/", api.GetGeneric) + http.HandleFunc("/renamed", api.GetGenericRenamed) + http.HandleFunc("/multi", api.GetGenericMulti) + http.HandleFunc("/multi-renamed", api.GetGenericMulti) + http.ListenAndServe(":8080", nil) +} diff --git a/testdata/generics_function_scoped/types/response.go b/testdata/generics_function_scoped/types/response.go new file mode 100644 index 000000000..d12e5324c --- /dev/null +++ b/testdata/generics_function_scoped/types/response.go @@ -0,0 +1,12 @@ +package types + +type GenericResponse[T any] struct { + Status string `json:"status"` + Data T `json:"data"` +} + +type GenericMultiResponse[T any, X any] struct { + Status string `json:"status"` + DataT T `json:"data_t"` + DataX X `json:"data_x"` +} diff --git a/testdata/generics_names/expected.json b/testdata/generics_names/expected.json index 741b2455d..5da1de542 100644 --- a/testdata/generics_names/expected.json +++ b/testdata/generics_names/expected.json @@ -63,7 +63,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/MultiBody-array_Post-array2_Post" + "$ref": "#/definitions/MultiBody-array_Post-array_array_Post" } } ], @@ -77,7 +77,7 @@ "222": { "description": "", "schema": { - "$ref": "#/definitions/MultiResponse-array_Post-array2_Post" + "$ref": "#/definitions/MultiResponse-array_Post-array_array_Post" } } } @@ -173,7 +173,7 @@ } } }, - "MultiBody-array_Post-array2_Post": { + "MultiBody-array_Post-array_array_Post": { "type": "object", "properties": { "data": { @@ -207,7 +207,7 @@ } } }, - "MultiResponse-array_Post-array2_Post": { + "MultiResponse-array_Post-array_array_Post": { "type": "object", "properties": { "data": { diff --git a/testdata/generics_nested/expected.json b/testdata/generics_nested/expected.json index 51b53a1c5..2dfe1e0ee 100644 --- a/testdata/generics_nested/expected.json +++ b/testdata/generics_nested/expected.json @@ -119,7 +119,7 @@ "205": { "description": "Reset Content", "schema": { - "$ref": "#/definitions/web.GenericNestedResponseMulti-types_Post-web_GenericInnerMultiType-types_Post-array_web_GenericInnerType-array2_types_Post" + "$ref": "#/definitions/web.GenericNestedResponseMulti-types_Post-web_GenericInnerMultiType-types_Post-array_web_GenericInnerType-array_array_types_Post" } }, "222": { @@ -203,7 +203,7 @@ } } }, - "web.GenericInnerMultiType-types_Post-array_web_GenericInnerType-array2_types_Post": { + "web.GenericInnerMultiType-types_Post-array_web_GenericInnerType-array_array_types_Post": { "type": "object", "properties": { "itemOne": { @@ -220,7 +220,7 @@ "items": { "type": "array", "items": { - "$ref": "#/definitions/web.GenericInnerType-array2_types_Post" + "$ref": "#/definitions/web.GenericInnerType-array_array_types_Post" } } } @@ -266,7 +266,7 @@ } } }, - "web.GenericInnerType-array2_types_Post": { + "web.GenericInnerType-array_array_types_Post": { "type": "object", "properties": { "items": { @@ -478,7 +478,7 @@ } } }, - "web.GenericNestedResponseMulti-types_Post-web_GenericInnerMultiType-types_Post-array_web_GenericInnerType-array2_types_Post": { + "web.GenericNestedResponseMulti-types_Post-web_GenericInnerMultiType-types_Post-array_web_GenericInnerType-array_array_types_Post": { "type": "object", "properties": { "itemOne": { @@ -493,7 +493,7 @@ "description": "ItemsTwo is the second thing", "type": "array", "items": { - "$ref": "#/definitions/web.GenericInnerMultiType-types_Post-array_web_GenericInnerType-array2_types_Post" + "$ref": "#/definitions/web.GenericInnerMultiType-types_Post-array_web_GenericInnerType-array_array_types_Post" } }, "status": { diff --git a/testdata/generics_property/expected.json b/testdata/generics_property/expected.json index 490909ee0..fe67258a1 100644 --- a/testdata/generics_property/expected.json +++ b/testdata/generics_property/expected.json @@ -213,7 +213,7 @@ } }, "value4": { - "$ref": "#/definitions/types.SubField1-api_Person-string" + "$ref": "#/definitions/types.SubField1-array_api_Person-string" } } }, @@ -242,7 +242,7 @@ } }, "value4": { - "$ref": "#/definitions/types.SubField1-types_Post-string" + "$ref": "#/definitions/types.SubField1-array_types_Post-string" } } }, @@ -348,44 +348,61 @@ } } }, - "types.SubField1-string-string": { + "types.SubField1-array_api_Person-string": { "type": "object", "properties": { "subValue1": { + "type": "array", + "items": { + "$ref": "#/definitions/api.Person" + } + }, + "subValue2": { "type": "string" + } + } + }, + "types.SubField1-array_types_Post-string": { + "type": "object", + "properties": { + "subValue1": { + "type": "array", + "items": { + "$ref": "#/definitions/types.Post" + } }, "subValue2": { "type": "string" } } }, - "types.SubField1-types_Field-api_Person-string": { + "types.SubField1-string-string": { "type": "object", "properties": { "subValue1": { - "$ref": "#/definitions/types.Field-api_Person" + "type": "string" }, "subValue2": { "type": "string" } } }, - "types.SubField1-types_Field-string-string": { + "types.SubField1-types_Field-api_Person-string": { "type": "object", "properties": { "subValue1": { - "$ref": "#/definitions/types.Field-string" + "$ref": "#/definitions/types.Field-api_Person" }, "subValue2": { "type": "string" } } }, - "types.SubField1-types_Post-string": { + "types.SubField1-types_Field-string-string": { "type": "object", "properties": { "subValue1": { - "$ref": "#/definitions/types.Post" + "$ref": "#/definitions/types.Field-string" }, "subValue2": { "type": "string" diff --git a/testdata/global_security/api/api.go b/testdata/global_security/api/api.go new file mode 100644 index 000000000..8b8741c57 --- /dev/null +++ b/testdata/global_security/api/api.go @@ -0,0 +1,34 @@ +package api + +import ( + "net/http" +) + +// @Summary default security +// @Success 200 +// @Router /testapi/application [get] +func GetApplication(w http.ResponseWriter, r *http.Request) {} + +// @Summary no security +// @Security +// @Success 200 +// @Router /testapi/nosec [get] +func GetNoSec(w http.ResponseWriter, r *http.Request) {} + +// @Summary basic security +// @Security BasicAuth +// @Success 200 +// @Router /testapi/basic [get] +func GetBasic(w http.ResponseWriter, r *http.Request) {} + +// @Summary oauth2 write +// @Security OAuth2Application[write] +// @Success 200 +// @Router /testapi/oauth/write [get] +func GetOAuthWrite(w http.ResponseWriter, r *http.Request) {} + +// @Summary oauth2 admin +// @Security OAuth2Application[admin] +// @Success 200 +// @Router /testapi/oauth/admin [get] +func GetOAuthAdmin(w http.ResponseWriter, r *http.Request) {} diff --git a/testdata/global_security/expected.json b/testdata/global_security/expected.json new file mode 100644 index 000000000..c79eb5a99 --- /dev/null +++ b/testdata/global_security/expected.json @@ -0,0 +1,105 @@ +{ + "swagger": "2.0", + "info": { + "title": "Swagger Example API", + "contact": {}, + "version": "1.0" + }, + "paths": { + "/testapi/application": { + "get": { + "summary": "default security", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/basic": { + "get": { + "security": [ + { + "BasicAuth": [] + } + ], + "summary": "basic security", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/nosec": { + "get": { + "security": [], + "summary": "no security", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/oauth/admin": { + "get": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "summary": "oauth2 admin", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/oauth/write": { + "get": { + "security": [ + { + "OAuth2Application": [ + "write" + ] + } + ], + "summary": "oauth2 write", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "securityDefinitions": { + "APIKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + }, + "BasicAuth": { + "type": "basic" + }, + "OAuth2Application": { + "type": "oauth2", + "flow": "application", + "tokenUrl": "https://example.com/oauth/token", + "scopes": { + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" + } + } + }, + "security": [ + { + "APIKeyAuth": [], + "OAuth2Application": [] + } + ] +} \ No newline at end of file diff --git a/testdata/global_security/main.go b/testdata/global_security/main.go new file mode 100644 index 000000000..83484bac8 --- /dev/null +++ b/testdata/global_security/main.go @@ -0,0 +1,18 @@ +package global_security + +// @title Swagger Example API +// @version 1.0 + +// @securityDefinitions.apikey APIKeyAuth +// @in header +// @name Authorization + +// @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 + +// @security APIKeyAuth || OAuth2Application +func main() {} diff --git a/testdata/param_structs/structs.go b/testdata/param_structs/structs.go new file mode 100644 index 000000000..2c8673a5d --- /dev/null +++ b/testdata/param_structs/structs.go @@ -0,0 +1,20 @@ +package structs + +type FormModel struct { + Foo string `form:"f" binding:"required" validate:"max=10"` + // B is another field + B bool +} + +type AuthHeader struct { + // Token is the auth token + Token string `header:"X-Auth-Token" binding:"required"` + // AnotherHeader is another header + AnotherHeader int `validate:"gte=0,lte=10"` +} + +type PathModel struct { + // ID is the id + Identifier int `uri:"id" binding:"required"` + Name string `validate:"max=10"` +} diff --git a/testdata/simple/api/api.go b/testdata/simple/api/api.go index 7eee24347..7eb81a40e 100644 --- a/testdata/simple/api/api.go +++ b/testdata/simple/api/api.go @@ -138,3 +138,20 @@ func GetPet6FunctionScopedResponse() { Name string } } + +// @Success 200 {object} api.GetPet6FunctionScopedComplexResponse.response "ok" +// @Router /GetPet6FunctionScopedComplexResponse [get] +func GetPet6FunctionScopedComplexResponse() { + type pet struct { + Name string + } + + type pointerPet struct { + Name string + } + + type response struct { + Pets []pet + PointerPet *pointerPet + } +} diff --git a/testdata/simple/expected.json b/testdata/simple/expected.json index 24ce9f696..e1bcadb7d 100644 --- a/testdata/simple/expected.json +++ b/testdata/simple/expected.json @@ -101,6 +101,18 @@ } } }, + "/GetPet6FunctionScopedComplexResponse": { + "get": { + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/api.GetPet6FunctionScopedComplexResponse.response" + } + } + } + } + }, "/GetPet6FunctionScopedResponse": { "get": { "responses": { @@ -401,6 +413,36 @@ } }, "definitions": { + "api.GetPet6FunctionScopedComplexResponse.pet": { + "type": "object", + "properties": { + "Name": { + "type": "string" + } + } + }, + "api.GetPet6FunctionScopedComplexResponse.pointerPet": { + "type": "object", + "properties": { + "Name": { + "type": "string" + } + } + }, + "api.GetPet6FunctionScopedComplexResponse.response": { + "type": "object", + "properties": { + "Pets": { + "type": "array", + "items": { + "$ref": "#/definitions/api.GetPet6FunctionScopedComplexResponse.pet" + } + }, + "PointerPet": { + "$ref": "#/definitions/api.GetPet6FunctionScopedComplexResponse.pointerPet" + } + } + }, "api.GetPet6FunctionScopedResponse.response": { "type": "object", "properties": { @@ -781,7 +823,7 @@ "authorizationUrl": "https://example.com/oauth/authorize", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information" + "admin": "Grants read and write access to administrative information" } }, "OAuth2Application": { @@ -789,8 +831,8 @@ "flow": "application", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Implicit": { @@ -798,8 +840,8 @@ "flow": "implicit", "authorizationUrl": "https://example.com/oauth/authorize", "scopes": { - "admin": " Grants read and write access to administrative information", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "write": "Grants write access" } }, "OAuth2Password": { @@ -807,9 +849,9 @@ "flow": "password", "tokenUrl": "https://example.com/oauth/token", "scopes": { - "admin": " Grants read and write access to administrative information", - "read": " Grants read access", - "write": " Grants write access" + "admin": "Grants read and write access to administrative information", + "read": "Grants read access", + "write": "Grants write access" } } } diff --git a/testdata/state/admin_expected.json b/testdata/state/admin_expected.json new file mode 100644 index 000000000..d5f3a3da5 --- /dev/null +++ b/testdata/state/admin_expected.json @@ -0,0 +1,396 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server.", + "title": "Swagger Example API", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "host": "petstore-admin.swagger.io", + "basePath": "/v3", + "paths": { + "/admin/file/upload": { + "post": { + "description": "Upload file", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "summary": "Upload file", + "operationId": "admin.file.upload", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/admin/testapi/get-string-by-int/{some_id}": { + "get": { + "description": "get string by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Add a new pet to the store", + "operationId": "admin.get-string-by-int", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "Some ID", + "name": "some_id", + "in": "path", + "required": true + }, + { + "description": "Some ID", + "name": "some_id", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/web.Pet" + } + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/admin/testapi/get-struct-array-by-string/{some_id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BasicAuth": [] + }, + { + "OAuth2Application": [ + "write" + ] + }, + { + "OAuth2Implicit": [ + "read", + "admin" + ] + }, + { + "OAuth2AccessCode": [ + "read" + ] + }, + { + "OAuth2Password": [ + "admin" + ] + } + ], + "description": "get struct array by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "operationId": "admin.get-struct-array-by-string", + "parameters": [ + { + "type": "string", + "description": "Some ID", + "name": "some_id", + "in": "path", + "required": true + }, + { + "enum": [ + 1, + 2, + 3 + ], + "type": "integer", + "description": "Category", + "name": "category", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "default": 0, + "description": "Offset", + "name": "offset", + "in": "query", + "required": true + }, + { + "maximum": 50, + "type": "integer", + "default": 10, + "description": "Limit", + "name": "limit", + "in": "query", + "required": true + }, + { + "maxLength": 50, + "minLength": 1, + "type": "string", + "default": "\"\"", + "description": "q", + "name": "q", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + } + }, + "definitions": { + "web.APIError": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorMessage": { + "type": "string" + } + } + }, + "web.Pet": { + "type": "object", + "properties": { + "category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "category_name" + }, + "photoURLs": { + "type": "array", + "items": { + "type": "string", + "format": "url" + }, + "example": [ + "http://test/image/1.jpg", + "http://test/image/2.jpg" + ] + }, + "smallCategory": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "detail_category_name" + }, + "photoURLs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "http://test/image/1.jpg", + "http://test/image/2.jpg" + ] + } + } + } + } + }, + "data": {}, + "decimal": { + "type": "number" + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "isAlive": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "poti" + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Pet2" + } + }, + "pets2": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Pet2" + } + }, + "photoURLs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "http://test/image/1.jpg", + "http://test/image/2.jpg" + ] + }, + "price": { + "type": "number", + "multipleOf": 0.01, + "example": 3.25 + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Tag" + } + }, + "uuid": { + "type": "string" + } + } + }, + "web.Pet2": { + "type": "object", + "properties": { + "deletedAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "middleName": { + "type": "string" + } + } + }, + "web.RevValue": { + "type": "object", + "properties": { + "data": { + "type": "integer" + }, + "err": { + "type": "integer" + }, + "status": { + "type": "boolean" + } + } + }, + "web.Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Pet" + } + } + } + } + } +} diff --git a/testdata/state/api/api.go b/testdata/state/api/api.go new file mode 100644 index 000000000..71718e804 --- /dev/null +++ b/testdata/state/api/api.go @@ -0,0 +1,76 @@ +package api + +import "net/http" + +// @State admin +// @Summary Add a new pet to the store +// @Description get string by ID +// @ID admin.get-string-by-int +// @Accept json +// @Produce json +// @Param some_id path int true "Some ID" Format(int64) +// @Param some_id body web.Pet true "Some ID" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /admin/testapi/get-string-by-int/{some_id} [get] +func GetStringByInt(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// @State admin +// @Description get struct array by ID +// @ID admin.get-struct-array-by-string +// @Accept json +// @Produce json +// @Param some_id path string true "Some ID" +// @Param category query int true "Category" Enums(1, 2, 3) +// @Param offset query int true "Offset" Minimum(0) default(0) +// @Param limit query int true "Limit" Maximum(50) default(10) +// @Param q query string true "q" Minlength(1) Maxlength(50) default("") +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Security ApiKeyAuth +// @Security BasicAuth +// @Security OAuth2Application[write] +// @Security OAuth2Implicit[read, admin] +// @Security OAuth2AccessCode[read] +// @Security OAuth2Password[admin] +// @Router /admin/testapi/get-struct-array-by-string/{some_id} [get] +func GetStructArrayByString(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// @State admin +// @Summary Upload file +// @Description Upload file +// @ID admin.file.upload +// @Accept multipart/form-data +// @Produce json +// @Param file formData file true "this is a test file" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /admin/file/upload [post] +func Upload(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// @State admin +// @Summary use Anonymous field +// @Success 200 {object} web.RevValue "ok" +func AnonymousField() { + +} + +// @State admin +// @Summary use pet2 +// @Success 200 {object} web.Pet2 "ok" +func Pet2() { + +} + +type Pet3 struct { + ID int `json:"id"` +} diff --git a/testdata/state/api/api_user.go b/testdata/state/api/api_user.go new file mode 100644 index 000000000..014541998 --- /dev/null +++ b/testdata/state/api/api_user.go @@ -0,0 +1,76 @@ +package api + +import "net/http" + +// @State user +// @Summary Add a new pet to the store +// @Description get string by ID +// @ID get-string-by-int +// @Accept json +// @Produce json +// @Param some_id path int true "Some ID" Format(int64) +// @Param some_id body web.Pet true "Some ID" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /testapi/get-string-by-int/{some_id} [get] +func GetStringByIntUser(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// @State user +// @Description get struct array by ID +// @ID get-struct-array-by-string +// @Accept json +// @Produce json +// @Param some_id path string true "Some ID" +// @Param category query int true "Category" Enums(1, 2, 3) +// @Param offset query int true "Offset" Minimum(0) default(0) +// @Param limit query int true "Limit" Maximum(50) default(10) +// @Param q query string true "q" Minlength(1) Maxlength(50) default("") +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Security ApiKeyAuth +// @Security BasicAuth +// @Security OAuth2Application[write] +// @Security OAuth2Implicit[read, admin] +// @Security OAuth2AccessCode[read] +// @Security OAuth2Password[admin] +// @Router /testapi/get-struct-array-by-string/{some_id} [get] +func GetStructArrayByStringUser(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// @State user +// @Summary Upload file +// @Description Upload file +// @ID file.upload +// @Accept multipart/form-data +// @Produce json +// @Param file formData file true "this is a test file" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.APIError "We need ID!!" +// @Failure 404 {object} web.APIError "Can not find ID" +// @Router /file/upload [post] +func UploadUser(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// @State user +// @Summary use Anonymous field +// @Success 200 {object} web.RevValue "ok" +func AnonymousFieldUser() { + +} + +// @State user +// @Summary use pet2 +// @Success 200 {object} web.Pet2 "ok" +func Pet2User() { + +} + +type Pet3User struct { + ID int `json:"id"` +} diff --git a/testdata/state/main.go b/testdata/state/main.go new file mode 100644 index 000000000..97cdd6666 --- /dev/null +++ b/testdata/state/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "net/http" + + "github.com/swaggo/swag/testdata/state/api" +) + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server Petstore server. +// @termsOfService http://swagger.io/terms/ + +// @contact.name API Support +// @contact.url http://www.swagger.io/support +// @contact.email support@swagger.io + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @hostState admin petstore-admin.swagger.io +// @hostState user petstore-user.swagger.io +// @BasePath /v3 +func main() { + state := "admin" // "admin" or "user" + switch state { + case "admin": + http.HandleFunc("/admin/testapi/get-string-by-int/", api.GetStringByInt) + http.HandleFunc("/admin/testapi/get-struct-array-by-string/", api.GetStructArrayByString) + http.HandleFunc("/admin/testapi/upload", api.Upload) + http.ListenAndServe(":8080", nil) + case "user": + http.HandleFunc("/testapi/get-string-by-int/", api.GetStringByIntUser) + http.HandleFunc("/testapi/get-struct-array-by-string/", api.GetStructArrayByStringUser) + http.HandleFunc("/testapi/upload", api.UploadUser) + http.ListenAndServe(":8080", nil) + } +} diff --git a/testdata/state/user_expected.json b/testdata/state/user_expected.json new file mode 100644 index 000000000..18431c031 --- /dev/null +++ b/testdata/state/user_expected.json @@ -0,0 +1,396 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server.", + "title": "Swagger Example API", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "host": "petstore-user.swagger.io", + "basePath": "/v3", + "paths": { + "/file/upload": { + "post": { + "description": "Upload file", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "summary": "Upload file", + "operationId": "file.upload", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/testapi/get-string-by-int/{some_id}": { + "get": { + "description": "get string by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Add a new pet to the store", + "operationId": "get-string-by-int", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "Some ID", + "name": "some_id", + "in": "path", + "required": true + }, + { + "description": "Some ID", + "name": "some_id", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/web.Pet" + } + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/testapi/get-struct-array-by-string/{some_id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BasicAuth": [] + }, + { + "OAuth2Application": [ + "write" + ] + }, + { + "OAuth2Implicit": [ + "read", + "admin" + ] + }, + { + "OAuth2AccessCode": [ + "read" + ] + }, + { + "OAuth2Password": [ + "admin" + ] + } + ], + "description": "get struct array by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "operationId": "get-struct-array-by-string", + "parameters": [ + { + "type": "string", + "description": "Some ID", + "name": "some_id", + "in": "path", + "required": true + }, + { + "enum": [ + 1, + 2, + 3 + ], + "type": "integer", + "description": "Category", + "name": "category", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "default": 0, + "description": "Offset", + "name": "offset", + "in": "query", + "required": true + }, + { + "maximum": 50, + "type": "integer", + "default": 10, + "description": "Limit", + "name": "limit", + "in": "query", + "required": true + }, + { + "maxLength": 50, + "minLength": 1, + "type": "string", + "default": "\"\"", + "description": "q", + "name": "q", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + } + }, + "definitions": { + "web.APIError": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorMessage": { + "type": "string" + } + } + }, + "web.Pet": { + "type": "object", + "properties": { + "category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "category_name" + }, + "photoURLs": { + "type": "array", + "items": { + "type": "string", + "format": "url" + }, + "example": [ + "http://test/image/1.jpg", + "http://test/image/2.jpg" + ] + }, + "smallCategory": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "detail_category_name" + }, + "photoURLs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "http://test/image/1.jpg", + "http://test/image/2.jpg" + ] + } + } + } + } + }, + "data": {}, + "decimal": { + "type": "number" + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "isAlive": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "poti" + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Pet2" + } + }, + "pets2": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Pet2" + } + }, + "photoURLs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "http://test/image/1.jpg", + "http://test/image/2.jpg" + ] + }, + "price": { + "type": "number", + "multipleOf": 0.01, + "example": 3.25 + }, + "status": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Tag" + } + }, + "uuid": { + "type": "string" + } + } + }, + "web.Pet2": { + "type": "object", + "properties": { + "deletedAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "middleName": { + "type": "string" + } + } + }, + "web.RevValue": { + "type": "object", + "properties": { + "data": { + "type": "integer" + }, + "err": { + "type": "integer" + }, + "status": { + "type": "boolean" + } + } + }, + "web.Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/definitions/web.Pet" + } + } + } + } + } +} diff --git a/testdata/state/web/handler.go b/testdata/state/web/handler.go new file mode 100644 index 000000000..61da78c50 --- /dev/null +++ b/testdata/state/web/handler.go @@ -0,0 +1,64 @@ +package web + +import ( + "time" + + uuid "github.com/gofrs/uuid" + "github.com/shopspring/decimal" +) + +type Pet struct { + ID int `example:"1" format:"int64"` + Category struct { + ID int `example:"1"` + Name string `example:"category_name"` + PhotoURLs []string `example:"http://test/image/1.jpg,http://test/image/2.jpg" format:"url"` + SmallCategory struct { + ID int `example:"1"` + Name string `example:"detail_category_name"` + PhotoURLs []string `example:"http://test/image/1.jpg,http://test/image/2.jpg"` + } + } + Name string `example:"poti"` + PhotoURLs []string `example:"http://test/image/1.jpg,http://test/image/2.jpg"` + Tags []Tag + Pets *[]Pet2 + Pets2 []*Pet2 + Status string + Price float32 `example:"3.25" multipleOf:"0.01"` + IsAlive bool `example:"true"` + Data interface{} + Hidden string `json:"-"` + UUID uuid.UUID + Decimal decimal.Decimal + Function func() +} + +type Tag struct { + ID int `format:"int64"` + Name string + Pets []Pet +} + +type Pet2 struct { + ID int + MiddleName *string + DeletedAt *time.Time +} + +type APIError struct { + ErrorCode int + ErrorMessage string + CreatedAt time.Time +} + +type RevValueBase struct { + Status bool + + Err int32 +} +type RevValue struct { + RevValueBase + + Data int +} diff --git a/types.go b/types.go index 8e2f51d87..5f3031e0b 100644 --- a/types.go +++ b/types.go @@ -3,6 +3,7 @@ package swag import ( "go/ast" "go/token" + "regexp" "strings" "github.com/go-openapi/spec" @@ -29,12 +30,14 @@ type TypeSpecDef struct { PkgPath string ParentSpec ast.Decl + SchemaName string + NotUnique bool } // Name the name of the typeSpec. func (t *TypeSpecDef) Name() string { - if t.TypeSpec != nil { + if t.TypeSpec != nil && t.TypeSpec.Name != nil { return t.TypeSpec.Name.Name } @@ -45,14 +48,6 @@ func (t *TypeSpecDef) Name() string { 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 ' - for _, comment := range t.TypeSpec.Comment.List { - texts := strings.Split(strings.TrimSpace(strings.TrimLeft(comment.Text, "/")), " ") - if len(texts) > 1 && strings.ToLower(texts[0]) == "@name" { - return texts[1] - } - } } var names []string @@ -64,7 +59,7 @@ func (t *TypeSpecDef) TypeName() string { return r }, t.PkgPath) names = append(names, pkgPath) - } else { + } else if t.File != nil { names = append(names, t.File.Name.Name) } if parentFun, ok := (t.ParentSpec).(*ast.FuncDecl); ok && parentFun != nil { @@ -79,6 +74,36 @@ func (t *TypeSpecDef) FullPath() string { return t.PkgPath + "." + t.Name() } +const regexCaseInsensitive = "(?i)" + +var reTypeName = regexp.MustCompile(regexCaseInsensitive + `^@name\s+(\S+)`) + +func (t *TypeSpecDef) Alias() string { + if t.TypeSpec.Comment == nil { + return "" + } + + // get alias from comment '// @name ' + 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] + } + } + + return "" +} + +func (t *TypeSpecDef) SetSchemaName() { + if alias := t.Alias(); alias != "" { + t.SchemaName = alias + return + } + + t.SchemaName = t.TypeName() +} + // AstFileInfo information of an ast.File. type AstFileInfo struct { //FileSet the FileSet object which is used to parse this go source file