32 Commits

Author SHA1 Message Date
Davide Bianchi
7615365291 docs: update CHANGELOG 2021-10-16 21:59:57 +02:00
Davide Bianchi
6411ef6e66 Merge branch 'main' of github.com:davidebianchi/gswagger 2021-10-16 21:59:03 +02:00
Davide Bianchi
49fd48c375 docs: update README 2021-10-16 21:58:54 +02:00
Davide Bianchi
9f3698cf59 Update README.md 2021-10-16 21:54:37 +02:00
Davide Bianchi
98ed6678e6 Update README.md 2021-10-16 21:54:28 +02:00
Davide Bianchi
0075df7fc4 Update README.md 2021-10-16 21:39:21 +02:00
Davide Bianchi
66b593f3f6 Update README.md 2021-10-16 21:38:58 +02:00
Davide Bianchi
30bf3ec60e docs: change readme 2021-10-16 21:01:59 +02:00
Davide Bianchi
ff30e87701 Merge pull request #33 from davidebianchi/support-router-interface 2021-10-16 21:00:23 +02:00
Davide Bianchi
07568cabc0 test: fix test 2021-10-16 20:59:27 +02:00
Davide Bianchi
2cffb94971 feat: improve documentation 2021-10-16 20:56:15 +02:00
Davide Bianchi
ec86fe1571 docs: improved docs 2021-10-16 20:12:39 +02:00
Davide Bianchi
5d1b1b795d fix: remove first level export of new router 2021-10-16 20:03:18 +02:00
Davide Bianchi
b964b3c3ca feat: support custom router interface 2021-10-16 20:00:52 +02:00
Davide Bianchi
4e8ccbd66d fix: fix warning in test 2021-10-16 19:27:48 +02:00
Davide Bianchi
9aaf041909 Merge pull request #32 from davidebianchi/upgrade-deps 2021-10-16 19:25:27 +02:00
Davide Bianchi
b1dc480411 run go mod tidy 2021-10-16 19:24:31 +02:00
Davide Bianchi
3d9ad0fb5f feat: upgrade deps 2021-10-16 19:23:52 +02:00
Davide Bianchi
5f07fcb394 Update README.md 2021-10-16 19:05:25 +02:00
Davide Bianchi
985e3dfd03 Update dependabot.yml 2021-10-16 19:04:21 +02:00
Davide Bianchi
67073bb3bb Merge pull request #30 from davidebianchi/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-05-18 21:44:48 +02:00
dependabot-preview[bot]
ba069411b1 Upgrade to GitHub-native Dependabot 2021-04-29 16:07:20 +00:00
Davide Bianchi
a950dcc879 Merge pull request #28 from davidebianchi/dependabot/go_modules/github.com/getkin/kin-openapi-0.60.0
chore(deps): bump github.com/getkin/kin-openapi from 0.53.0 to 0.60.0
2021-04-26 18:54:00 +02:00
dependabot-preview[bot]
1fe64517bd chore(deps): bump github.com/getkin/kin-openapi from 0.53.0 to 0.60.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.53.0 to 0.60.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.53.0...v0.60.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-26 06:34:55 +00:00
Davide Bianchi
651f2d0478 Merge pull request #25 from davidebianchi/dependabot/go_modules/github.com/getkin/kin-openapi-0.53.0
chore(deps): bump github.com/getkin/kin-openapi from 0.48.0 to 0.53.0
2021-04-03 18:57:28 +02:00
dependabot-preview[bot]
22cf8cdbc4 chore(deps): bump github.com/getkin/kin-openapi from 0.48.0 to 0.53.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.48.0 to 0.53.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.48.0...v0.53.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-24 06:58:26 +00:00
Davide Bianchi
2d310b15e1 Merge pull request #21 from davidebianchi/dependabot/go_modules/github.com/getkin/kin-openapi-0.48.0
chore(deps): bump github.com/getkin/kin-openapi from 0.39.0 to 0.48.0
2021-03-07 18:14:05 +01:00
dependabot-preview[bot]
b77a71d0e4 chore(deps): bump github.com/getkin/kin-openapi from 0.39.0 to 0.48.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.39.0 to 0.48.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.39.0...v0.48.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-05 07:01:19 +00:00
Davide Bianchi
7f240250d5 Create CHANGELOG.md 2021-02-07 18:19:23 +01:00
Davide Bianchi
3b0f5b33eb Merge pull request #15 from davidebianchi/dependabot/go_modules/github.com/stretchr/testify-1.7.0
chore(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0
2021-02-07 18:17:45 +01:00
dependabot-preview[bot]
6c8a4b5cdf chore(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.6.1...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-07 17:04:21 +00:00
Davide Bianchi
04774f8ed5 update deps 2021-02-07 18:02:52 +01:00
13 changed files with 233 additions and 98 deletions

7
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: gomod
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10

24
CHANGELOG.md Normal file
View File

@@ -0,0 +1,24 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.2.0 - 16-10-2021
### BREAKING CHANGES
Introduced the `apirouter.Router` interface, which abstract the used router.
Changed function are:
- Router struct now support `apirouter.Router` interface
- NewRouter function accepted router is an `apirouter.Router`
- AddRawRoute now accept `apirouter.Handler` iterface
- AddRawRoute returns an interface instead of *mux.Router. This interface is the Route returned by specific Router
- AddRoute now accept `apirouter.Handler` iterface
- AddRoute returns an interface instead of *mux.Router. This interface is the Route returned by specific Router
## v0.1.0
Initial release

View File

@@ -4,31 +4,39 @@
[![Go Report Card][go-report-card]][go-report-card-link]
[![GoDoc][godoc-svg]][godoc-link]
# Gorilla Swagger
# gswagger
</div>
Generate a swagger dynamically based on the types used to handle request and response.
Generate an openapi spec dynamically based on the types used to handle request and response.
It works with [gorilla-mux](https://github.com/gorilla/mux) and uses [kin-openapi]
to automatically generate and serve a swagger file.
It works with any router which support handler net/http HandlerFunc compatible.
The routers supported out of the box are:
- [gorilla-mux](https://github.com/gorilla/mux)
This lib uses [kin-openapi] to automatically generate and serve a swagger file.
To convert struct to schemas, we use [jsonschema] library.
The struct must contains the appropriate struct tags to be inserted in json schema to generate the schema dynamically.
It is always possible to add a totally custom swagger schema using [kin-openapi]
It is always possible to add a totally custom swagger schema using [kin-openapi].
## Usage
An example usage of this lib:
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()
r := mux.NewRouter()
router, _ := swagger.NewRouter(r, gswagger.Options{
muxRouter := mux.NewRouter()
router, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(muxRouter), swagger.Options{
Context: context,
Openapi: &openapi3.Swagger{
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: "my swagger title",
Title: "my title",
Version: "1.0.0",
},
},
@@ -39,20 +47,31 @@ okHandler := func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("OK"))
}
router.AddRoute(http.MethodPost, "/users", okHandler, Definitions{
RequestBody: &gswagger.ContentValue{
Content: gswagger.Content{
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 Users []User
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]gswagger.ContentValue{
Responses: map[int]swagger.ContentValue{
201: {
Content: gswagger.Content{
Content: swagger.Content{
"text/html": {Value: ""},
},
},
401: {
Content: gswagger.Content{
Content: swagger.Content{
"application/json": {Value: &errorResponse{}},
},
Description: "invalid request",
@@ -60,11 +79,11 @@ router.AddRoute(http.MethodPost, "/users", okHandler, Definitions{
},
})
router.AddRoute(http.MethodGet, "/users", okHandler, Definitions{
Responses: map[int]ContentValue{
router.AddRoute(http.MethodGet, "/users", okHandler, swagger.Definitions{
Responses: map[int]swagger.ContentValue{
200: {
Content: Content{
"application/json": {Value: &Users{}},
Content: swagger.Content{
"application/json": {Value: &[]User{}},
},
},
},
@@ -101,7 +120,7 @@ You can create manually a swagger with `oneOf` using the `AddRawRoute` method, o
*Formats*:
* `uuid` is unsupported by [kin-openapi]
- `uuid` is unsupported by [kin-openapi]
## Versioning
@@ -117,3 +136,4 @@ see the [tags on this repository](https://github.com/davidebianchi/gswagger/tags
[godoc-link]: https://godoc.org/github.com/davidebianchi/gswagger
[go-report-card]: https://goreportcard.com/badge/github.com/davidebianchi/gswagger
[go-report-card-link]: https://goreportcard.com/report/github.com/davidebianchi/gswagger
[semver]: https://semver.org/

17
apirouter/gorilla.go Normal file
View File

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

47
apirouter/gorilla_test.go Normal file
View File

@@ -0,0 +1,47 @@
package apirouter
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
)
func TestGorillaMuxRouter(t *testing.T) {
muxRouter := mux.NewRouter()
ar := NewGorillaMuxRouter(muxRouter)
t.Run("create a new api router", func(t *testing.T) {
require.Implements(t, (*Router)(nil), ar)
})
t.Run("add new route", func(t *testing.T) {
route := ar.AddRoute("/foo", http.MethodGet, func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(200)
w.Write(nil)
})
_, ok := route.(*mux.Route)
require.True(t, ok)
t.Run("router exposes correctly api", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/foo", nil)
muxRouter.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)
muxRouter.ServeHTTP(w, r)
require.Equal(t, http.StatusMethodNotAllowed, w.Result().StatusCode)
})
})
}

12
apirouter/router.go Normal file
View File

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

11
go.mod
View File

@@ -3,11 +3,12 @@ module github.com/davidebianchi/gswagger
go 1.15
require (
github.com/alecthomas/jsonschema v0.0.0-20201129101101-7b852d451add
github.com/getkin/kin-openapi v0.35.0
github.com/alecthomas/jsonschema v0.0.0-20210920000243-787cd8204a0d
github.com/getkin/kin-openapi v0.79.0
github.com/ghodss/yaml v1.0.0
github.com/go-openapi/swag v0.19.13 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/gorilla/mux v1.8.0
github.com/iancoleman/orderedmap v0.1.0 // indirect
github.com/stretchr/testify v1.6.1
github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/stretchr/testify v1.7.0
)

31
go.sum
View File

@@ -1,26 +1,23 @@
github.com/alecthomas/jsonschema v0.0.0-20201129101101-7b852d451add h1:UWj2AVW7oygpatGgJRy6rvNBcl+J7u8vl6qTV+XNWzA=
github.com/alecthomas/jsonschema v0.0.0-20201129101101-7b852d451add/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
github.com/alecthomas/jsonschema v0.0.0-20210920000243-787cd8204a0d h1:sUHuJQ3zwLmUgKM1v51WLWRtoy9r+hc/m7DoNftpUdA=
github.com/alecthomas/jsonschema v0.0.0-20210920000243-787cd8204a0d/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
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.35.0 h1:YoJusew7Es36hSpnEx3gRL2DGj/82T3j3Akbs9VIXDQ=
github.com/getkin/kin-openapi v0.35.0/go.mod h1:ZJSfy1PxJv2QQvH9EdBj3nupRTVvV42mkW6zKUlRBwk=
github.com/getkin/kin-openapi v0.79.0 h1:YLZIgIhZLq9z5WFHHIK+oWORRfn6jjwr7qN0xak0xbE=
github.com/getkin/kin-openapi v0.79.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
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=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.13 h1:233UVgMy1DlmCYYfOiFpta6e2urloh+sEs5id6lyzog=
github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
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/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 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.1.0 h1:2orAxZBJsvimgEBmMWfXaFlzSG2fbQil5qzP3F6cCkg=
github.com/iancoleman/orderedmap v0.1.0/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/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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -29,33 +26,29 @@ 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 h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
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.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
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=

View File

@@ -1,4 +1,4 @@
package swagger
package swagger_test
import (
"context"
@@ -8,6 +8,8 @@ import (
"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/stretchr/testify/require"
@@ -34,7 +36,7 @@ func TestIntegration(t *testing.T) {
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, DefaultJSONDocumentationPath, nil)
r := httptest.NewRequest(http.MethodGet, swagger.DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, r)
@@ -61,9 +63,9 @@ func setupSwagger(t *testing.T) *mux.Router {
context := context.Background()
muxRouter := mux.NewRouter()
router, err := NewRouter(muxRouter, Options{
router, err := swagger.NewRouter(apirouter.NewGorillaMuxRouter(muxRouter), swagger.Options{
Context: context,
Openapi: &openapi3.Swagger{
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
@@ -76,7 +78,7 @@ func setupSwagger(t *testing.T) *mux.Router {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`OK`))
}
operation := Operation{}
operation := swagger.Operation{}
_, err = router.AddRawRoute(http.MethodGet, "/hello", handler, operation)
require.NoError(t, err)

24
main.go
View File

@@ -7,9 +7,9 @@ import (
"net/http"
"strings"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/getkin/kin-openapi/openapi3"
"github.com/ghodss/yaml"
"github.com/gorilla/mux"
)
var (
@@ -27,10 +27,12 @@ const (
defaultOpenapiVersion = "3.0.0"
)
// Router handle the gorilla mux router and the swagger schema
// Router handle the api router and the swagger schema.
// api router supported out of the box are:
// - gorilla mux
type Router struct {
router *mux.Router
swaggerSchema *openapi3.Swagger
router apirouter.Router
swaggerSchema *openapi3.T
context context.Context
jsonDocumentationPath string
yamlDocumentationPath string
@@ -39,7 +41,7 @@ type Router struct {
// Options to be passed to create the new router and swagger
type Options struct {
Context context.Context
Openapi *openapi3.Swagger
Openapi *openapi3.T
// JSONDocumentationPath is the path exposed by json endpoint. Default to /documentation/json.
JSONDocumentationPath string
// YAMLDocumentationPath is the path exposed by yaml endpoint. Default to /documentation/yaml.
@@ -47,7 +49,7 @@ type Options struct {
}
// NewRouter generate new router with swagger. Default to OpenAPI 3.0.0
func NewRouter(router *mux.Router, options Options) (*Router, error) {
func NewRouter(router apirouter.Router, options Options) (*Router, error) {
swagger, err := generateNewValidSwagger(options.Openapi)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrValidatingSwagger, err)
@@ -83,7 +85,7 @@ func NewRouter(router *mux.Router, options Options) (*Router, error) {
}, nil
}
func generateNewValidSwagger(swagger *openapi3.Swagger) (*openapi3.Swagger, error) {
func generateNewValidSwagger(swagger *openapi3.T) (*openapi3.T, error) {
if swagger == nil {
return nil, fmt.Errorf("swagger is required")
}
@@ -118,21 +120,21 @@ func (r Router) GenerateAndExposeSwagger() error {
if err != nil {
return fmt.Errorf("%w json marshal: %s", ErrGenerateSwagger, err)
}
r.router.HandleFunc(r.jsonDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
r.router.AddRoute(r.jsonDocumentationPath, http.MethodGet, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(jsonSwagger)
}).Methods(http.MethodGet)
})
yamlSwagger, err := yaml.JSONToYAML(jsonSwagger)
if err != nil {
return fmt.Errorf("%w yaml marshal: %s", ErrGenerateSwagger, err)
}
r.router.HandleFunc(r.yamlDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
r.router.AddRoute(r.yamlDocumentationPath, http.MethodGet, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write(yamlSwagger)
}).Methods(http.MethodGet)
})
return nil
}

View File

@@ -3,25 +3,27 @@ package swagger
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
)
func TestNewRouter(t *testing.T) {
mRouter := mux.NewRouter()
mRouter := apirouter.NewGorillaMuxRouter(mux.NewRouter())
info := &openapi3.Info{
Title: "my title",
Version: "my version",
}
openapi := &openapi3.Swagger{
openapi := &openapi3.T{
Info: info,
Paths: openapi3.Paths{},
}
@@ -117,7 +119,7 @@ func TestNewRouter(t *testing.T) {
func TestGenerateValidSwagger(t *testing.T) {
t.Run("not ok - empty swagger info", func(t *testing.T) {
swagger := &openapi3.Swagger{}
swagger := &openapi3.T{}
swagger, err := generateNewValidSwagger(swagger)
require.Nil(t, swagger)
@@ -125,7 +127,7 @@ func TestGenerateValidSwagger(t *testing.T) {
})
t.Run("not ok - empty info title", func(t *testing.T) {
swagger := &openapi3.Swagger{
swagger := &openapi3.T{
Info: &openapi3.Info{},
}
@@ -135,7 +137,7 @@ func TestGenerateValidSwagger(t *testing.T) {
})
t.Run("not ok - empty info version", func(t *testing.T) {
swagger := &openapi3.Swagger{
swagger := &openapi3.T{
Info: &openapi3.Info{
Title: "title",
},
@@ -147,7 +149,7 @@ func TestGenerateValidSwagger(t *testing.T) {
})
t.Run("ok - custom swagger", func(t *testing.T) {
swagger := &openapi3.Swagger{
swagger := &openapi3.T{
Info: &openapi3.Info{},
}
@@ -167,13 +169,13 @@ func TestGenerateValidSwagger(t *testing.T) {
Title: "my title",
Version: "my version",
}
swagger := &openapi3.Swagger{
swagger := &openapi3.T{
Info: info,
}
swagger, err := generateNewValidSwagger(swagger)
require.NoError(t, err)
require.Equal(t, &openapi3.Swagger{
require.Equal(t, &openapi3.T{
OpenAPI: defaultOpenapiVersion,
Info: info,
Paths: openapi3.Paths{},
@@ -184,8 +186,8 @@ 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(mRouter, Options{
Openapi: &openapi3.Swagger{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
Openapi: &openapi3.T{
Info: &openapi3.Info{
Title: "title",
Version: "version",
@@ -207,12 +209,13 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("correctly expose json documentation from loaded swagger file", func(t *testing.T) {
mRouter := mux.NewRouter()
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("testdata/users_employees.json")
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(mRouter, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
Openapi: swagger,
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
@@ -233,13 +236,14 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("correctly expose json documentation from loaded swagger file - custom path", func(t *testing.T) {
mRouter := mux.NewRouter()
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("testdata/users_employees.json")
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(mRouter, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
Openapi: swagger,
JSONDocumentationPath: "/custom/path",
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
@@ -260,12 +264,13 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("correctly expose yaml documentation from loaded swagger file", func(t *testing.T) {
mRouter := mux.NewRouter()
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("testdata/users_employees.json")
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(mRouter, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
Openapi: swagger,
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
@@ -286,13 +291,14 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
t.Run("correctly expose yaml documentation from loaded swagger file - custom path", func(t *testing.T) {
mRouter := mux.NewRouter()
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("testdata/users_employees.json")
swagger, err := openapi3.NewLoader().LoadFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(mRouter, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mRouter), Options{
Openapi: swagger,
YAMLDocumentationPath: "/custom/path",
})
require.NoError(t, err)
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
@@ -310,3 +316,12 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.YAMLEq(t, string(expected), body, string(body))
})
}
func readBody(t *testing.T, requestBody io.ReadCloser) string {
t.Helper()
body, err := ioutil.ReadAll(requestBody)
require.NoError(t, err)
return string(body)
}

View File

@@ -3,12 +3,11 @@ package swagger
import (
"errors"
"fmt"
"net/http"
"sort"
"github.com/alecthomas/jsonschema"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
)
var (
@@ -22,12 +21,9 @@ var (
ErrQuerystring = errors.New("errors generating querystring schema")
)
// Handler is the http type handler
type Handler func(w http.ResponseWriter, req *http.Request)
// 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, path string, handler Handler, operation Operation) (*mux.Route, error) {
func (r Router) AddRawRoute(method string, path string, handler apirouter.HandlerFunc, operation Operation) (interface{}, error) {
op := operation.Operation
if op != nil {
err := operation.Validate(r.context)
@@ -42,10 +38,8 @@ func (r Router) AddRawRoute(method string, path string, handler Handler, operati
}
r.swaggerSchema.AddOperation(path, method, op)
return r.router.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
// Handle, when content-type is json, the request/response marshalling? Maybe with a specific option.
handler(w, req)
}).Methods(method), nil
// Handle, when content-type is json, the request/response marshalling? Maybe with a specific option.
return r.router.AddRoute(path, method, handler), nil
}
// Content is the type of a content.
@@ -90,7 +84,7 @@ const (
)
// AddRoute add a route with json schema inferted by passed schema.
func (r Router) AddRoute(method string, path string, handler Handler, schema Definitions) (*mux.Route, error) {
func (r Router) AddRoute(method string, path string, handler apirouter.HandlerFunc, schema Definitions) (interface{}, error) {
operation := NewOperation()
operation.Responses = make(openapi3.Responses)

View File

@@ -8,6 +8,7 @@ import (
"net/http/httptest"
"testing"
"github.com/davidebianchi/gswagger/apirouter"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
@@ -410,7 +411,7 @@ func TestAddRoutes(t *testing.T) {
context := context.Background()
r := mux.NewRouter()
router, err := NewRouter(r, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(r), Options{
Context: context,
Openapi: getBaseSwagger(t),
})
@@ -601,7 +602,7 @@ func TestResolveRequestBodySchema(t *testing.T) {
}
mux := mux.NewRouter()
router, err := NewRouter(mux, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mux), Options{
Openapi: getBaseSwagger(t),
})
require.NoError(t, err)
@@ -744,7 +745,7 @@ func TestResolveResponsesSchema(t *testing.T) {
}
mux := mux.NewRouter()
router, err := NewRouter(mux, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mux), Options{
Openapi: getBaseSwagger(t),
})
require.NoError(t, err)
@@ -914,7 +915,7 @@ func TestResolveParametersSchema(t *testing.T) {
}
mux := mux.NewRouter()
router, err := NewRouter(mux, Options{
router, err := NewRouter(apirouter.NewGorillaMuxRouter(mux), Options{
Openapi: getBaseSwagger(t),
})
require.NoError(t, err)
@@ -936,13 +937,13 @@ func TestResolveParametersSchema(t *testing.T) {
}
}
func getBaseSwagger(t *testing.T) *openapi3.Swagger {
func getBaseSwagger(t *testing.T) *openapi3.T {
t.Helper()
return &openapi3.Swagger{
return &openapi3.T{
Info: &openapi3.Info{
Title: swaggerOpenapiTitle,
Version: swaggerOpenapiVersion,
Title: "test swagger title",
Version: "test swagger version",
},
}
}