Skip to content

Commit 18b02da

Browse files
authored
Merge pull request #1 from alexferl/add_tests_docs
added tests and docs
2 parents fad9c45 + 3874605 commit 18b02da

File tree

10 files changed

+740
-48
lines changed

10 files changed

+740
-48
lines changed

README.md

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,162 @@
1-
# echo-openapi
1+
# echo-openapi [![Go Report Card](https://goreportcard.com/badge/github.com/alexferl/echo-openapi)](https://goreportcard.com/report/github.com/alexferl/echo-openapi) [![codecov](https://codecov.io/gh/alexferl/echo-openapi/branch/master/graph/badge.svg)](https://codecov.io/gh/alexferl/echo-openapi)
2+
3+
An [OpenAPI](https://www.openapis.org/) middleware for the [Echo](https://github.com/labstack/echo) framework using
4+
[getkin/kin-openapi](https://github.com/getkin/kin-openapi) to validate HTTP requests and responses.
5+
6+
## Installing
7+
```shell
8+
go get github.com/alexferl/echo-openapi
9+
```
10+
11+
## Using
12+
13+
### Code example
14+
```go
15+
package main
16+
17+
import (
18+
"net/http"
19+
20+
mw "github.com/alexferl/echo-openapi"
21+
"github.com/labstack/echo/v4"
22+
)
23+
24+
/*
25+
# openapi.yaml
26+
openapi: 3.0.4
27+
info:
28+
version: 1.0.0
29+
title: Test API
30+
description: A test API
31+
paths:
32+
/hello:
33+
post:
34+
description: Hello
35+
parameters:
36+
- name: message
37+
in: query
38+
required: true
39+
schema:
40+
type: string
41+
minLength: 1
42+
maxLength: 100
43+
responses:
44+
'200':
45+
description: Successful response
46+
content:
47+
application/json:
48+
schema:
49+
type: object
50+
additionalProperties: false
51+
required:
52+
- message
53+
properties:
54+
message:
55+
type: string
56+
description: Welcome message
57+
minLength: 4
58+
*/
59+
60+
type Handler struct {
61+
*mw.Handler
62+
}
63+
64+
func (h *Handler) Hello(c echo.Context) error {
65+
msg := c.QueryParam("message")
66+
return h.Validate(c, http.StatusOK, echo.Map{"message": msg})
67+
}
68+
69+
func main() {
70+
e := echo.New()
71+
72+
h := &Handler{mw.NewHandler()}
73+
e.Add(http.MethodPost, "/hello", h.Hello)
74+
75+
e.Use(mw.OpenAPI("./path/to/openapi.yaml"))
76+
77+
e.Logger.Fatal(e.Start("localhost:1323"))
78+
}
79+
```
80+
Send an invalid request to test request validation:
81+
```shell
82+
curl -i -X POST http://localhost:1323/hello
83+
HTTP/1.1 422 Unprocessable Entity
84+
Content-Type: application/json; charset=UTF-8
85+
Date: Mon, 07 Nov 2022 01:13:40 GMT
86+
Content-Length: 117
87+
88+
{"message":"Validation error","errors":["parameter 'message' in query has an error: value is required but missing"]}
89+
```
90+
91+
Send a valid request:
92+
```shell
93+
curl -i -X POST http://localhost:1323/hello\?message\=hello
94+
HTTP/1.1 200 OK
95+
Content-Type: application/json
96+
Date: Mon, 07 Nov 2022 01:22:59 GMT
97+
Content-Length: 19
98+
99+
{"message":"hello"}
100+
```
101+
102+
Send a valid request with an invalid response:
103+
```shell
104+
curl -i -X POST http://localhost:1323/hello\?message\=a
105+
HTTP/1.1 500 Internal Server Error
106+
Content-Type: application/json
107+
Date: Mon, 07 Nov 2022 01:16:43 GMT
108+
Content-Length: 36
109+
110+
{"message":"Internal Server Error"}
111+
```
112+
You should also have the following in the server's log to help you debug your schema:
113+
```shell
114+
{"time":"2022-11-06T20:16:43.914629-05:00","level":"ERROR","prefix":"echo","file":"handler.go","line":"133","message":"response body doesn't match the schema: Error at \"/message\": minimum string length is 4\nSchema:\n {\n \"description\": \"Welcome message\",\n \"minLength\": 4,\n \"type\": \"string\"\n }\n\nValue:\n \"hi\"\n"}
115+
```
116+
117+
### Configuration
118+
```go
119+
type Config struct {
120+
// Skipper defines a function to skip middleware.
121+
Skipper middleware.Skipper
122+
123+
// Schema defines the OpenAPI that will be loaded and
124+
// that the request and responses will be validated against.
125+
// Required.
126+
Schema string
127+
128+
// ContextKey defines the key that will be used to store the validator
129+
// on the echo.Context when the request is successfully validated.
130+
// Optional. Defaults to "validator".
131+
ContextKey string
132+
133+
// ExemptRoutes defines routes and methods that don't require tokens.
134+
// Optional.
135+
ExemptRoutes map[string][]string
136+
}
137+
138+
type HandlerConfig struct {
139+
// ContentType sets the Content-Type header of the response.
140+
// Optional. Defaults to "application/json".
141+
ContentType string
142+
143+
// ValidatorKey defines the key that will be used to read the
144+
// *openapi3filter.RequestValidationInput from the echo.Context
145+
// set by the middleware.
146+
// Optional. Defaults to "validator".
147+
ValidatorKey string
148+
149+
// ExcludeRequestBody makes Validate skips request body validation.
150+
// Optional. Defaults to false.
151+
ExcludeRequestBody bool
152+
153+
// ExcludeResponseBody makes Validate skips response body validation.
154+
// Optional. Defaults to false.
155+
ExcludeResponseBody bool
156+
157+
// IncludeResponseStatus so ValidateResponse fails on response
158+
// statuses not defined in the OpenAPI spec.
159+
// Optional. Defaults to true.
160+
IncludeResponseStatus bool
161+
}
162+
```

codecov.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
coverage:
2+
range: 50..75
3+
round: down
4+
precision: 2

fixtures/invalid.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
openapi: 3.0.4
2+
invalid:

fixtures/openapi.yaml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
openapi: 3.0.4
2+
info:
3+
version: 1.0.0
4+
title: Test API
5+
description: A test API
6+
paths:
7+
/:
8+
get:
9+
description: Root
10+
responses:
11+
'200':
12+
description: Successful response
13+
content:
14+
application/json:
15+
schema:
16+
type: object
17+
additionalProperties: false
18+
required:
19+
- message
20+
properties:
21+
message:
22+
type: string
23+
description: Welcome message
24+
/no-content:
25+
post:
26+
description: No content
27+
responses:
28+
'204':
29+
description: Successful response
30+
/text:
31+
get:
32+
description: Text route
33+
responses:
34+
'200':
35+
description: Successful response
36+
content:
37+
text/plain:
38+
schema:
39+
type: string
40+
example: ok
41+
/exempt:
42+
post:
43+
description: Exempt route
44+
responses:
45+
'200':
46+
description: Successful response
47+
/validation:
48+
post:
49+
description: Validation route
50+
requestBody:
51+
required: true
52+
content:
53+
application/json:
54+
schema:
55+
type: object
56+
additionalProperties: false
57+
required:
58+
- username
59+
properties:
60+
username:
61+
type: string
62+
pattern: "^[0-9a-zA-Z._]+$"
63+
description: The username of the user
64+
minLength: 2
65+
maxLength: 30
66+
responses:
67+
'200':
68+
description: Successful response
69+
content:
70+
application/json:
71+
schema:
72+
type: object
73+
additionalProperties: false
74+
properties:
75+
username:
76+
type: string
77+
pattern: "^[0-9a-zA-Z._]+$"
78+
description: The username of the user
79+
minLength: 2
80+
maxLength: 30
81+
/validation/{username}:
82+
post:
83+
description: Validation route
84+
parameters:
85+
- name: username
86+
in: path
87+
required: true
88+
schema:
89+
type: string
90+
minLength: 2
91+
maxLength: 30
92+
- name: limit
93+
in: query
94+
schema:
95+
type: integer
96+
minimum: 1
97+
maximum: 100
98+
default: 10
99+
- name: x-username
100+
in: header
101+
schema:
102+
type: string
103+
minLength: 2
104+
maxLength: 30
105+
responses:
106+
'200':
107+
description: Successful response

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@ go 1.19
55
require (
66
github.com/getkin/kin-openapi v0.107.0
77
github.com/labstack/echo/v4 v4.9.1
8+
github.com/stretchr/testify v1.7.0
89
)
910

1011
require (
12+
github.com/davecgh/go-spew v1.1.1 // indirect
1113
github.com/go-openapi/jsonpointer v0.19.5 // indirect
1214
github.com/go-openapi/swag v0.19.5 // indirect
15+
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
1316
github.com/gorilla/mux v1.8.0 // indirect
1417
github.com/invopop/yaml v0.1.0 // indirect
1518
github.com/labstack/gommon v0.4.0 // indirect
1619
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
1720
github.com/mattn/go-colorable v0.1.11 // indirect
1821
github.com/mattn/go-isatty v0.0.14 // indirect
1922
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
23+
github.com/pmezard/go-difflib v1.0.0 // indirect
2024
github.com/valyala/bytebufferpool v1.0.0 // indirect
2125
github.com/valyala/fasttemplate v1.2.1 // indirect
2226
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
2327
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
2428
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
2529
golang.org/x/text v0.3.7 // indirect
30+
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
2631
gopkg.in/yaml.v2 v2.4.0 // indirect
2732
gopkg.in/yaml.v3 v3.0.1 // indirect
2833
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUe
77
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
88
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
99
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
10+
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
11+
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
1012
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
1113
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
1214
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
@@ -50,6 +52,8 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1U
5052
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
5153
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
5254
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
55+
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
56+
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
5357
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5458
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
5559
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

0 commit comments

Comments
 (0)