-
Notifications
You must be signed in to change notification settings - Fork 0
/
nvalid.go
112 lines (108 loc) · 3.68 KB
/
nvalid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package nvalid
import (
"bytes"
"io/ioutil"
"net/http"
"net/url"
"github.com/getkin/kin-openapi/openapi3"
"github.com/getkin/kin-openapi/openapi3filter"
"github.com/getkin/kin-openapi/routers/gorillamux"
"github.com/muir/nject"
"github.com/muir/nvelope"
"github.com/pkg/errors"
)
// OpenAPI3Validator returns request and response validators that
// can drop into an nvelope-based http handler chain. The first
// returned function should be placed just before or after the request
// decoder. The second function should be as the parameter for
// nvelope.WithAPIEnforcer().
//
// OpenAPI3Validator's parameters are the location (file or URL) where
// the swagger can be found and an option specifier for openapi3filter:
// should it return MultiError or just a single error.
func OpenAPI3Validator(
swaggerLocation string, // can be a URL or a filename(
multiError bool,
) (
func(r *http.Request, body nvelope.Body) nject.TerminalError,
nvelope.APIEnforcerFunc,
error,
) {
if swaggerURL, err := url.Parse(swaggerLocation); err == nil {
doc, err := openapi3.NewLoader().LoadFromURI(swaggerURL)
if err != nil {
return nil, nil, errors.Wrapf(err, "load swagger from %s", swaggerLocation)
}
return OpenAPI3ValidatorFromParsed(doc, swaggerLocation, multiError)
}
doc, err := openapi3.NewLoader().LoadFromFile(swaggerLocation)
if err != nil {
return nil, nil, errors.Wrapf(err, "load swagger file %s", swaggerLocation)
}
return OpenAPI3ValidatorFromParsed(doc, swaggerLocation, multiError)
}
// OpenAPI3ValidatorFromParsed returns request and response validators that
// can drop into an nvelope-based http handler chain. The first
// returned function should be placed just before or after the request
// decoder. The second function should be as the parameter for
// nvelope.WithAPIEnforcer().
//
// OpenAPI3Validator's parameters are the parsed API specification, the
// location (file or URL) where the swagger can be found; and an option
// specifier for openapi3filter: should it return MultiError or just a single error.
func OpenAPI3ValidatorFromParsed(
doc *openapi3.T,
swaggerLocation string,
multiError bool,
) (
func(r *http.Request, body nvelope.Body) nject.TerminalError,
nvelope.APIEnforcerFunc,
error,
) {
router, err := gorillamux.NewRouter(doc)
if err != nil {
return nil, nil, errors.Wrapf(err, "create router from swagger at %s", swaggerLocation)
}
request := func(r *http.Request, body nvelope.Body) nject.TerminalError {
route, pathParams, err := router.FindRoute(r)
if err != nil {
return errors.Wrapf(err, "Find route for request to %s", r.URL)
}
r.Body = ioutil.NopCloser(bytes.NewReader(body))
err = openapi3filter.ValidateRequest(r.Context(), &openapi3filter.RequestValidationInput{
Request: r,
PathParams: pathParams,
Route: route,
Options: &openapi3filter.Options{
MultiError: multiError,
},
})
err = nvelope.ReturnCode(err, 400)
return err
}
response := func(httpCode int, enc []byte, header http.Header, r *http.Request) error {
route, pathParams, err := router.FindRoute(r)
if err != nil {
return errors.Wrapf(err, "Find route for request to %s", r.URL)
}
err = openapi3filter.ValidateResponse(r.Context(), &openapi3filter.ResponseValidationInput{
RequestValidationInput: &openapi3filter.RequestValidationInput{
Request: r,
PathParams: pathParams,
Route: route,
Options: &openapi3filter.Options{
MultiError: multiError,
},
},
Status: httpCode,
Header: header,
Body: ioutil.NopCloser(bytes.NewReader(enc)),
Options: &openapi3filter.Options{
IncludeResponseStatus: true,
MultiError: multiError,
},
})
return err
}
return request, response, nil
}