diff options
author | Mike Crute <mike@crute.us> | 2023-08-01 17:19:17 -0700 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2023-08-01 17:19:17 -0700 |
commit | 058e4faba9c1531661234a5adaa7a6c3791f92d4 (patch) | |
tree | bf7cc7879e1ad41f8dc1cbbaae74dad632556af8 | |
parent | 14a8d74efed00c13bd59e2ff9adc0b743803535d (diff) | |
download | golib-058e4faba9c1531661234a5adaa7a6c3791f92d4.tar.bz2 golib-058e4faba9c1531661234a5adaa7a6c3791f92d4.tar.xz golib-058e4faba9c1531661234a5adaa7a6c3791f92d4.zip |
echo: add new content type negotiatorecho/v0.10.0
-rw-r--r-- | echo/controller/content_type_negotiator_v2.go | 196 | ||||
-rw-r--r-- | echo/controller/content_type_negotiator_v2_test.go | 72 | ||||
-rw-r--r-- | echo/go.mod | 8 | ||||
-rw-r--r-- | echo/go.sum | 18 |
4 files changed, 284 insertions, 10 deletions
diff --git a/echo/controller/content_type_negotiator_v2.go b/echo/controller/content_type_negotiator_v2.go new file mode 100644 index 0000000..63d7fd3 --- /dev/null +++ b/echo/controller/content_type_negotiator_v2.go | |||
@@ -0,0 +1,196 @@ | |||
1 | package controller | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "net/http" | ||
6 | |||
7 | "github.com/labstack/echo/v4" | ||
8 | |||
9 | glecho "code.crute.us/mcrute/golib/echo" | ||
10 | glhttp "code.crute.us/mcrute/golib/net/http" | ||
11 | ) | ||
12 | |||
13 | const ( | ||
14 | ContextMediaTypeParameters = "__golib_echo_negotiator_media_type_params" | ||
15 | DefaultContentType = "*/*" | ||
16 | ) | ||
17 | |||
18 | type ContentTypeHandlerMap map[string]ErrorHandler | ||
19 | |||
20 | type ErrorHandler interface { | ||
21 | HandleError(echo.Context, int, error) error | ||
22 | } | ||
23 | |||
24 | type GetableHandler interface{ HandleGet(echo.Context) error } | ||
25 | type HeadableHandler interface{ HandleHead(echo.Context) error } | ||
26 | type PostableHandler interface{ HandlePost(echo.Context) error } | ||
27 | type PutableHandler interface{ HandlePut(echo.Context) error } | ||
28 | type PatchableHandler interface{ HandlePatch(echo.Context) error } | ||
29 | type DeleteableHandler interface{ HandleDelete(echo.Context) error } | ||
30 | type ConnectableHandler interface{ HandleConnect(echo.Context) error } | ||
31 | type OptionableHandler interface{ HandleOptions(echo.Context) error } | ||
32 | type TraceableHandler interface{ HandleTrace(echo.Context) error } | ||
33 | |||
34 | type ContentTypeNegotiatingHandlerV2 struct { | ||
35 | handlers map[string]map[string]echo.HandlerFunc // Content-Type -> HTTP Method -> Handler | ||
36 | errorHandlers map[string]ErrorHandler | ||
37 | mediaTypes glhttp.AcceptableTypes | ||
38 | } | ||
39 | |||
40 | func NewContentTypeNegotiatingHandlerV2() *ContentTypeNegotiatingHandlerV2 { | ||
41 | return &ContentTypeNegotiatingHandlerV2{ | ||
42 | handlers: map[string]map[string]echo.HandlerFunc{}, | ||
43 | mediaTypes: glhttp.AcceptableTypes{}, | ||
44 | errorHandlers: map[string]ErrorHandler{}, | ||
45 | } | ||
46 | } | ||
47 | |||
48 | func buildMethodMap(hnd ErrorHandler) map[string]echo.HandlerFunc { | ||
49 | methods := map[string]echo.HandlerFunc{} | ||
50 | |||
51 | if h, ok := hnd.(GetableHandler); ok { | ||
52 | methods[http.MethodGet] = h.HandleGet | ||
53 | } | ||
54 | |||
55 | if h, ok := hnd.(HeadableHandler); ok { | ||
56 | methods[http.MethodHead] = h.HandleHead | ||
57 | } | ||
58 | |||
59 | if h, ok := hnd.(PostableHandler); ok { | ||
60 | methods[http.MethodPost] = h.HandlePost | ||
61 | } | ||
62 | |||
63 | if h, ok := hnd.(PutableHandler); ok { | ||
64 | methods[http.MethodPut] = h.HandlePut | ||
65 | } | ||
66 | |||
67 | if h, ok := hnd.(PatchableHandler); ok { | ||
68 | methods[http.MethodPatch] = h.HandlePatch | ||
69 | } | ||
70 | |||
71 | if h, ok := hnd.(DeleteableHandler); ok { | ||
72 | methods[http.MethodDelete] = h.HandleDelete | ||
73 | } | ||
74 | |||
75 | if h, ok := hnd.(ConnectableHandler); ok { | ||
76 | methods[http.MethodConnect] = h.HandleConnect | ||
77 | } | ||
78 | |||
79 | if h, ok := hnd.(OptionableHandler); ok { | ||
80 | methods[http.MethodOptions] = h.HandleOptions | ||
81 | } | ||
82 | |||
83 | if h, ok := hnd.(TraceableHandler); ok { | ||
84 | methods[http.MethodTrace] = h.HandleTrace | ||
85 | } | ||
86 | |||
87 | return methods | ||
88 | } | ||
89 | |||
90 | // RegisterHandler registers a handler for a content type and method. It | ||
91 | // will return an error if registration fails. | ||
92 | func (h *ContentTypeNegotiatingHandlerV2) RegisterHandler(ct string, hnd ErrorHandler) error { | ||
93 | mt, err := glhttp.ParseMediaType(ct) | ||
94 | if err != nil { | ||
95 | return err | ||
96 | } | ||
97 | |||
98 | h.mediaTypes = append(h.mediaTypes, mt) | ||
99 | h.mediaTypes.Sorted() | ||
100 | |||
101 | h.handlers[mt.String()] = buildMethodMap(hnd) | ||
102 | h.errorHandlers[mt.String()] = hnd | ||
103 | return nil | ||
104 | } | ||
105 | |||
106 | func (h *ContentTypeNegotiatingHandlerV2) Default(hnd ErrorHandler) *ContentTypeNegotiatingHandlerV2 { | ||
107 | return h.Handles(DefaultContentType, hnd) | ||
108 | } | ||
109 | |||
110 | // Handles is a fluent builder that panics on error | ||
111 | func (h *ContentTypeNegotiatingHandlerV2) Handles(ct string, hnd ErrorHandler) *ContentTypeNegotiatingHandlerV2 { | ||
112 | if err := h.RegisterHandler(ct, hnd); err != nil { | ||
113 | panic(err) | ||
114 | } | ||
115 | return h | ||
116 | } | ||
117 | |||
118 | func (h *ContentTypeNegotiatingHandlerV2) HandlesAll(hnds ContentTypeHandlerMap) *ContentTypeNegotiatingHandlerV2 { | ||
119 | for ct, hnd := range hnds { | ||
120 | h.Handles(ct, hnd) | ||
121 | } | ||
122 | return h | ||
123 | } | ||
124 | |||
125 | func (h *ContentTypeNegotiatingHandlerV2) handleDefaultError(c echo.Context, code int, status string) error { | ||
126 | if hnd, ok := h.errorHandlers[DefaultContentType]; ok { | ||
127 | return hnd.HandleError(c, code, errors.New(status)) | ||
128 | } | ||
129 | |||
130 | return c.JSON(code, map[string]any{ | ||
131 | "error": map[string]any{ | ||
132 | "code": code, | ||
133 | "message": status, | ||
134 | }, | ||
135 | }) | ||
136 | } | ||
137 | |||
138 | func (h *ContentTypeNegotiatingHandlerV2) Handle(c echo.Context) error { | ||
139 | accept, err := glhttp.ParseAccept(c.Request().Header.Values("Accept")) | ||
140 | if err != nil { | ||
141 | return err | ||
142 | } | ||
143 | |||
144 | match, matcher := h.mediaTypes.FindMatch(accept) | ||
145 | if match == nil { | ||
146 | code := http.StatusNotAcceptable | ||
147 | return h.handleDefaultError(c, code, http.StatusText(code)) | ||
148 | } | ||
149 | |||
150 | c.Set(ContextMediaTypeParameters, matcher.Parameters) | ||
151 | |||
152 | ctHandlers := h.handlers[match.String()] | ||
153 | handler, ok := ctHandlers[c.Request().Method] | ||
154 | if !ok { | ||
155 | code := http.StatusMethodNotAllowed | ||
156 | return h.errorHandlers[match.String()].HandleError(c, code, errors.New(http.StatusText(code))) | ||
157 | } | ||
158 | |||
159 | c.Response().Header().Set("Content-Type", match.String()) | ||
160 | |||
161 | return handler(c) | ||
162 | } | ||
163 | |||
164 | func (h *ContentTypeNegotiatingHandlerV2) Connect(path string, r glecho.URLRouter, mw ...echo.MiddlewareFunc) *ContentTypeNegotiatingHandlerV2 { | ||
165 | allMethods := map[string]*int{} | ||
166 | for _, v := range h.handlers { | ||
167 | for k, _ := range v { | ||
168 | allMethods[k] = nil | ||
169 | } | ||
170 | } | ||
171 | |||
172 | for k, _ := range allMethods { | ||
173 | switch k { | ||
174 | case http.MethodGet: | ||
175 | r.GET(path, h.Handle, mw...) | ||
176 | case http.MethodHead: | ||
177 | r.HEAD(path, h.Handle, mw...) | ||
178 | case http.MethodPost: | ||
179 | r.POST(path, h.Handle, mw...) | ||
180 | case http.MethodPut: | ||
181 | r.PUT(path, h.Handle, mw...) | ||
182 | case http.MethodPatch: | ||
183 | r.PATCH(path, h.Handle, mw...) | ||
184 | case http.MethodDelete: | ||
185 | r.DELETE(path, h.Handle, mw...) | ||
186 | case http.MethodConnect: | ||
187 | r.Add(http.MethodConnect, path, h.Handle, mw...) | ||
188 | case http.MethodOptions: | ||
189 | r.OPTIONS(path, h.Handle, mw...) | ||
190 | case http.MethodTrace: | ||
191 | r.Add(http.MethodTrace, path, h.Handle, mw...) | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return h | ||
196 | } | ||
diff --git a/echo/controller/content_type_negotiator_v2_test.go b/echo/controller/content_type_negotiator_v2_test.go new file mode 100644 index 0000000..689003a --- /dev/null +++ b/echo/controller/content_type_negotiator_v2_test.go | |||
@@ -0,0 +1,72 @@ | |||
1 | package controller | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "net/http" | ||
7 | "net/http/httptest" | ||
8 | "testing" | ||
9 | |||
10 | "github.com/labstack/echo/v4" | ||
11 | "github.com/stretchr/testify/assert" | ||
12 | ) | ||
13 | |||
14 | // TODO | ||
15 | // Not Acceptable Error | ||
16 | // Bad Request Error | ||
17 | // Other error | ||
18 | // Error with default handler | ||
19 | // Error without default handler | ||
20 | |||
21 | // Check context params | ||
22 | // No handler for method | ||
23 | // Check content-type return header | ||
24 | // Handler runs | ||
25 | |||
26 | type GetOnlyHandler struct{} | ||
27 | |||
28 | func (h *GetOnlyHandler) HandleError(c echo.Context, code int, err error) error { | ||
29 | return c.String(code, fmt.Sprintf("%s -- default", err)) | ||
30 | } | ||
31 | |||
32 | func (h *GetOnlyHandler) HandleGet(c echo.Context) error { | ||
33 | return c.String(http.StatusOK, "GET OK -- default") | ||
34 | } | ||
35 | |||
36 | type GetPostHandler struct{ Name string } | ||
37 | |||
38 | func (h *GetPostHandler) HandleError(c echo.Context, code int, err error) error { | ||
39 | return c.String(code, fmt.Sprintf("%s %s", h.Name, err)) | ||
40 | } | ||
41 | |||
42 | func (h *GetPostHandler) HandleGet(c echo.Context) error { | ||
43 | return c.String(http.StatusOK, fmt.Sprintf("%s GET OK", h.Name)) | ||
44 | } | ||
45 | |||
46 | func (h *GetPostHandler) HandlePost(c echo.Context) error { | ||
47 | return c.String(http.StatusOK, fmt.Sprintf("%s POST OK", h.Name)) | ||
48 | } | ||
49 | |||
50 | func TestHandlerSuccess(t *testing.T) { | ||
51 | hnd := NewContentTypeNegotiatingHandlerV2(). | ||
52 | HandlesAll(ContentTypeHandlerMap{ | ||
53 | "*/*": &GetOnlyHandler{}, | ||
54 | "text/xml": &GetPostHandler{"xml"}, | ||
55 | "text/*": &GetPostHandler{"html"}, | ||
56 | "text/plain": &GetPostHandler{"plain"}, | ||
57 | }) | ||
58 | |||
59 | e := echo.New() | ||
60 | req := httptest.NewRequest(http.MethodPost, "/", nil) | ||
61 | req.Header.Set("Accept", "text/*,*/*;q=0.8") | ||
62 | //"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") | ||
63 | rec := httptest.NewRecorder() | ||
64 | c := e.NewContext(req, rec) | ||
65 | |||
66 | assert.NoError(t, hnd.Handle(c)) | ||
67 | |||
68 | res := rec.Result() | ||
69 | o, _ := io.ReadAll(res.Body) | ||
70 | fmt.Printf("%s\n", o) | ||
71 | assert.False(t, true) | ||
72 | } | ||
diff --git a/echo/go.mod b/echo/go.mod index 39501c2..fcedf30 100644 --- a/echo/go.mod +++ b/echo/go.mod | |||
@@ -2,16 +2,18 @@ module code.crute.us/mcrute/golib/echo | |||
2 | 2 | ||
3 | go 1.18 | 3 | go 1.18 |
4 | 4 | ||
5 | replace code.crute.us/mcrute/golib => ../ | ||
6 | |||
5 | require ( | 7 | require ( |
6 | code.crute.us/mcrute/golib v0.4.2 | 8 | code.crute.us/mcrute/golib v0.4.2 |
7 | code.crute.us/mcrute/golib/clients/netbox v0.1.0 | 9 | code.crute.us/mcrute/golib/clients/netbox v0.1.0 |
8 | code.crute.us/mcrute/golib/secrets v0.1.0 | 10 | code.crute.us/mcrute/golib/secrets v0.1.0 |
9 | code.crute.us/mcrute/golib/vault v0.2.4 | 11 | code.crute.us/mcrute/golib/vault v0.2.4 |
10 | github.com/elnormous/contenttype v1.0.0 | 12 | github.com/elnormous/contenttype v1.0.3 |
11 | github.com/labstack/echo/v4 v4.6.1 | 13 | github.com/labstack/echo/v4 v4.6.1 |
12 | github.com/labstack/gommon v0.3.1 | 14 | github.com/labstack/gommon v0.3.1 |
13 | github.com/prometheus/client_golang v1.11.0 | 15 | github.com/prometheus/client_golang v1.11.0 |
14 | github.com/stretchr/testify v1.7.0 | 16 | github.com/stretchr/testify v1.8.1 |
15 | gopkg.in/square/go-jose.v2 v2.5.1 | 17 | gopkg.in/square/go-jose.v2 v2.5.1 |
16 | ) | 18 | ) |
17 | 19 | ||
@@ -72,5 +74,5 @@ require ( | |||
72 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect | 74 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect |
73 | google.golang.org/grpc v1.41.0 // indirect | 75 | google.golang.org/grpc v1.41.0 // indirect |
74 | google.golang.org/protobuf v1.26.0 // indirect | 76 | google.golang.org/protobuf v1.26.0 // indirect |
75 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect | 77 | gopkg.in/yaml.v3 v3.0.1 // indirect |
76 | ) | 78 | ) |
diff --git a/echo/go.sum b/echo/go.sum index 0c48b3b..fe3ba40 100644 --- a/echo/go.sum +++ b/echo/go.sum | |||
@@ -30,8 +30,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo | |||
30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= | 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= |
31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= | 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= |
32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= |
33 | code.crute.us/mcrute/golib v0.4.2 h1:WeZyBMRFOiIkdNhsXx3kMgRGDUdUshzHS/J4OIPThlY= | ||
34 | code.crute.us/mcrute/golib v0.4.2/go.mod h1:dukLPhs1H8dxtkhXtpJZYo/bMzefLRbdRj9Tj67wdaQ= | ||
35 | code.crute.us/mcrute/golib/clients/netbox v0.1.0 h1:7ae676WtINm2oSLoUw1ERIZ2ndLD5gi7cvv2iZZd1XI= | 33 | code.crute.us/mcrute/golib/clients/netbox v0.1.0 h1:7ae676WtINm2oSLoUw1ERIZ2ndLD5gi7cvv2iZZd1XI= |
36 | code.crute.us/mcrute/golib/clients/netbox v0.1.0/go.mod h1:csRsnmAwenAz8Pbo7CcQTWzn6uaXadELIdB81JxsacY= | 34 | code.crute.us/mcrute/golib/clients/netbox v0.1.0/go.mod h1:csRsnmAwenAz8Pbo7CcQTWzn6uaXadELIdB81JxsacY= |
37 | code.crute.us/mcrute/golib/secrets v0.1.0 h1:22W0rLhE5jvIQlsUDQt1soGBEoBn4rl4a883f1yBybI= | 35 | code.crute.us/mcrute/golib/secrets v0.1.0 h1:22W0rLhE5jvIQlsUDQt1soGBEoBn4rl4a883f1yBybI= |
@@ -76,8 +74,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 | |||
76 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 74 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
77 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | 75 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
78 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 76 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
79 | github.com/elnormous/contenttype v1.0.0 h1:cTLou7K7uQMsPEmRiTJosAznsPcYuoBmXMrFAf86t2A= | 77 | github.com/elnormous/contenttype v1.0.3 h1:5DrD4LGO3ohab+jPplwE/LlY9JqmkYdssz4Zu7xl8Cs= |
80 | github.com/elnormous/contenttype v1.0.0/go.mod h1:ngVcyGGU8pnn4QJ5sL4StrNgc/wmXZXy5IQSBuHOFPg= | 78 | github.com/elnormous/contenttype v1.0.3/go.mod h1:ngVcyGGU8pnn4QJ5sL4StrNgc/wmXZXy5IQSBuHOFPg= |
81 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | 79 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= |
82 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | 80 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= |
83 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | 81 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= |
@@ -334,14 +332,19 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx | |||
334 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | 332 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= |
335 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= | 333 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= |
336 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 334 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
337 | github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= | ||
338 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 335 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
336 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
337 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= | ||
338 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||
339 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | 339 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
340 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | 340 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
341 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | 341 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
342 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | 342 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= |
343 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
344 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | 343 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
344 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
345 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
346 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||
347 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||
345 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= | 348 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= |
346 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | 349 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= |
347 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | 350 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= |
@@ -653,8 +656,9 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
653 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 656 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
654 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | 657 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |
655 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 658 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
656 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= | ||
657 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 659 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
660 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
661 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
658 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 662 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
659 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 663 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
660 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 664 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |