From ae312cc3b6ad786f2dd445a7b6108ec5254ce85e Mon Sep 17 00:00:00 2001 From: yaoxianjie Date: Fri, 3 Jun 2022 17:48:11 +0800 Subject: [PATCH 1/6] feat: add swagger middleware --- .gitignore | 18 +- NOTICE | 5 + README.md | 169 ++++++++++++++++ example/basic/docs/docs.go | 56 ++++++ example/basic/docs/swagger.json | 36 ++++ example/basic/docs/swagger.yaml | 25 +++ example/basic/main.go | 75 +++++++ go.mod | 39 ++++ go.sum | 130 ++++++++++++ licenses/LICENSE-swaggo.txt | 21 ++ swagger.go | 347 ++++++++++++++++++++++++++++++++ swagger_test.go | 201 ++++++++++++++++++ 12 files changed, 1121 insertions(+), 1 deletion(-) create mode 100644 NOTICE create mode 100644 README.md create mode 100644 example/basic/docs/docs.go create mode 100644 example/basic/docs/swagger.json create mode 100644 example/basic/docs/swagger.yaml create mode 100644 example/basic/main.go create mode 100644 go.sum create mode 100644 licenses/LICENSE-swaggo.txt create mode 100644 swagger.go create mode 100644 swagger_test.go diff --git a/.gitignore b/.gitignore index 62c8935..398baf2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,17 @@ -.idea/ \ No newline at end of file +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +.idea diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..bbb12ae --- /dev/null +++ b/NOTICE @@ -0,0 +1,5 @@ +CloudWeGo +Copyright 2022 CloudWeGo authors. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..229345f --- /dev/null +++ b/README.md @@ -0,0 +1,169 @@ +# Hertz-swagger + +hertz middleware to automatically generate RESTful API documentation with Swagger 2.0. + +## Usage + +### Start using it + +1. Add comments to your API source code, [See Declarative Comments Format](https://github.com/swaggo/swag/blob/master/README.md#declarative-comments-format). +2. Download [Swag](https://github.com/swaggo/swag) for Go by using: + +```sh +go get -u github.com/swaggo/swag/cmd/swag +``` + +Starting in Go 1.17, installing executables with `go get` is deprecated. `go install` may be used instead: + +```sh +go install github.com/swaggo/swag/cmd/swag +``` + +3. Run the [Swag](https://github.com/swaggo/swag) at your Go project root path(for instance `~/root/go-peoject-name`), + [Swag](https://github.com/swaggo/swag) will parse comments and generate required files(`docs` folder and `docs/doc.go`) + at `~/root/go-peoject-name/docs`. + +```sh +swag init +``` + +4. Download [hertz-swagger](https://github.com/hertz-contrib/swagger) by using: + +```sh +go get -u github.com/hertz-contrib/swagger +go get -u github.com/swaggo/files +``` + +Import following in your code: + +```go +import "github.com/hertz-contrib/swagger" // hertz-swagger middleware +import "github.com/swaggo/files" // swagger embed files + +``` + +### Canonical example: + +Now assume you have implemented a simple api as following: + +```go +func PingHandler(c context.Context, ctx *app.RequestContext) { + ctx.JSON(200, map[string]string{ + "ping": "pong", + }) +} + +``` + +So how to use hertz-swagger on api above? Just follow the following guide. + +1. Add Comments for apis and main function with hertz-swagger rules like following: + +```go +// PingHandler 测试handler +// @Summary 测试Summary +// @Description 测试Description +// @Accept application/json +// @Produce application/json +// @Router /ping [get] +func PingHandler(c context.Context, ctx *app.RequestContext) { + ctx.JSON(200, map[string]string{ + "ping": "pong", + }) +} +``` + +2. Use `swag init` command to generate a docs, docs generated will be stored at `docs/`. +3. import the docs like this: + I assume your project named `github.com/go-project-name/docs`. + +```go +import ( + docs "github.com/go-project-name/docs" +) +``` + +4. build your application and after that, go to http://localhost:8888/swagger/index.html ,you to see your Swagger UI. + +5. The full code and folder relatives here: + +```go +package main + +import ( + "context" + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + hertzSwagger "github.com/hertz-contrib/swagger" + _ "github.com/hertz-contrib/swagger/example/basic/docs" + swaggerFiles "github.com/swaggo/files" +) + +// PingHandler 测试handler +// @Summary 测试Summary +// @Description 测试Description +// @Accept application/json +// @Produce application/json +// @Router /ping [get] +func PingHandler(c context.Context, ctx *app.RequestContext) { + ctx.JSON(200, map[string]string{ + "ping": "pong", + }) +} + +// @title HertzTest +// @version 1.0 +// @description This is a demo using Hertz. + +// @contact.name hertz-contrib +// @contact.url https://github.com/hertz-contrib + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host localhost:8888 +// @BasePath / +// @schemes http +func main() { + h := server.Default() + + h.GET("/ping", PingHandler) + + url := hertzSwagger.URL("http://localhost:8888/swagger/doc.json") // The url pointing to API definition + h.GET("/swagger/*any", hertzSwagger.WrapHandler(swaggerFiles.Handler, url)) + + h.Spin() +} + +``` + +Demo project tree, `swag init` is run at relative `.` + +``` +. +├── docs +│   ├── docs.go +│   ├── swagger.json +│   └── swagger.yaml +├── go.mod +├── go.sum +└── main.go +``` + +## Multiple APIs +This feature was introduced in swag v1.7.9 + +## Configuration + +You can configure Swagger using different configuration options + + +| Option | Type | Default | Description | +| ------------------------ | ------ | ---------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| URL | string | "doc.json" | URL pointing to API definition | +| DocExpansion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). | +| DeepLinking | bool | true | If set to true, enables deep linking for tags and operations. See the Deep Linking documentation for more information. | +| DefaultModelsExpandDepth | int | 1 | Default expansion depth for models (set to -1 completely hide the models). | +| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one hertz router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_). | +| PersistAuthorization | bool | false | If set to true, it persists authorization data and it would not be lost on browser close/refresh. | +| Oauth2DefaultClientID | string | "" | If set, it's used to prepopulate the *client_id* field of the OAuth2 Authorization dialog. | diff --git a/example/basic/docs/docs.go b/example/basic/docs/docs.go new file mode 100644 index 0000000..882adfc --- /dev/null +++ b/example/basic/docs/docs.go @@ -0,0 +1,56 @@ +// Package docs GENERATED BY SWAG; DO NOT EDIT +// This file was generated by swaggo/swag +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "hertz-contrib", + "url": "https://github.com/hertz-contrib" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/ping": { + "get": { + "description": "测试Description", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "测试Summary", + "responses": {} + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "localhost:8888", + BasePath: "/", + Schemes: []string{"http"}, + Title: "HertzTest", + Description: "This is a demo using Hertz.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/example/basic/docs/swagger.json b/example/basic/docs/swagger.json new file mode 100644 index 0000000..f891f39 --- /dev/null +++ b/example/basic/docs/swagger.json @@ -0,0 +1,36 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "This is a demo using Hertz.", + "title": "HertzTest", + "contact": { + "name": "hertz-contrib", + "url": "https://github.com/hertz-contrib" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "host": "localhost:8888", + "basePath": "/", + "paths": { + "/ping": { + "get": { + "description": "测试Description", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "测试Summary", + "responses": {} + } + } + } +} \ No newline at end of file diff --git a/example/basic/docs/swagger.yaml b/example/basic/docs/swagger.yaml new file mode 100644 index 0000000..8a1aba6 --- /dev/null +++ b/example/basic/docs/swagger.yaml @@ -0,0 +1,25 @@ +basePath: / +host: localhost:8888 +info: + contact: + name: hertz-contrib + url: https://github.com/hertz-contrib + description: This is a demo using Hertz. + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: HertzTest + version: "1.0" +paths: + /ping: + get: + consumes: + - application/json + description: 测试Description + produces: + - application/json + responses: {} + summary: 测试Summary +schemes: +- http +swagger: "2.0" diff --git a/example/basic/main.go b/example/basic/main.go new file mode 100644 index 0000000..82be7ef --- /dev/null +++ b/example/basic/main.go @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (c) 2017 Swaggo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + + + * This file may have been modified by CloudWeGo authors. All CloudWeGo + * Modifications are Copyright 2022 CloudWeGo Authors. + */ + +package main + +import ( + "context" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + hertzSwagger "github.com/hertz-contrib/swagger" + _ "github.com/hertz-contrib/swagger/example/basic/docs" + swaggerFiles "github.com/swaggo/files" +) + +// PingHandler 测试handler +// @Summary 测试Summary +// @Description 测试Description +// @Accept application/json +// @Produce application/json +// @Router /ping [get] +func PingHandler(c context.Context, ctx *app.RequestContext) { + ctx.JSON(200, map[string]string{ + "ping": "pong", + }) +} + +// @title HertzTest +// @version 1.0 +// @description This is a demo using Hertz. + +// @contact.name hertz-contrib +// @contact.url https://github.com/hertz-contrib + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host localhost:8888 +// @BasePath / +// @schemes http +func main() { + h := server.Default() + + h.GET("/ping", PingHandler) + + url := hertzSwagger.URL("http://localhost:8888/swagger/doc.json") // The url pointing to API definition + h.GET("/swagger/*any", hertzSwagger.WrapHandler(swaggerFiles.Handler, url)) + + h.Spin() +} diff --git a/go.mod b/go.mod index 96f952b..30d817f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,42 @@ module github.com/hertz-contrib/swagger go 1.18 + +require ( + github.com/cloudwego/hertz v0.0.1 + github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 + github.com/swaggo/swag v1.8.2 + golang.org/x/net v0.0.0-20220531201128-c960675eff93 +) + +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/go-tagexpr/v2 v2.9.2 // indirect + github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 // indirect + github.com/bytedance/sonic v1.3.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 // indirect + github.com/cloudwego/netpoll v0.2.4 // 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/golang/protobuf v1.5.0 // indirect + github.com/henrylee2cn/ameda v1.4.10 // indirect + github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/nyaruka/phonenumbers v1.0.55 // indirect + github.com/tidwall/gjson v1.13.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.10 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..37fd2e0 --- /dev/null +++ b/go.sum @@ -0,0 +1,130 @@ +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= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= +github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 h1:PtwsQyQJGxf8iaPptPNaduEIu9BnrNms+pcRdHAxZaM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= +github.com/bytedance/sonic v1.3.0 h1:T2rlvNytw6bTmczlAXvGqmuMzIqGJBOsJKYwRPWR7Y8= +github.com/bytedance/sonic v1.3.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/cloudwego/hertz v0.0.1 h1:csED6Jv0XXr8kR4svQUSCyFJcgNVh3yT+F+fvkwHhtw= +github.com/cloudwego/hertz v0.0.1/go.mod h1:prTyExvsH/UmDkvfU3dp3EHsZFQISfT8R7BirvpTKdo= +github.com/cloudwego/netpoll v0.2.4 h1:Kbo2HA1cXEgoy/bu1jSNrjcqZj2diENcJqLy6vKiROU= +github.com/cloudwego/netpoll v0.2.4/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +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/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +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/goccy/go-json v0.9.4 h1:L8MLKG2mvVXiQu07qB6hmfqeSYQdOnqPot2GhsIwIaI= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= +github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +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.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 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +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/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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= +github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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/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/swag v1.8.2 h1:D4aBiVS2a65zhyk3WFqOUz7Rz0sOaUcgeErcid5uGL4= +github.com/swaggo/swag v1.8.2/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= +github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= +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= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +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-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA= +golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +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= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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.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= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/licenses/LICENSE-swaggo.txt b/licenses/LICENSE-swaggo.txt new file mode 100644 index 0000000..8c16ff8 --- /dev/null +++ b/licenses/LICENSE-swaggo.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Swaggo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/swagger.go b/swagger.go new file mode 100644 index 0000000..56d1142 --- /dev/null +++ b/swagger.go @@ -0,0 +1,347 @@ +/* + * MIT License + * + * Copyright (c) 2017 Swaggo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + + + * This file may have been modified by CloudWeGo authors. All CloudWeGo + * Modifications are Copyright 2022 CloudWeGo Authors. + */ + +package hertzSwagger + +import ( + "bytes" + "context" + "html/template" + "net/http" + "os" + "path/filepath" + "regexp" + "sync" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/protocol/consts" + "github.com/swaggo/swag" + "golang.org/x/net/webdav" +) + +type swaggerConfig struct { + URL string + DocExpansion string + Title string + Oauth2RedirectURL template.JS + DefaultModelsExpandDepth int + DeepLinking bool + PersistAuthorization bool + Oauth2DefaultClientID string +} + +// Config stores hertzSwagger configuration variables. +type Config struct { + // The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`. + URL string + DocExpansion string + InstanceName string + Title string + DefaultModelsExpandDepth int + DeepLinking bool + PersistAuthorization bool + Oauth2DefaultClientID string +} + +func (config Config) toSwaggerConfig() swaggerConfig { + return swaggerConfig{ + URL: config.URL, + DeepLinking: config.DeepLinking, + DocExpansion: config.DocExpansion, + DefaultModelsExpandDepth: config.DefaultModelsExpandDepth, + Oauth2RedirectURL: "`${window.location.protocol}//${window.location.host}$" + + "{window.location.pathname.split('/').slice(0, window.location.pathname.split('/').length - 1).join('/')}" + + "/oauth2-redirect.html`", + Title: config.Title, + PersistAuthorization: config.PersistAuthorization, + Oauth2DefaultClientID: config.Oauth2DefaultClientID, + } +} + +// URL presents the url pointing to API definition (normally swagger.json or swagger.yaml). +func URL(url string) func(*Config) { + return func(c *Config) { + c.URL = url + } +} + +// DocExpansion list, full, none. +func DocExpansion(docExpansion string) func(*Config) { + return func(c *Config) { + c.DocExpansion = docExpansion + } +} + +// DeepLinking set the swagger deep linking configuration. +func DeepLinking(deepLinking bool) func(*Config) { + return func(c *Config) { + c.DeepLinking = deepLinking + } +} + +// DefaultModelsExpandDepth set the default expansion depth for models +// (set to -1 completely hide the models). +func DefaultModelsExpandDepth(depth int) func(*Config) { + return func(c *Config) { + c.DefaultModelsExpandDepth = depth + } +} + +// InstanceName set the instance name that was used to generate the swagger documents +// Defaults to swag.Name ("swagger"). +func InstanceName(name string) func(*Config) { + return func(c *Config) { + c.InstanceName = name + } +} + +// PersistAuthorization Persist authorization information over browser close/refresh. +// Defaults to false. +func PersistAuthorization(persistAuthorization bool) func(*Config) { + return func(c *Config) { + c.PersistAuthorization = persistAuthorization + } +} + +// Oauth2DefaultClientID set the default client ID used for OAuth2 +func Oauth2DefaultClientID(oauth2DefaultClientID string) func(*Config) { + return func(c *Config) { + c.Oauth2DefaultClientID = oauth2DefaultClientID + } +} + +// WrapHandler wraps `http.Handler` into `app.HandlerFunc`. +func WrapHandler(handler *webdav.Handler, options ...func(*Config)) app.HandlerFunc { + config := Config{ + URL: "doc.json", + DocExpansion: "list", + InstanceName: swag.Name, + Title: "Swagger UI", + DefaultModelsExpandDepth: 1, + DeepLinking: true, + PersistAuthorization: false, + Oauth2DefaultClientID: "", + } + + for _, c := range options { + c(&config) + } + + return CustomWrapHandler(&config, handler) +} + +// CustomWrapHandler wraps `http.Handler` into `app.HandlerFunc`. +func CustomWrapHandler(config *Config, handler *webdav.Handler) app.HandlerFunc { + var once sync.Once + + if config.InstanceName == "" { + config.InstanceName = swag.Name + } + + if config.Title == "" { + config.Title = "Swagger UI" + } + + // create a template with name + index, _ := template.New("swagger_index.html").Parse(swaggerIndexTpl) + + matcher := regexp.MustCompile(`(.*)(index\.html|doc\.json|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[?|.]*`) + + return func(c context.Context, ctx *app.RequestContext) { + if string(ctx.Request.Method()) != consts.MethodGet { + ctx.AbortWithStatus(http.StatusMethodNotAllowed) + + return + } + + matches := matcher.FindStringSubmatch(ctx.Request.URI().String()) + + if len(matches) != 3 { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + + return + } + + path := matches[2] + once.Do(func() { + handler.Prefix = matches[1] + }) + + switch filepath.Ext(path) { + case ".html": + ctx.Header("Content-Type", "text/html; charset=utf-8") + case ".css": + ctx.Header("Content-Type", "text/css; charset=utf-8") + case ".js": + ctx.Header("Content-Type", "application/javascript") + case ".png": + ctx.Header("Content-Type", "image/png") + case ".json": + ctx.Header("Content-Type", "application/json; charset=utf-8") + } + + switch path { + case "index.html": + _ = index.Execute(ctx, config.toSwaggerConfig()) + case "doc.json": + doc, err := swag.ReadDoc(config.InstanceName) + if err != nil { + ctx.AbortWithStatus(http.StatusInternalServerError) + return + } + if _, err = ctx.Write([]byte(doc)); err != nil { + ctx.AbortWithStatus(http.StatusInternalServerError) + return + } + + default: + f, err := handler.FileSystem.OpenFile(c, path, os.O_RDONLY, 0) + if err != nil { + ctx.AbortWithStatus(http.StatusInternalServerError) + return + } + buf := new(bytes.Buffer) + if _, err = buf.ReadFrom(f); err != nil { + ctx.AbortWithStatus(http.StatusInternalServerError) + return + } + if _, err = ctx.Write([]byte(buf.String())); err != nil { + ctx.AbortWithStatus(http.StatusInternalServerError) + return + } + } + } +} + +const swaggerIndexTpl = ` + + + + + {{.Title}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +` diff --git a/swagger_test.go b/swagger_test.go new file mode 100644 index 0000000..cc375ce --- /dev/null +++ b/swagger_test.go @@ -0,0 +1,201 @@ +/* + * MIT License + * + * Copyright (c) 2017 Swaggo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + + + * This file may have been modified by CloudWeGo authors. All CloudWeGo + * Modifications are Copyright 2022 CloudWeGo Authors. + */ + +package hertzSwagger + +import ( + "io/ioutil" + "net/http" + "testing" + + "github.com/cloudwego/hertz/pkg/common/config" + "github.com/cloudwego/hertz/pkg/common/ut" + "github.com/cloudwego/hertz/pkg/route" + + "github.com/swaggo/swag" + + "github.com/cloudwego/hertz/pkg/common/test/assert" + swaggerFiles "github.com/swaggo/files" +) + +type mockedSwag struct{} + +func (s *mockedSwag) ReadDoc() string { + return `{ +}` +} + +func TestWrapHandler(t *testing.T) { + router := route.NewEngine(config.NewOptions([]config.Option{})) + router.GET("/*any", WrapHandler(swaggerFiles.Handler, URL("https://github.com/hertz-contrib/swagger"))) + + w := ut.PerformRequest(router, http.MethodGet, "/index.html", nil) + resp := w.Result() + assert.DeepEqual(t, http.StatusOK, resp.StatusCode()) +} + +func TestCustomWrapHandler(t *testing.T) { + router := route.NewEngine(config.NewOptions([]config.Option{})) + router.Any("/*any", CustomWrapHandler(&Config{}, swaggerFiles.Handler)) + + w1 := ut.PerformRequest(router, http.MethodGet, "/index.html", nil) + assert.DeepEqual(t, http.StatusOK, w1.Code) + assert.DeepEqual(t, string(w1.Header().ContentType()), "text/html; charset=utf-8") + + w2 := ut.PerformRequest(router, http.MethodGet, "/doc.json", nil) + assert.DeepEqual(t, http.StatusInternalServerError, w2.Code) + + doc := &mockedSwag{} + swag.Register(swag.Name, doc) + + w3 := ut.PerformRequest(router, http.MethodGet, "/doc.json", nil) + assert.DeepEqual(t, http.StatusOK, w3.Code) + assert.DeepEqual(t, string(w3.Header().ContentType()), "application/json; charset=utf-8") + + // Perform body rendering validation + w3Body, err := ioutil.ReadAll(w3.Body) + assert.Nil(t, err) + assert.DeepEqual(t, doc.ReadDoc(), string(w3Body)) + + w4 := ut.PerformRequest(router, http.MethodGet, "/favicon-16x16.png", nil) + assert.DeepEqual(t, http.StatusOK, w4.Code) + assert.DeepEqual(t, string(w4.Header().ContentType()), "image/png") + + w5 := ut.PerformRequest(router, http.MethodGet, "/swagger-ui.css", nil) + assert.DeepEqual(t, http.StatusOK, w5.Code) + assert.DeepEqual(t, string(w5.Header().ContentType()), "text/css; charset=utf-8") + + w6 := ut.PerformRequest(router, http.MethodGet, "/swagger-ui-bundle.js", nil) + assert.DeepEqual(t, http.StatusOK, w6.Code) + assert.DeepEqual(t, string(w6.Header().ContentType()), "application/javascript") + + assert.DeepEqual(t, http.StatusNotFound, ut.PerformRequest(router, http.MethodGet, "/notfound", nil).Code) + + assert.DeepEqual(t, http.StatusMethodNotAllowed, ut.PerformRequest(router, http.MethodPost, "/index.html", nil).Code) + + assert.DeepEqual(t, http.StatusMethodNotAllowed, ut.PerformRequest(router, http.MethodPut, "/index.html", nil).Code) +} + +func TestURL(t *testing.T) { + cfg := Config{} + + expected := "https://github.com/swaggo/http-swagger" + configFunc := URL(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.URL) +} + +func TestDocExpansion(t *testing.T) { + var cfg Config + + expected := "list" + configFunc := DocExpansion(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.DocExpansion) + + expected = "full" + configFunc = DocExpansion(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.DocExpansion) + + expected = "none" + configFunc = DocExpansion(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.DocExpansion) +} + +func TestDeepLinking(t *testing.T) { + var cfg Config + assert.DeepEqual(t, false, cfg.DeepLinking) + + configFunc := DeepLinking(true) + configFunc(&cfg) + assert.DeepEqual(t, true, cfg.DeepLinking) + + configFunc = DeepLinking(false) + configFunc(&cfg) + assert.DeepEqual(t, false, cfg.DeepLinking) +} + +func TestDefaultModelsExpandDepth(t *testing.T) { + var cfg Config + + assert.DeepEqual(t, 0, cfg.DefaultModelsExpandDepth) + + expected := -1 + configFunc := DefaultModelsExpandDepth(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.DefaultModelsExpandDepth) + + expected = 1 + configFunc = DefaultModelsExpandDepth(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.DefaultModelsExpandDepth) +} + +func TestInstanceName(t *testing.T) { + var cfg Config + + assert.DeepEqual(t, "", cfg.InstanceName) + + expected := swag.Name + configFunc := InstanceName(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.InstanceName) + + expected = "custom_name" + configFunc = InstanceName(expected) + configFunc(&cfg) + assert.DeepEqual(t, expected, cfg.InstanceName) +} + +func TestPersistAuthorization(t *testing.T) { + var cfg Config + assert.DeepEqual(t, false, cfg.PersistAuthorization) + + configFunc := PersistAuthorization(true) + configFunc(&cfg) + assert.DeepEqual(t, true, cfg.PersistAuthorization) + + configFunc = PersistAuthorization(false) + configFunc(&cfg) + assert.DeepEqual(t, false, cfg.PersistAuthorization) +} + +func TestOauth2DefaultClientID(t *testing.T) { + var cfg Config + assert.DeepEqual(t, "", cfg.Oauth2DefaultClientID) + + configFunc := Oauth2DefaultClientID("default_client_id") + configFunc(&cfg) + assert.DeepEqual(t, "default_client_id", cfg.Oauth2DefaultClientID) + + configFunc = Oauth2DefaultClientID("") + configFunc(&cfg) + assert.DeepEqual(t, "", cfg.Oauth2DefaultClientID) +} From 0d223c34aa57722bfa4e850dc2103bdb575f7f0f Mon Sep 17 00:00:00 2001 From: yaoxianjie Date: Fri, 3 Jun 2022 23:54:23 +0800 Subject: [PATCH 2/6] chore: fix typos --- README.md | 8 ++++---- example/basic/main.go | 6 +++--- swagger.go | 2 +- swagger_test.go | 8 +++----- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 229345f..935ec54 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hertz-swagger -hertz middleware to automatically generate RESTful API documentation with Swagger 2.0. +Hertz middleware to automatically generate RESTful API documentation with Swagger 2.0. ## Usage @@ -94,7 +94,7 @@ import ( "context" "github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app/server" - hertzSwagger "github.com/hertz-contrib/swagger" + swagger "github.com/hertz-contrib/swagger" _ "github.com/hertz-contrib/swagger/example/basic/docs" swaggerFiles "github.com/swaggo/files" ) @@ -129,8 +129,8 @@ func main() { h.GET("/ping", PingHandler) - url := hertzSwagger.URL("http://localhost:8888/swagger/doc.json") // The url pointing to API definition - h.GET("/swagger/*any", hertzSwagger.WrapHandler(swaggerFiles.Handler, url)) + url := swagger.URL("http://localhost:8888/swagger/doc.json") // The url pointing to API definition + h.GET("/swagger/*any", swagger.WrapHandler(swaggerFiles.Handler, url)) h.Spin() } diff --git a/example/basic/main.go b/example/basic/main.go index 82be7ef..9052408 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -33,7 +33,7 @@ import ( "github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app/server" - hertzSwagger "github.com/hertz-contrib/swagger" + swagger "github.com/hertz-contrib/swagger" _ "github.com/hertz-contrib/swagger/example/basic/docs" swaggerFiles "github.com/swaggo/files" ) @@ -68,8 +68,8 @@ func main() { h.GET("/ping", PingHandler) - url := hertzSwagger.URL("http://localhost:8888/swagger/doc.json") // The url pointing to API definition - h.GET("/swagger/*any", hertzSwagger.WrapHandler(swaggerFiles.Handler, url)) + url := swagger.URL("http://localhost:8888/swagger/doc.json") // The url pointing to API definition + h.GET("/swagger/*any", swagger.WrapHandler(swaggerFiles.Handler, url)) h.Spin() } diff --git a/swagger.go b/swagger.go index 56d1142..9ae9639 100644 --- a/swagger.go +++ b/swagger.go @@ -26,7 +26,7 @@ * Modifications are Copyright 2022 CloudWeGo Authors. */ -package hertzSwagger +package swagger import ( "bytes" diff --git a/swagger_test.go b/swagger_test.go index cc375ce..ec96fdd 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -26,7 +26,7 @@ * Modifications are Copyright 2022 CloudWeGo Authors. */ -package hertzSwagger +package swagger import ( "io/ioutil" @@ -34,13 +34,11 @@ import ( "testing" "github.com/cloudwego/hertz/pkg/common/config" + "github.com/cloudwego/hertz/pkg/common/test/assert" "github.com/cloudwego/hertz/pkg/common/ut" "github.com/cloudwego/hertz/pkg/route" - - "github.com/swaggo/swag" - - "github.com/cloudwego/hertz/pkg/common/test/assert" swaggerFiles "github.com/swaggo/files" + "github.com/swaggo/swag" ) type mockedSwag struct{} From 23393af85b427621ad129824e90e21511b1a7717 Mon Sep 17 00:00:00 2001 From: yaoxianjie Date: Sat, 4 Jun 2022 00:11:58 +0800 Subject: [PATCH 3/6] chore: add license-eye ignore --- .licenserc.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .licenserc.yaml diff --git a/.licenserc.yaml b/.licenserc.yaml new file mode 100644 index 0000000..cc41c74 --- /dev/null +++ b/.licenserc.yaml @@ -0,0 +1,13 @@ +header: + license: + spdx-id: Apache-2.0 + copyright-owner: CloudWeGo Authors + + paths: + - '**/*.go' + - '**/*.s' + + paths-ignore: + - example/basic/docs/** + + comment: on-failure \ No newline at end of file From 4f6741d41d311c6559129ee115b2ebf6928de75f Mon Sep 17 00:00:00 2001 From: yaoxianjie Date: Sat, 4 Jun 2022 00:25:22 +0800 Subject: [PATCH 4/6] chore: fix license-eye ignore --- .licenserc.yaml | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/.licenserc.yaml b/.licenserc.yaml index cc41c74..5ef3dae 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -2,12 +2,37 @@ header: license: spdx-id: Apache-2.0 copyright-owner: CloudWeGo Authors + content: | + MIT License + + Copyright (c) 2017 Swaggo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + This file may have been modified by CloudWeGo authors. All CloudWeGo + Modifications are Copyright 2022 CloudWeGo Authors. paths: - '**/*.go' - '**/*.s' paths-ignore: - - example/basic/docs/** + - '**/docs/**' comment: on-failure \ No newline at end of file From f6b150407eed64a17c0303ac82bdf9f8997ad23f Mon Sep 17 00:00:00 2001 From: yaoxianjie Date: Sat, 4 Jun 2022 11:38:29 +0800 Subject: [PATCH 5/6] chore: fix import typo --- README.md | 2 +- example/basic/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 935ec54..18e2836 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ import ( "context" "github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app/server" - swagger "github.com/hertz-contrib/swagger" + "github.com/hertz-contrib/swagger" _ "github.com/hertz-contrib/swagger/example/basic/docs" swaggerFiles "github.com/swaggo/files" ) diff --git a/example/basic/main.go b/example/basic/main.go index 9052408..599a423 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -33,7 +33,7 @@ import ( "github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app/server" - swagger "github.com/hertz-contrib/swagger" + "github.com/hertz-contrib/swagger" _ "github.com/hertz-contrib/swagger/example/basic/docs" swaggerFiles "github.com/swaggo/files" ) From 1d8823dd24050508db90ce828ac62bdfa9311ce1 Mon Sep 17 00:00:00 2001 From: yaoxianjie Date: Sat, 4 Jun 2022 14:51:03 +0800 Subject: [PATCH 6/6] chore: change type conversion --- swagger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger.go b/swagger.go index 9ae9639..7a510b7 100644 --- a/swagger.go +++ b/swagger.go @@ -230,7 +230,7 @@ func CustomWrapHandler(config *Config, handler *webdav.Handler) app.HandlerFunc ctx.AbortWithStatus(http.StatusInternalServerError) return } - if _, err = ctx.Write([]byte(buf.String())); err != nil { + if _, err = ctx.Write(buf.Bytes()); err != nil { ctx.AbortWithStatus(http.StatusInternalServerError) return }