34 Commits

Author SHA1 Message Date
Davide Bianchi
48e22b5674 Upgrade version to v0.7.0 2022-12-22 19:36:46 +01:00
Davide Bianchi
a1df7c966e docs: update CHANGELOG 2022-12-22 19:33:13 +01:00
Davide Bianchi
6e38c8b9ea Merge pull request #93 from davidebianchi/dependabot/go_modules/github.com/getkin/kin-openapi-0.111.0 2022-12-22 19:26:41 +01:00
Davide Bianchi
40ea80b890 Merge pull request #94 from davidebianchi/support-fiber 2022-12-22 19:26:30 +01:00
Davide Bianchi
1daad04aab feat: change GenerateAndExposeSwagger fn name 2022-12-22 19:23:37 +01:00
Davide Bianchi
77e080c587 docs: update CHANGELOG 2022-12-22 19:15:33 +01:00
Davide Bianchi
00d9267cf8 feat: improve README 2022-12-22 19:11:37 +01:00
Davide Bianchi
efab680870 docs: update CHANGELOG 2022-12-22 18:43:13 +01:00
Davide Bianchi
fa4fd37f17 feat: add first class support to echo router 2022-12-22 18:42:41 +01:00
Davide Bianchi
74baec392e feat: move gorilla integration under gorilla folder 2022-12-22 18:27:57 +01:00
Davide Bianchi
a48fb550df feat: drop support golang below v1.17 2022-12-22 15:37:14 +01:00
Davide Bianchi
76d2e514e1 feat: add strong types on Route 2022-12-22 15:35:56 +01:00
Davide Bianchi
aeb5680612 feat: add fiber support 2022-12-22 12:44:55 +01:00
Davide Bianchi
10a5f64f5c feat: change apirouter implementation to support multiple type of handler 2022-12-22 11:29:13 +01:00
Davide Bianchi
fca6b6910d feat: move gorilla under support 2022-12-22 10:03:20 +01:00
Davide Bianchi
da751837df upgrade to go 19 2022-12-22 09:54:37 +01:00
dependabot[bot]
cbe3488c8d chore(deps): bump github.com/getkin/kin-openapi from 0.108.0 to 0.111.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.108.0 to 0.111.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.108.0...v0.111.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 03:03:15 +00:00
Davide Bianchi
315cd103a7 Merge pull request #88 from davidebianchi/dependabot/go_modules/github.com/getkin/kin-openapi-0.108.0 2022-11-17 09:35:44 +01:00
Davide Bianchi
d7b5134200 Upgrade version to v0.6.1 2022-11-17 09:34:39 +01:00
Davide Bianchi
31e40cedfd Merge pull request #90 from davidebianchi/fix/remove-deprecated-lib 2022-11-17 09:34:11 +01:00
Davide Bianchi
6f3dcc578c update go version in github actions 2022-11-17 09:32:13 +01:00
Davide Bianchi
45cf385dec docs: update CHANGELOG 2022-11-17 09:30:28 +01:00
Davide Bianchi
e5f3c438b4 fix: remove use of the deprecated io/ioutil lib 2022-11-17 09:29:43 +01:00
Federico Maggi
047317ef17 Merge pull request #89 from FilippoRezzonico/fix/RJMR-254-jsonschema-lib-without-patternProperties
Fix/rjmr 254 jsonschema lib without pattern properties
2022-11-16 18:40:44 +01:00
filippo.rezzonico
b007e57092 updated changelog 2022-11-16 18:27:28 +01:00
filippo.rezzonico
6c9d0e579f added test 2022-11-16 18:24:10 +01:00
filippo.rezzonico
f84327d424 changed jsonschema 2022-11-16 18:22:24 +01:00
dependabot[bot]
3a598c0726 chore(deps): bump github.com/getkin/kin-openapi from 0.107.0 to 0.108.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.107.0 to 0.108.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.107.0...v0.108.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-10 03:05:07 +00:00
Davide Bianchi
085abe98d1 Merge pull request #83 from davidebianchi/dependabot/go_modules/github.com/stretchr/testify-1.8.1 2022-11-06 12:08:46 +01:00
Davide Bianchi
c5fff8f982 Merge pull request #84 from davidebianchi/dependabot/go_modules/github.com/getkin/kin-openapi-0.107.0 2022-11-06 12:08:37 +01:00
Davide Bianchi
fee2444c96 Merge pull request #80 from davidebianchi/dependabot/go_modules/github.com/labstack/echo/v4-4.9.1 2022-11-06 12:08:21 +01:00
dependabot[bot]
2939f1027d chore(deps): bump github.com/getkin/kin-openapi from 0.103.0 to 0.107.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.103.0 to 0.107.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.103.0...v0.107.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-28 03:10:41 +00:00
dependabot[bot]
3a4118844e chore(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 03:11:45 +00:00
dependabot[bot]
3e72099700 chore(deps): bump github.com/labstack/echo/v4 from 4.9.0 to 4.9.1
Bumps [github.com/labstack/echo/v4](https://github.com/labstack/echo) from 4.9.0 to 4.9.1.
- [Release notes](https://github.com/labstack/echo/releases)
- [Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/labstack/echo/compare/v4.9.0...v4.9.1)

---
updated-dependencies:
- dependency-name: github.com/labstack/echo/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-13 03:09:18 +00:00
24 changed files with 1253 additions and 445 deletions

View File

@@ -9,10 +9,10 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
go_version: [1.16, 1.17]
go_version: [1.18, 1.19]
os: [ubuntu-latest]
include:
- go_version: 1.17
- go_version: 1.19
os: macos-latest
steps:
- uses: actions/checkout@v1

View File

@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
## 0.7.0 - 22-12-2022
This is a big major release. The main achievement is to increase the usability of this library to all the routers.
Below are listed the breaking changes you should care when update the version.
### BREAKING CHANGES
- `apirouter.NewGorillaMuxRouter` is now `gorilla.NewRouter` (exposed by package `github.com/davidebianchi/gswagger/support/gorilla`).
- removed `apirouter.HandlerFunc`. Now it is exposed by `gorilla.HandlerFunc`
- changed `apirouter.Router` interface:
- now it accept a generics `HandlerFunc` to define the handler function
- add method `SwaggerHandler(contentType string, json []byte) HandlerFunc`
- `NewRouter` function now accept `HandlerFunc` as generics
- drop support to golang <= 1.17
- `GenerateAndExposeSwagger` renamed to `GenerateAndExposeOpenapi`
### Feature
- support to different types of routers
- add [fiber](https://github.com/gofiber/fiber) support
- add [echo](https://echo.labstack.com/) support
## 0.6.1 - 17-11-2022
### Changed
- change jsonschema lib to `mia-platform/jsonschema v0.1.0`. This update removes the `patternProperties` with `additionalProperties` from all schemas
- remove use of deprecated io/ioutil lib
## 0.6.0 - 04-11-2022
### Added

View File

@@ -13,7 +13,6 @@ all: test
.PHONY: test
test:
go test ./... -coverprofile coverage.out
$(MAKE) clean
.PHONY: version
version:

View File

@@ -11,11 +11,13 @@
Generate an openapi spec dynamically based on the types used to handle request and response.
It works with any router which support handler net/http HandlerFunc compatible.
It works with any router, it is simple to add support to your router implementing the [apirouter](apirouter/router.go) interface.
The routers supported out of the box are:
- [gorilla-mux](https://github.com/gorilla/mux)
- [fiber](https://github.com/gofiber/fiber)
- [echo](https://echo.labstack.com/)
This lib uses [kin-openapi] to automatically generate and serve a swagger file.
@@ -25,15 +27,13 @@ It is always possible to add a totally custom swagger schema using [kin-openapi]
## Usage
To add a router not handled out of the box, it must implements the [Router interface](./apirouter/router.go).
An example usage of this lib with gorilla mux:
```go
context := context.Background()
muxRouter := mux.NewRouter()
router, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(muxRouter), swagger.Options{
router, _ := swagger.NewRouter(gorilla.NewRouter(muxRouter), swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
@@ -54,7 +54,6 @@ type User struct {
Groups []string `json:"groups,omitempty" jsonschema:"title=groups of the user,default=users"`
Address string `json:"address" jsonschema:"title=user address"`
}
type Users []User
type errorResponse struct {
Message string `json:"message"`
}
@@ -99,39 +98,22 @@ operation := swagger.NewOperation()
operation.AddRequestBody(requestBody)
router.AddRawRoute(http.MethodPost, "/cars", okHandler, operation)
router.GenerateAndExposeOpenapi()
```
This configuration will output the schema shown [here](testdata/users_employees.json).
This configuration will output the schema shown [here](./support/gorilla/testdata/examples-users.json).
## Auto generated path params schema
The path params, if not set in schema, are auto generated from the path. Currently, it is supported only the path params like `{myPath}`.
For example, with this use case:
```golang
okHandler := func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
_, err := router.AddRoute(http.MethodGet, "/users/{userId}", okHandler, Definitions{
Querystring: ParameterValue{
"query": {
Schema: &Schema{Value: ""},
},
},
})
require.NoError(t, err)
_, err = router.AddRoute(http.MethodGet, "/cars/{carId}/drivers/{driverId}", okHandler, Definitions{})
require.NoError(t, err)
```
For example, with this use case, you can see the [example test](./support/gorilla/examples_test.go).
The generated oas schema will contains `userId`, `carId` and `driverId` as path params set to string.
If only one params is set, you must specify manually all the path params.
The generated file for this test case is [here](./testdata/params-autofill.json).
The generated OAS for this test case is visible [here](./support/gorilla/testdata/examples-users.json).
## SubRouter
@@ -140,7 +122,7 @@ It is possible to add a prefix to all the routes created under the specific rout
It could also be useful if you need a sub router to create a group of APIs which use the same middleware (for example,this could be achieved by the SubRouter features of gorilla mux, for example).
To see the SubRouter example, please see the [SubRouter test](./integration_test.go).
To see the SubRouter example, please see the integration test of one of the supported routers.
### FAQ
@@ -169,7 +151,7 @@ see the [tags on this repository](https://github.com/davidebianchi/gswagger/tags
<!-- Reference -->
[kin-openapi]: https://github.com/getkin/kin-openapi
[jsonschema]: https://github.com/invopop/jsonschemaa
[jsonschema]: https://github.com/mia-platform/jsonschema
[github-actions]: https://github.com/davidebianchi/gswagger/actions
[github-actions-svg]: https://github.com/davidebianchi/gswagger/workflows/Test%20and%20build/badge.svg
[godoc-svg]: https://godoc.org/github.com/davidebianchi/gswagger?status.svg

View File

@@ -1,17 +0,0 @@
package apirouter
import "github.com/gorilla/mux"
type gorillaRouter struct {
router *mux.Router
}
func (r gorillaRouter) AddRoute(method string, path string, handler HandlerFunc) Route {
return r.router.HandleFunc(path, handler).Methods(method)
}
func NewGorillaMuxRouter(router *mux.Router) Router {
return gorillaRouter{
router: router,
}
}

View File

@@ -1,12 +1,6 @@
package apirouter
import "net/http"
// Handler is the http type handler
type HandlerFunc func(w http.ResponseWriter, req *http.Request)
type Router interface {
type Router[HandlerFunc any, Route any] interface {
AddRoute(method string, path string, handler HandlerFunc) Route
SwaggerHandler(contentType string, blob []byte) HandlerFunc
}
type Route interface{}

29
go.mod
View File

@@ -1,33 +1,42 @@
module github.com/davidebianchi/gswagger
go 1.17
go 1.19
require (
github.com/getkin/kin-openapi v0.103.0
github.com/getkin/kin-openapi v0.111.0
github.com/ghodss/yaml v1.0.0
github.com/go-openapi/swag v0.21.1 // indirect
github.com/gorilla/mux v1.8.0
github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/invopop/jsonschema v0.6.0
github.com/labstack/echo/v4 v4.9.0
github.com/labstack/echo/v4 v4.9.1
github.com/mailru/easyjson v0.7.7 // indirect
github.com/stretchr/testify v1.8.0
github.com/mia-platform/jsonschema v0.1.0
github.com/stretchr/testify v1.8.1
)
require github.com/gofiber/fiber/v2 v2.40.1
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.41.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20220312131142-6068a2e6cfdc // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

61
go.sum
View File

@@ -1,9 +1,11 @@
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
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/getkin/kin-openapi v0.103.0 h1:F5wAtaQvPWxKCAYZ69LgHAThgu16p4u41VQtbn1U8LA=
github.com/getkin/kin-openapi v0.103.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE=
github.com/getkin/kin-openapi v0.111.0 h1:zspOcFKBCQOY8d9Yockcbit8iVR2hco9qLaoQoj7kmw=
github.com/getkin/kin-openapi v0.111.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
@@ -11,80 +13,91 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/gofiber/fiber/v2 v2.40.1 h1:pc7n9VVpGIqNsvg9IPLQhyFEMJL8gCs1kneH5D1pIl4=
github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/invopop/jsonschema v0.6.0 h1:8e+xY8ZEn8gDHUYylSlLHy22P+SLeIRIHv3nM3hCbmY=
github.com/invopop/jsonschema v0.6.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
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/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
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/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
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/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mia-platform/jsonschema v0.1.0 h1:tjQf7TaYROsAqk7SXTL+44TrfKk3bSEvhRGPS51IA5Y=
github.com/mia-platform/jsonschema v0.1.0/go.mod h1:r2DJjPA/+6S+WPnXZt1xONMvO2b4hlhfXfUYV0po/Dk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
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.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY=
github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220312131142-6068a2e6cfdc h1:i6Z9eOQAdM7lvsbkT3fwFNtSAAC+A59TYilFj53HW+E=
golang.org/x/crypto v0.0.0-20220312131142-6068a2e6cfdc/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=

View File

@@ -1,254 +0,0 @@
package swagger_test
import (
"context"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
swagger "github.com/davidebianchi/gswagger"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/require"
)
const (
swaggerOpenapiTitle = "test swagger title"
swaggerOpenapiVersion = "test swagger version"
)
func TestIntegration(t *testing.T) {
t.Run("router works correctly", func(t *testing.T) {
muxRouter, _ := setupSwagger(t)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
t.Run("router works correctly - echo", func(t *testing.T) {
echoRouter, _ := setupEchoSwagger(t)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
t.Run("works correctly with subrouter - handles path prefix - gorilla mux", func(t *testing.T) {
muxRouter, swaggerRouter := setupSwagger(t)
muxSubRouter := muxRouter.NewRoute().Subrouter()
subRouter, err := swaggerRouter.SubRouter(apirouter.NewGorillaMuxRouter(muxSubRouter), swagger.SubRouterOptions{
PathPrefix: "/prefix",
})
require.NoError(t, err)
_, err = subRouter.AddRoute(http.MethodGet, "/foo", okHandler, swagger.Definitions{})
require.NoError(t, err)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("correctly call sub router", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/prefix/foo", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
})
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
t.Run("works correctly with subrouter - handles path prefix - echo", func(t *testing.T) {
eRouter, swaggerRouter := setupEchoSwagger(t)
subRouter, err := swaggerRouter.SubRouter(echoRouter{router: eRouter}, swagger.SubRouterOptions{
PathPrefix: "/prefix",
})
require.NoError(t, err)
_, err = subRouter.AddRoute(http.MethodGet, "/foo", okHandler, swagger.Definitions{})
require.NoError(t, err)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
eRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("correctly call sub router", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/prefix/foo", nil)
eRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
})
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
eRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
}
func readBody(t *testing.T, requestBody io.ReadCloser) string {
t.Helper()
body, err := ioutil.ReadAll(requestBody)
require.NoError(t, err)
return string(body)
}
func setupSwagger(t *testing.T) (*mux.Router, *swagger.Router) {
t.Helper()
context := context.Background()
muxRouter := mux.NewRouter()
router, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(muxRouter), swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
},
},
})
require.NoError(t, err)
operation := swagger.Operation{}
_, err = router.AddRawRoute(http.MethodGet, "/hello", okHandler, operation)
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
return muxRouter, router
}
func okHandler(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`OK`))
}
func setupEchoSwagger(t *testing.T) (*echo.Echo, *swagger.Router) {
t.Helper()
context := context.Background()
e := echo.New()
router, err := swagger.NewRouter(echoRouter{router: e}, swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
},
},
})
require.NoError(t, err)
operation := swagger.Operation{}
_, err = router.AddRawRoute(http.MethodGet, "/hello", func(w http.ResponseWriter, req *http.Request) {
ctx := e.NewContext(req, w)
echoOkHandler(ctx)
}, operation)
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
return e, router
}
func echoOkHandler(c echo.Context) error {
return c.String(http.StatusOK, "OK")
}
type echoRouter struct {
router *echo.Echo
}
func (r echoRouter) AddRoute(method, path string, handler apirouter.HandlerFunc) apirouter.Route {
return r.router.Add(method, path, echo.WrapHandler(http.HandlerFunc(handler)))
}

32
main.go
View File

@@ -30,8 +30,8 @@ const (
// Router handle the api router and the swagger schema.
// api router supported out of the box are:
// - gorilla mux
type Router struct {
router apirouter.Router
type Router[HandlerFunc, Route any] struct {
router apirouter.Router[HandlerFunc, Route]
swaggerSchema *openapi3.T
context context.Context
jsonDocumentationPath string
@@ -52,8 +52,8 @@ type Options struct {
}
// NewRouter generate new router with swagger. Default to OpenAPI 3.0.0
func NewRouter(router apirouter.Router, options Options) (*Router, error) {
swagger, err := generateNewValidSwagger(options.Openapi)
func NewRouter[HandlerFunc, Route any](router apirouter.Router[HandlerFunc, Route], options Options) (*Router[HandlerFunc, Route], error) {
swagger, err := generateNewValidOpenapi(options.Openapi)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrValidatingSwagger, err)
}
@@ -79,7 +79,7 @@ func NewRouter(router apirouter.Router, options Options) (*Router, error) {
jsonDocumentationPath = options.JSONDocumentationPath
}
return &Router{
return &Router[HandlerFunc, Route]{
router: router,
swaggerSchema: swagger,
context: ctx,
@@ -93,8 +93,8 @@ type SubRouterOptions struct {
PathPrefix string
}
func (r Router) SubRouter(router apirouter.Router, opts SubRouterOptions) (*Router, error) {
return &Router{
func (r Router[HandlerFunc, Route]) SubRouter(router apirouter.Router[HandlerFunc, Route], opts SubRouterOptions) (*Router[HandlerFunc, Route], error) {
return &Router[HandlerFunc, Route]{
router: router,
swaggerSchema: r.swaggerSchema,
context: r.context,
@@ -104,7 +104,7 @@ func (r Router) SubRouter(router apirouter.Router, opts SubRouterOptions) (*Rout
}, nil
}
func generateNewValidSwagger(swagger *openapi3.T) (*openapi3.T, error) {
func generateNewValidOpenapi(swagger *openapi3.T) (*openapi3.T, error) {
if swagger == nil {
return nil, fmt.Errorf("swagger is required")
}
@@ -128,9 +128,9 @@ func generateNewValidSwagger(swagger *openapi3.T) (*openapi3.T, error) {
return swagger, nil
}
// GenerateAndExposeSwagger creates a /documentation/json route on router and
// GenerateAndExposeOpenapi creates a /documentation/json route on router and
// expose the generated swagger
func (r Router) GenerateAndExposeSwagger() error {
func (r Router[_, _]) GenerateAndExposeOpenapi() error {
if err := r.swaggerSchema.Validate(r.context); err != nil {
return fmt.Errorf("%w: %s", ErrValidatingSwagger, err)
}
@@ -139,21 +139,13 @@ func (r Router) GenerateAndExposeSwagger() error {
if err != nil {
return fmt.Errorf("%w json marshal: %s", ErrGenerateSwagger, err)
}
r.router.AddRoute(http.MethodGet, r.jsonDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(jsonSwagger)
})
r.router.AddRoute(http.MethodGet, r.jsonDocumentationPath, r.router.SwaggerHandler("application/json", jsonSwagger))
yamlSwagger, err := yaml.JSONToYAML(jsonSwagger)
if err != nil {
return fmt.Errorf("%w yaml marshal: %s", ErrGenerateSwagger, err)
}
r.router.AddRoute(http.MethodGet, r.yamlDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write(yamlSwagger)
})
r.router.AddRoute(http.MethodGet, r.yamlDocumentationPath, r.router.SwaggerHandler("text/plain", yamlSwagger))
return nil
}

View File

@@ -4,13 +4,13 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/davidebianchi/gswagger/support/gorilla"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
@@ -18,7 +18,7 @@ import (
func TestNewRouter(t *testing.T) {
muxRouter := mux.NewRouter()
mAPIRouter := apirouter.NewGorillaMuxRouter(muxRouter)
mAPIRouter := gorilla.NewRouter(muxRouter)
info := &openapi3.Info{
Title: "my title",
@@ -42,7 +42,7 @@ func TestNewRouter(t *testing.T) {
})
require.NoError(t, err)
require.Equal(t, &Router{
require.Equal(t, &Router[gorilla.HandlerFunc, gorilla.Route]{
context: context.Background(),
router: mAPIRouter,
swaggerSchema: openapi,
@@ -60,7 +60,7 @@ func TestNewRouter(t *testing.T) {
})
require.NoError(t, err)
require.Equal(t, &Router{
require.Equal(t, &Router[gorilla.HandlerFunc, gorilla.Route]{
context: ctx,
router: mAPIRouter,
swaggerSchema: openapi,
@@ -80,7 +80,7 @@ func TestNewRouter(t *testing.T) {
})
require.NoError(t, err)
require.Equal(t, &Router{
require.Equal(t, &Router[gorilla.HandlerFunc, gorilla.Route]{
context: ctx,
router: mAPIRouter,
swaggerSchema: openapi,
@@ -122,7 +122,7 @@ func TestGenerateValidSwagger(t *testing.T) {
t.Run("not ok - empty swagger info", func(t *testing.T) {
swagger := &openapi3.T{}
swagger, err := generateNewValidSwagger(swagger)
swagger, err := generateNewValidOpenapi(swagger)
require.Nil(t, swagger)
require.EqualError(t, err, "swagger info is required")
})
@@ -132,7 +132,7 @@ func TestGenerateValidSwagger(t *testing.T) {
Info: &openapi3.Info{},
}
swagger, err := generateNewValidSwagger(swagger)
swagger, err := generateNewValidOpenapi(swagger)
require.Nil(t, swagger)
require.EqualError(t, err, "swagger info title is required")
})
@@ -144,7 +144,7 @@ func TestGenerateValidSwagger(t *testing.T) {
},
}
swagger, err := generateNewValidSwagger(swagger)
swagger, err := generateNewValidOpenapi(swagger)
require.Nil(t, swagger)
require.EqualError(t, err, "swagger info version is required")
})
@@ -154,13 +154,13 @@ func TestGenerateValidSwagger(t *testing.T) {
Info: &openapi3.Info{},
}
swagger, err := generateNewValidSwagger(swagger)
swagger, err := generateNewValidOpenapi(swagger)
require.Nil(t, swagger)
require.EqualError(t, err, "swagger info title is required")
})
t.Run("not ok - swagger is required", func(t *testing.T) {
swagger, err := generateNewValidSwagger(nil)
swagger, err := generateNewValidOpenapi(nil)
require.Nil(t, swagger)
require.EqualError(t, err, "swagger is required")
})
@@ -174,7 +174,7 @@ func TestGenerateValidSwagger(t *testing.T) {
Info: info,
}
swagger, err := generateNewValidSwagger(swagger)
swagger, err := generateNewValidOpenapi(swagger)
require.NoError(t, err)
require.Equal(t, &openapi3.T{
OpenAPI: defaultOpenapiVersion,
@@ -187,7 +187,7 @@ func TestGenerateValidSwagger(t *testing.T) {
func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("fails swagger validation", func(t *testing.T) {
mRouter := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: "title",
@@ -202,7 +202,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.Error(t, err)
require.True(t, strings.HasPrefix(err.Error(), fmt.Sprintf("%s:", ErrValidatingSwagger)))
})
@@ -213,12 +213,12 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: swagger,
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
w := httptest.NewRecorder()
@@ -229,7 +229,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "application/json"))
body := readBody(t, w.Result().Body)
actual, err := ioutil.ReadFile("testdata/users_employees.json")
actual, err := os.ReadFile("testdata/users_employees.json")
require.NoError(t, err)
require.JSONEq(t, string(actual), body)
})
@@ -240,13 +240,13 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: swagger,
JSONDocumentationPath: "/custom/path",
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
w := httptest.NewRecorder()
@@ -257,7 +257,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "application/json"))
body := readBody(t, w.Result().Body)
actual, err := ioutil.ReadFile("testdata/users_employees.json")
actual, err := os.ReadFile("testdata/users_employees.json")
require.NoError(t, err)
require.JSONEq(t, string(actual), body)
})
@@ -268,12 +268,12 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: swagger,
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
w := httptest.NewRecorder()
@@ -284,7 +284,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "text/plain"))
body := readBody(t, w.Result().Body)
expected, err := ioutil.ReadFile("testdata/users_employees.yaml")
expected, err := os.ReadFile("testdata/users_employees.yaml")
require.NoError(t, err)
require.YAMLEq(t, string(expected), body, string(body))
})
@@ -295,13 +295,13 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: swagger,
YAMLDocumentationPath: "/custom/path",
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
w := httptest.NewRecorder()
@@ -312,7 +312,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "text/plain"))
body := readBody(t, w.Result().Body)
expected, err := ioutil.ReadFile("testdata/users_employees.yaml")
expected, err := os.ReadFile("testdata/users_employees.yaml")
require.NoError(t, err)
require.YAMLEq(t, string(expected), body, string(body))
})
@@ -320,7 +320,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("ok - subrouter", func(t *testing.T) {
mRouter := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: "test swagger title",
@@ -337,7 +337,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
}, Definitions{})
mSubRouter := mRouter.PathPrefix("/prefix").Subrouter()
subrouter, err := router.SubRouter(apirouter.NewGorillaMuxRouter(mSubRouter), SubRouterOptions{
subrouter, err := router.SubRouter(gorilla.NewRouter(mSubRouter), SubRouterOptions{
PathPrefix: "/prefix",
})
require.NoError(t, err)
@@ -356,7 +356,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.NoError(t, err)
})
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
w := httptest.NewRecorder()
@@ -367,7 +367,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "application/json"))
body := readBody(t, w.Result().Body)
actual, err := ioutil.ReadFile("testdata/subrouter.json")
actual, err := os.ReadFile("testdata/subrouter.json")
require.NoError(t, err)
require.JSONEq(t, string(actual), body)
})
@@ -375,7 +375,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("ok - new router with path prefix", func(t *testing.T) {
mRouter := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
router, err := NewRouter(gorilla.NewRouter(mRouter), Options{
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: "test swagger title",
@@ -392,7 +392,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
w.Write([]byte("ok"))
}, Definitions{})
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
w := httptest.NewRecorder()
@@ -403,7 +403,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "application/json"))
body := readBody(t, w.Result().Body)
actual, err := ioutil.ReadFile("testdata/router_with_prefix.json")
actual, err := os.ReadFile("testdata/router_with_prefix.json")
require.NoError(t, err)
require.JSONEq(t, string(actual), body)
})
@@ -412,7 +412,7 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
func readBody(t *testing.T, requestBody io.ReadCloser) string {
t.Helper()
body, err := ioutil.ReadAll(requestBody)
body, err := io.ReadAll(requestBody)
require.NoError(t, err)
return string(body)

View File

@@ -7,9 +7,8 @@ import (
"sort"
"strings"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/getkin/kin-openapi/openapi3"
"github.com/invopop/jsonschema"
"github.com/mia-platform/jsonschema"
)
var (
@@ -25,12 +24,12 @@ var (
// AddRawRoute add route to router with specific method, path and handler. Add the
// router also to the swagger schema, after validating it
func (r Router) AddRawRoute(method string, routePath string, handler apirouter.HandlerFunc, operation Operation) (interface{}, error) {
func (r Router[HandlerFunc, Route]) AddRawRoute(method string, routePath string, handler HandlerFunc, operation Operation) (Route, error) {
op := operation.Operation
if op != nil {
err := operation.Validate(r.context)
if err != nil {
return nil, err
return getZero[Route](), err
}
} else {
op = openapi3.NewOperation()
@@ -89,46 +88,46 @@ const (
cookieParamType = "cookie"
)
// AddRoute add a route with json schema inferted by passed schema.
func (r Router) AddRoute(method string, path string, handler apirouter.HandlerFunc, schema Definitions) (interface{}, error) {
// AddRoute add a route with json schema inferred by passed schema.
func (r Router[HandlerFunc, Route]) AddRoute(method string, path string, handler HandlerFunc, schema Definitions) (Route, error) {
operation := NewOperation()
operation.Responses = make(openapi3.Responses)
operation.Tags = schema.Tags
err := r.resolveRequestBodySchema(schema.RequestBody, operation)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrRequestBody, err)
return getZero[Route](), fmt.Errorf("%w: %s", ErrRequestBody, err)
}
err = r.resolveResponsesSchema(schema.Responses, operation)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrResponses, err)
return getZero[Route](), fmt.Errorf("%w: %s", ErrResponses, err)
}
err = r.resolveParameterSchema(pathParamsType, getPathParamsAutofilled(schema, path), operation)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
}
err = r.resolveParameterSchema(queryParamType, schema.Querystring, operation)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
}
err = r.resolveParameterSchema(headerParamType, schema.Headers, operation)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
}
err = r.resolveParameterSchema(cookieParamType, schema.Cookies, operation)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
}
return r.AddRawRoute(method, path, handler, operation)
}
func (r Router) getSchemaFromInterface(v interface{}, allowAdditionalProperties bool) (*openapi3.Schema, error) {
func (r Router[_, _]) getSchemaFromInterface(v interface{}, allowAdditionalProperties bool) (*openapi3.Schema, error) {
if v == nil {
return &openapi3.Schema{}, nil
}
@@ -159,7 +158,7 @@ func (r Router) getSchemaFromInterface(v interface{}, allowAdditionalProperties
return schema, nil
}
func (r Router) resolveRequestBodySchema(bodySchema *ContentValue, operation Operation) error {
func (r Router[_, _]) resolveRequestBodySchema(bodySchema *ContentValue, operation Operation) error {
if bodySchema == nil {
return nil
}
@@ -178,7 +177,7 @@ func (r Router) resolveRequestBodySchema(bodySchema *ContentValue, operation Ope
return nil
}
func (r Router) resolveResponsesSchema(responses map[int]ContentValue, operation Operation) error {
func (r Router[_, _]) resolveResponsesSchema(responses map[int]ContentValue, operation Operation) error {
if responses == nil {
operation.Responses = openapi3.NewResponses()
}
@@ -197,7 +196,7 @@ func (r Router) resolveResponsesSchema(responses map[int]ContentValue, operation
return nil
}
func (r Router) resolveParameterSchema(paramType string, paramConfig ParameterValue, operation Operation) error {
func (r Router[_, _]) resolveParameterSchema(paramType string, paramConfig ParameterValue, operation Operation) error {
var keys = make([]string, 0, len(paramConfig))
for k := range paramConfig {
keys = append(keys, k)
@@ -248,7 +247,7 @@ func (r Router) resolveParameterSchema(paramType string, paramConfig ParameterVa
return nil
}
func (r Router) addContentToOASSchema(content Content) (openapi3.Content, error) {
func (r Router[_, _]) addContentToOASSchema(content Content) (openapi3.Content, error) {
oasContent := openapi3.NewContent()
for k, v := range content {
var err error
@@ -279,3 +278,8 @@ func getPathParamsAutofilled(schema Definitions, path string) ParameterValue {
}
return schema.PathParams
}
func getZero[T any]() T {
var result T
return result
}

View File

@@ -3,12 +3,12 @@ package swagger
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/davidebianchi/gswagger/support/gorilla"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
@@ -17,6 +17,8 @@ import (
const jsonType = "application/json"
const formDataType = "multipart/form-data"
type TestRouter = Router[gorilla.HandlerFunc, gorilla.Route]
func TestAddRoutes(t *testing.T) {
type User struct {
@@ -56,19 +58,19 @@ func TestAddRoutes(t *testing.T) {
tests := []struct {
name string
routes func(t *testing.T, router *Router)
routes func(t *testing.T, router *TestRouter)
fixturesPath string
testPath string
testMethod string
}{
{
name: "no routes",
routes: func(t *testing.T, router *Router) {},
routes: func(t *testing.T, router *TestRouter) {},
fixturesPath: "testdata/empty.json",
},
{
name: "empty route schema",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodPost, "/", okHandler, Definitions{})
require.NoError(t, err)
},
@@ -78,7 +80,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "multiple real routes",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodPost, "/users", okHandler, Definitions{
RequestBody: &ContentValue{
Content: Content{
@@ -128,7 +130,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "multipart request body",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodPost, "/files", okHandler, Definitions{
RequestBody: &ContentValue{
Content: Content{
@@ -157,7 +159,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema with params",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
var number = 0
_, err := router.AddRoute(http.MethodGet, "/users/{userId}", okHandler, Definitions{
PathParams: ParameterValue{
@@ -186,7 +188,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema without params autofilled",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodGet, "/users/{userId}", okHandler, Definitions{
Querystring: ParameterValue{
"query": {
@@ -204,7 +206,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema with querystring",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodGet, "/projects", okHandler, Definitions{
Querystring: ParameterValue{
"projectId": {
@@ -220,7 +222,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema with headers",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodGet, "/projects", okHandler, Definitions{
Headers: ParameterValue{
"foo": {
@@ -239,7 +241,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema with cookies",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodGet, "/projects", okHandler, Definitions{
Cookies: ParameterValue{
"debug": {
@@ -258,7 +260,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema defined without value",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodPost, "/{id}", okHandler, Definitions{
RequestBody: &ContentValue{
Description: "request body without schema",
@@ -287,7 +289,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "allOf schema",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
schema := openapi3.NewAllOfSchema()
schema.AllOf = []*openapi3.SchemaRef{
{
@@ -332,7 +334,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "anyOf schema",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
schema := openapi3.NewAnyOfSchema()
schema.AnyOf = []*openapi3.SchemaRef{
{
@@ -376,7 +378,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "oneOf support on properties",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodPost, "/user-profile", okHandler, Definitions{
RequestBody: &ContentValue{
Content: Content{
@@ -424,7 +426,7 @@ func TestAddRoutes(t *testing.T) {
},
{
name: "schema with tags",
routes: func(t *testing.T, router *Router) {
routes: func(t *testing.T, router *TestRouter) {
_, err := router.AddRoute(http.MethodGet, "/users", okHandler, Definitions{
Tags: []string{"Tag1", "Tag2"},
})
@@ -440,7 +442,7 @@ func TestAddRoutes(t *testing.T) {
context := context.Background()
r := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(r), Options{
router, err := NewRouter(gorilla.NewRouter(r), Options{
Context: context,
Openapi: getBaseSwagger(t),
})
@@ -450,7 +452,7 @@ func TestAddRoutes(t *testing.T) {
// Add routes to test
test.routes(t, router)
err = router.GenerateAndExposeSwagger()
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
if test.testPath != "" {
@@ -477,7 +479,7 @@ func TestAddRoutes(t *testing.T) {
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
expected, err := ioutil.ReadFile(test.fixturesPath)
expected, err := os.ReadFile(test.fixturesPath)
require.NoError(t, err)
require.JSONEq(t, string(expected), body, "actual json data: %s", body)
})
@@ -630,7 +632,7 @@ func TestResolveRequestBodySchema(t *testing.T) {
}
mux := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mux), Options{
router, err := NewRouter(gorilla.NewRouter(mux), Options{
Openapi: getBaseSwagger(t),
})
require.NoError(t, err)
@@ -656,6 +658,14 @@ func TestResolveResponsesSchema(t *testing.T) {
type TestStruct struct {
Message string `json:"message,omitempty"`
}
type NestedTestStruct struct {
Notification string `json:"notification"`
NestedMapOfStructs map[string]TestStruct `json:"nestedMapOfStructs,omitempty"`
}
type ComplexTestStruct struct {
Communication string `json:"communication"`
MapOfStructs map[string]NestedTestStruct `json:"mapOfStructs,omitempty"`
}
tests := []struct {
name string
responsesSchema map[int]ContentValue
@@ -698,6 +708,79 @@ func TestResolveResponsesSchema(t *testing.T) {
}
}`,
},
{
name: "with complex schema",
responsesSchema: map[int]ContentValue{
200: {
Content: Content{
jsonType: {Value: &ComplexTestStruct{
Communication: "myCommunication",
MapOfStructs: map[string]NestedTestStruct{
"myProperty": {
Notification: "myNotification",
NestedMapOfStructs: map[string]TestStruct{
"myNestedProperty": {
Message: "myMessage",
},
},
},
},
}},
},
},
},
expectedErr: nil,
expectedJSON: `{
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"additionalProperties": false,
"properties": {
"communication": {
"type": "string"
},
"mapOfStructs": {
"additionalProperties": {
"additionalProperties": false,
"properties": {
"nestedMapOfStructs": {
"additionalProperties": {
"additionalProperties": false,
"properties": {
"message": {
"type": "string"
}
},
"type": "object"
},
"type": "object"
},
"notification": {
"type": "string"
}
},
"required": [
"notification"
],
"type": "object"
},
"type": "object"
}
},
"required": [
"communication"
],
"type": "object"
}
}
},
"description": ""
}
}
}`,
},
{
name: "with more status codes",
responsesSchema: map[int]ContentValue{
@@ -773,7 +856,7 @@ func TestResolveResponsesSchema(t *testing.T) {
}
mux := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mux), Options{
router, err := NewRouter(gorilla.NewRouter(mux), Options{
Openapi: getBaseSwagger(t),
})
require.NoError(t, err)
@@ -943,7 +1026,7 @@ func TestResolveParametersSchema(t *testing.T) {
}
mux := mux.NewRouter()
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mux), Options{
router, err := NewRouter(gorilla.NewRouter(mux), Options{
Openapi: getBaseSwagger(t),
})
require.NoError(t, err)

32
support/echo/echo.go Normal file
View File

@@ -0,0 +1,32 @@
package echo
import (
"github.com/davidebianchi/gswagger/apirouter"
"net/http"
"github.com/labstack/echo/v4"
)
type Route = *echo.Route
type echoRouter struct {
router *echo.Echo
}
func (r echoRouter) AddRoute(method string, path string, handler echo.HandlerFunc) Route {
return r.router.Add(method, path, handler)
}
func (r echoRouter) SwaggerHandler(contentType string, blob []byte) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Add("Content-Type", contentType)
return c.JSONBlob(http.StatusOK, blob)
}
}
func NewRouter(router *echo.Echo) apirouter.Router[echo.HandlerFunc, Route] {
return echoRouter{
router: router,
}
}

65
support/echo/echo_test.go Normal file
View File

@@ -0,0 +1,65 @@
package echo
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/require"
)
func TestGorillaMuxRouter(t *testing.T) {
echoRouter := echo.New()
ar := NewRouter(echoRouter)
t.Run("create a new api router", func(t *testing.T) {
require.Implements(t, (*apirouter.Router[echo.HandlerFunc, Route])(nil), ar)
})
t.Run("add new route", func(t *testing.T) {
route := ar.AddRoute(http.MethodGet, "/foo", func(c echo.Context) error {
return c.String(http.StatusOK, "")
})
require.IsType(t, route, &echo.Route{})
t.Run("router exposes correctly api", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/foo", nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
})
t.Run("router exposes api only to the specific method", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/foo", nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusMethodNotAllowed, w.Result().StatusCode)
})
})
t.Run("create openapi handler", func(t *testing.T) {
handlerFunc := ar.SwaggerHandler("text/html", []byte("some data"))
echoRouter.GET("/oas", handlerFunc)
t.Run("responds correctly to the API", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/oas", nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
require.Equal(t, "text/html", w.Result().Header.Get("Content-Type"))
body, err := io.ReadAll(w.Result().Body)
require.NoError(t, err)
require.Equal(t, "some data", string(body))
})
})
}

View File

@@ -0,0 +1,138 @@
package echo_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
oasEcho "github.com/davidebianchi/gswagger/support/echo"
swagger "github.com/davidebianchi/gswagger"
"github.com/getkin/kin-openapi/openapi3"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/require"
)
const (
swaggerOpenapiTitle = "test swagger title"
swaggerOpenapiVersion = "test swagger version"
)
type echoSwaggerRouter = swagger.Router[echo.HandlerFunc, *echo.Route]
func TestIntegration(t *testing.T) {
t.Run("router works correctly - echo", func(t *testing.T) {
echoRouter, _ := setupEchoSwagger(t)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
echoRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
t.Run("works correctly with subrouter - handles path prefix - echo", func(t *testing.T) {
eRouter, swaggerRouter := setupEchoSwagger(t)
subRouter, err := swaggerRouter.SubRouter(oasEcho.NewRouter(eRouter), swagger.SubRouterOptions{
PathPrefix: "/prefix",
})
require.NoError(t, err)
_, err = subRouter.AddRoute(http.MethodGet, "/foo", okHandler, swagger.Definitions{})
require.NoError(t, err)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
eRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("correctly call sub router", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/prefix/foo", nil)
eRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
})
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
eRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
}
func readBody(t *testing.T, requestBody io.ReadCloser) string {
t.Helper()
body, err := io.ReadAll(requestBody)
require.NoError(t, err)
return string(body)
}
func setupEchoSwagger(t *testing.T) (*echo.Echo, *echoSwaggerRouter) {
t.Helper()
context := context.Background()
e := echo.New()
router, err := swagger.NewRouter(oasEcho.NewRouter(e), swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
},
},
})
require.NoError(t, err)
operation := swagger.Operation{}
_, err = router.AddRawRoute(http.MethodGet, "/hello", okHandler, operation)
require.NoError(t, err)
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
return e, router
}
func okHandler(c echo.Context) error {
return c.String(http.StatusOK, "OK")
}

30
support/fiber/fiber.go Normal file
View File

@@ -0,0 +1,30 @@
package fiber
import (
"github.com/davidebianchi/gswagger/apirouter"
"github.com/gofiber/fiber/v2"
)
type HandlerFunc = fiber.Handler
type Route = fiber.Router
type fiberRouter struct {
router fiber.Router
}
func NewRouter(router fiber.Router) apirouter.Router[HandlerFunc, Route] {
return fiberRouter{
router: router,
}
}
func (r fiberRouter) AddRoute(method string, path string, handler HandlerFunc) Route {
return r.router.Add(method, path, handler)
}
func (r fiberRouter) SwaggerHandler(contentType string, blob []byte) HandlerFunc {
return func(c *fiber.Ctx) error {
c.Set("Content-Type", contentType)
return c.Send(blob)
}
}

View File

@@ -0,0 +1,63 @@
package fiber
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/require"
)
func TestFiberRouterSupport(t *testing.T) {
fiberRouter := fiber.New()
ar := NewRouter(fiberRouter)
t.Run("create a new api router", func(t *testing.T) {
require.Implements(t, (*apirouter.Router[HandlerFunc, Route])(nil), ar)
})
t.Run("add new route", func(t *testing.T) {
route := ar.AddRoute(http.MethodGet, "/foo", func(c *fiber.Ctx) error {
return c.SendStatus(http.StatusOK)
})
require.IsType(t, route, fiber.New())
t.Run("router exposes correctly api", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/foo", nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("router exposes api only to the specific method", func(t *testing.T) {
r := httptest.NewRequest(http.MethodPost, "/foo", nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
})
})
t.Run("create openapi handler", func(t *testing.T) {
handlerFunc := ar.SwaggerHandler("text/html", []byte("some data"))
fiberRouter.Get("/oas", handlerFunc)
t.Run("responds correctly to the API", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/oas", nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.Equal(t, "text/html", resp.Header.Get("Content-Type"))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "some data", string(body))
})
})
}

View File

@@ -0,0 +1,149 @@
package fiber_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
swagger "github.com/davidebianchi/gswagger"
oasFiber "github.com/davidebianchi/gswagger/support/fiber"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/require"
)
type SwaggerRouter = swagger.Router[oasFiber.HandlerFunc, oasFiber.Route]
const (
swaggerOpenapiTitle = "test swagger title"
swaggerOpenapiVersion = "test swagger version"
)
func TestWithFiber(t *testing.T) {
t.Run("router works correctly", func(t *testing.T) {
router, _ := setupSwagger(t)
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
resp, err := router.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
body := readBody(t, resp.Body)
require.Equal(t, "OK", body)
t.Run("and generate swagger", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
resp, err := router.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
body := readBody(t, resp.Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
t.Run("works correctly with subrouter - handles path prefix - gorilla mux", func(t *testing.T) {
fiberRouter, oasRouter := setupSwagger(t)
fiberRouter.Route("/foo", func(router fiber.Router) {
subRouter, err := oasRouter.SubRouter(oasFiber.NewRouter(router), swagger.SubRouterOptions{
PathPrefix: "/prefix",
})
require.NoError(t, err)
_, err = subRouter.AddRoute(http.MethodGet, "/nested", okHandler, swagger.Definitions{})
require.NoError(t, err)
})
oasRouter.AddRoute(http.MethodGet, "/foo", okHandler, swagger.Definitions{})
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
body := readBody(t, resp.Body)
require.Equal(t, "OK", body)
t.Run("correctly call router", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/foo", nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
body := readBody(t, resp.Body)
require.Equal(t, "OK", body)
})
t.Run("correctly call sub router", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/foo/prefix/nested", nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
body := readBody(t, resp.Body)
require.Equal(t, "OK", body)
})
t.Run("and generate swagger", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
resp, err := fiberRouter.Test(r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
body := readBody(t, resp.Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
}
func setupSwagger(t *testing.T) (*fiber.App, *SwaggerRouter) {
t.Helper()
context := context.Background()
fiberRouter := fiber.New()
router, err := swagger.NewRouter(oasFiber.NewRouter(fiberRouter), swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
},
},
})
require.NoError(t, err)
operation := swagger.Operation{}
_, err = router.AddRawRoute(http.MethodGet, "/hello", okHandler, operation)
require.NoError(t, err)
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
return fiberRouter, router
}
func okHandler(c *fiber.Ctx) error {
c.Status(http.StatusOK)
return c.SendString("OK")
}
func readBody(t *testing.T, requestBody io.ReadCloser) string {
t.Helper()
body, err := io.ReadAll(requestBody)
require.NoError(t, err)
return string(body)
}

View File

@@ -0,0 +1,118 @@
package gorilla_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
swagger "github.com/davidebianchi/gswagger"
"github.com/davidebianchi/gswagger/support/gorilla"
"github.com/stretchr/testify/require"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
)
func TestExample(t *testing.T) {
context := context.Background()
muxRouter := mux.NewRouter()
router, _ := swagger.NewRouter(gorilla.NewRouter(muxRouter), swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: "my title",
Version: "1.0.0",
},
},
})
okHandler := func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
type User struct {
Name string `json:"name" jsonschema:"title=The user name,required" jsonschema_extras:"example=Jane"`
PhoneNumber int `json:"phone" jsonschema:"title=mobile number of user"`
Groups []string `json:"groups,omitempty" jsonschema:"title=groups of the user,default=users"`
Address string `json:"address" jsonschema:"title=user address"`
}
type errorResponse struct {
Message string `json:"message"`
}
router.AddRoute(http.MethodPost, "/users", okHandler, swagger.Definitions{
RequestBody: &swagger.ContentValue{
Content: swagger.Content{
"application/json": {Value: User{}},
},
},
Responses: map[int]swagger.ContentValue{
201: {
Content: swagger.Content{
"text/html": {Value: ""},
},
},
401: {
Content: swagger.Content{
"application/json": {Value: &errorResponse{}},
},
Description: "invalid request",
},
},
})
router.AddRoute(http.MethodGet, "/users", okHandler, swagger.Definitions{
Responses: map[int]swagger.ContentValue{
200: {
Content: swagger.Content{
"application/json": {Value: &[]User{}},
},
},
},
})
carSchema := openapi3.NewObjectSchema().WithProperties(map[string]*openapi3.Schema{
"foo": openapi3.NewStringSchema(),
"bar": openapi3.NewIntegerSchema().WithMax(15).WithMin(5),
})
requestBody := openapi3.NewRequestBody().WithJSONSchema(carSchema)
operation := swagger.NewOperation()
operation.AddRequestBody(requestBody)
router.AddRawRoute(http.MethodPost, "/cars", okHandler, operation)
_, err := router.AddRoute(http.MethodGet, "/users/{userId}", okHandler, swagger.Definitions{
Querystring: swagger.ParameterValue{
"query": {
Schema: &swagger.Schema{Value: ""},
},
},
})
require.NoError(t, err)
_, err = router.AddRoute(http.MethodGet, "/cars/{carId}/drivers/{driverId}", okHandler, swagger.Definitions{})
require.NoError(t, err)
router.GenerateAndExposeOpenapi()
t.Run("correctly exposes documentation", func(t *testing.T) {
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
require.Equal(t, "application/json", w.Result().Header.Get("Content-Type"))
body, err := io.ReadAll(w.Result().Body)
require.NoError(t, err)
expected, err := os.ReadFile("./testdata/examples-users.json")
require.NoError(t, err)
require.JSONEq(t, string(expected), string(body), "actual json data: %s", body)
})
}

View File

@@ -0,0 +1,35 @@
package gorilla
import (
"github.com/davidebianchi/gswagger/apirouter"
"net/http"
"github.com/gorilla/mux"
)
// HandlerFunc is the http type handler used by gorilla/mux
type HandlerFunc func(w http.ResponseWriter, req *http.Request)
type Route = *mux.Route
type gorillaRouter struct {
router *mux.Router
}
func (r gorillaRouter) AddRoute(method string, path string, handler HandlerFunc) Route {
return r.router.HandleFunc(path, handler).Methods(method)
}
func (r gorillaRouter) SwaggerHandler(contentType string, blob []byte) HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", contentType)
w.WriteHeader(http.StatusOK)
w.Write(blob)
}
}
func NewRouter(router *mux.Router) apirouter.Router[HandlerFunc, Route] {
return gorillaRouter{
router: router,
}
}

View File

@@ -1,20 +1,22 @@
package apirouter
package gorilla
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
)
func TestGorillaMuxRouter(t *testing.T) {
muxRouter := mux.NewRouter()
ar := NewGorillaMuxRouter(muxRouter)
ar := NewRouter(muxRouter)
t.Run("create a new api router", func(t *testing.T) {
require.Implements(t, (*Router)(nil), ar)
require.Implements(t, (*apirouter.Router[HandlerFunc, Route])(nil), ar)
})
t.Run("add new route", func(t *testing.T) {
@@ -22,9 +24,7 @@ func TestGorillaMuxRouter(t *testing.T) {
w.WriteHeader(200)
w.Write(nil)
})
_, ok := route.(*mux.Route)
require.True(t, ok)
require.IsType(t, route, &mux.Route{})
t.Run("router exposes correctly api", func(t *testing.T) {
w := httptest.NewRecorder()
@@ -44,4 +44,23 @@ func TestGorillaMuxRouter(t *testing.T) {
require.Equal(t, http.StatusMethodNotAllowed, w.Result().StatusCode)
})
})
t.Run("create openapi handler", func(t *testing.T) {
handlerFunc := ar.SwaggerHandler("text/html", []byte("some data"))
muxRouter.HandleFunc("/oas", handlerFunc).Methods(http.MethodGet)
t.Run("responds correctly to the API", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/oas", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
require.Equal(t, "text/html", w.Result().Header.Get("Content-Type"))
body, err := io.ReadAll(w.Result().Body)
require.NoError(t, err)
require.Equal(t, "some data", string(body))
})
})
}

View File

@@ -0,0 +1,139 @@
package gorilla_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
swagger "github.com/davidebianchi/gswagger"
"github.com/davidebianchi/gswagger/support/gorilla"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
)
const (
swaggerOpenapiTitle = "test swagger title"
swaggerOpenapiVersion = "test swagger version"
)
type SwaggerRouter = swagger.Router[gorilla.HandlerFunc, gorilla.Route]
func TestGorillaIntegration(t *testing.T) {
t.Run("router works correctly", func(t *testing.T) {
muxRouter, _ := setupSwagger(t)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
t.Run("works correctly with subrouter - handles path prefix - gorilla mux", func(t *testing.T) {
muxRouter, swaggerRouter := setupSwagger(t)
muxSubRouter := muxRouter.NewRoute().Subrouter()
subRouter, err := swaggerRouter.SubRouter(gorilla.NewRouter(muxSubRouter), swagger.SubRouterOptions{
PathPrefix: "/prefix",
})
require.NoError(t, err)
_, err = subRouter.AddRoute(http.MethodGet, "/foo", okHandler, swagger.Definitions{})
require.NoError(t, err)
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/hello", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
t.Run("correctly call sub router", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/prefix/foo", nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "OK", body)
})
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
body := readBody(t, w.Result().Body)
require.Equal(t, "{\"components\":{},\"info\":{\"title\":\"test swagger title\",\"version\":\"test swagger version\"},\"openapi\":\"3.0.0\",\"paths\":{\"/hello\":{\"get\":{\"responses\":{\"default\":{\"description\":\"\"}}}}}}", body)
})
})
}
func readBody(t *testing.T, requestBody io.ReadCloser) string {
t.Helper()
body, err := io.ReadAll(requestBody)
require.NoError(t, err)
return string(body)
}
func setupSwagger(t *testing.T) (*mux.Router, *SwaggerRouter) {
t.Helper()
context := context.Background()
muxRouter := mux.NewRouter()
router, err := swagger.NewRouter(gorilla.NewRouter(muxRouter), swagger.Options{
Context: context,
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
},
},
})
require.NoError(t, err)
operation := swagger.Operation{}
_, err = router.AddRawRoute(http.MethodGet, "/hello", okHandler, operation)
require.NoError(t, err)
err = router.GenerateAndExposeOpenapi()
require.NoError(t, err)
return muxRouter, router
}
func okHandler(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`OK`))
}

View File

@@ -0,0 +1,186 @@
{
"components": {},
"info": {
"title": "my title",
"version": "1.0.0"
},
"openapi": "3.0.0",
"paths": {
"/cars/{carId}/drivers/{driverId}": {
"get": {
"parameters": [
{
"in": "path",
"name": "carId",
"required": true,
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "driverId",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"default": {
"description": ""
}
}
}
},
"/users": {
"get": {
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"additionalProperties": false,
"properties": {
"address": {
"title": "user address",
"type": "string"
},
"groups": {
"default": [
"users"
],
"items": {
"type": "string"
},
"title": "groups of the user",
"type": "array"
},
"name": {
"example": "Jane",
"title": "The user name",
"type": "string"
},
"phone": {
"title": "mobile number of user",
"type": "integer"
}
},
"required": [
"name",
"phone",
"address"
],
"type": "object"
},
"type": "array"
}
}
},
"description": ""
}
}
},
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"additionalProperties": false,
"properties": {
"address": {
"title": "user address",
"type": "string"
},
"groups": {
"default": [
"users"
],
"items": {
"type": "string"
},
"title": "groups of the user",
"type": "array"
},
"name": {
"example": "Jane",
"title": "The user name",
"type": "string"
},
"phone": {
"title": "mobile number of user",
"type": "integer"
}
},
"required": [
"name",
"phone",
"address"
],
"type": "object"
}
}
}
},
"responses": {
"201": {
"content": {
"text/html": {
"schema": {
"type": "string"
}
}
},
"description": ""
},
"401": {
"content": {
"application/json": {
"schema": {
"additionalProperties": false,
"properties": {
"message": {
"type": "string"
}
},
"required": [
"message"
],
"type": "object"
}
}
},
"description": "invalid request"
}
}
}
},
"/users/{userId}": {
"get": {
"parameters": [
{
"in": "path",
"name": "userId",
"required": true,
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"default": {
"description": ""
}
}
}
}
}
}