-
Notifications
You must be signed in to change notification settings - Fork 1
/
lit.go
214 lines (214 loc) · 5.55 KB
/
lit.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Package lit is a fast and expressive HTTP framework.
//
// # The basics
//
// In Lit, an HTTP handler is a function that receives a [*Request] and returns a [Response]. Register new handlers
// using the [*Router.Handle] method.
//
// For instance, the Divide function below is a handler that returns the division of two integers coming from query
// parameters:
//
// type Request struct {
// A int `query:"a"`
// B int `query:"b"`
// }
//
// func (r *Request) Validate() []validate.Field {
// return []validate.Field{
// validate.NotEqual(&r.B, 0),
// }
// }
//
// func Divide(r *lit.Request) lit.Response {
// req, err := bind.Query[Request](r)
// if err != nil {
// return render.BadRequest(err)
// }
//
// return render.OK(req.A / req.B)
// }
//
// In order to extend a handler's functionality and to be able to reuse the logic in several handlers,
// such as logging or authorization logic, one can use middlewares.
// In Lit, a middleware is a function that receives a [Handler] and returns a [Handler]. Register new middlewares
// using the [*Router.Use] method.
//
// For instance, the AppendRequestID function below is a middleware that assigns an ID to the request and appends it
// to the context:
//
// type ContextKeyType string
//
// var RequestIDKey ContextKeyType = "request-id"
//
// func AppendRequestID(h lit.Handler) lit.Handler {
// return func(r *lit.Request) lit.Response {
// var (
// requestID = uuid.New()
// ctx = context.WithValue(r.Context(), RequestIDKey, requestID)
// )
//
// r.WithContext(ctx)
//
// return h(r)
// }
// }
//
// It is recommended to use the [Log] and [Recover] middlewares.
//
// Check the [package-level examples] for more use cases.
//
// # Model binding and receiving files
//
// Lit can parse and validate data coming from a request's URI parameters, header, body or query parameters
// to Go structs, including files from multipart form requests.
//
// Check [github.com/jvcoutinho/lit/bind] package.
//
// # Validation
//
// Lit can validate Go structs with generics and compile-time assertions.
//
// Check [github.com/jvcoutinho/lit/validate] package.
//
// # Responding requests, redirecting, serving files and streams
//
// Lit responds requests with implementations of the [Response] interface. Current provided implementations include
// JSON responses, redirections, no content responses, files and streams.
//
// Check [github.com/jvcoutinho/lit/render] package.
//
// # Testing handlers
//
// Handlers can be unit tested in several ways. The simplest and idiomatic form is calling the handler with a crafted
// request and asserting the response:
//
// type Request struct {
// A int `query:"a"`
// B int `query:"b"`
// }
//
// func (r *Request) Validate() []validate.Field {
// return []validate.Field{
// validate.NotEqual(&r.B, 0),
// }
// }
//
// func Divide(r *lit.Request) lit.Response {
// req, err := bind.Query[Request](r)
// if err != nil {
// return render.BadRequest(err)
// }
//
// return render.OK(req.A / req.B)
// }
//
// func TestDivide(t *testing.T) {
// t.Parallel()
//
// tests := []struct {
// description string
// a int
// b int
// want lit.Response
// }{
// {
// description: "BEquals0",
// a: 3,
// b: 0,
// want: render.BadRequest("b should not be equal to 0"),
// },
// {
// description: "Division",
// a: 6,
// b: 3,
// want: render.OK(2),
// },
// }
//
// for _, test := range tests {
// test := test
// t.Run(test.description, func(t *testing.T) {
// t.Parallel()
//
// var (
// path = fmt.Sprintf("/?a=%d&b=%d", test.a, test.b)
// request = lit.NewRequest(
// httptest.NewRequest(http.MethodGet, path, nil),
// )
// got = Divide(request)
// want = test.want
// )
//
// if !reflect.DeepEqual(got, want) {
// t.Fatalf("got: %v; want: %v", got, want)
// }
// })
// }
// }
//
// # Testing middlewares
//
// Middlewares can be tested in the same way as handlers (crafting a request and asserting the response of the handler
// after the transformation):
//
// func ValidateXAPIKeyHeader(h lit.Handler) lit.Handler {
// return func(r *lit.Request) lit.Response {
// apiKeyHeader, err := bind.HeaderField[string](r, "X-API-KEY")
// if err != nil {
// return render.BadRequest(err)
// }
//
// if apiKeyHeader == "" {
// return render.Unauthorized("API Key must be provided")
// }
//
// return h(r)
// }
// }
//
// func TestValidateXAPIKeyHeader(t *testing.T) {
// t.Parallel()
//
// testHandler := func(r *lit.Request) lit.Response {
// return render.NoContent()
// }
//
// tests := []struct {
// description string
// apiKeyHeader string
// want lit.Response
// }{
// {
// description: "EmptyHeader",
// apiKeyHeader: "",
// want: render.Unauthorized("API Key must be provided"),
// },
// {
// description: "ValidAPIKey",
// apiKeyHeader: "api-key-1",
// want: render.NoContent(),
// },
// }
//
// for _, test := range tests {
// test := test
// t.Run(test.description, func(t *testing.T) {
// t.Parallel()
//
// r := httptest.NewRequest(http.MethodGet, "/", nil)
// r.Header.Add("X-API-KEY", test.apiKeyHeader)
//
// var (
// request = lit.NewRequest(r)
// got = ValidateXAPIKeyHeader(testHandler)(request)
// )
//
// if !reflect.DeepEqual(got, test.want) {
// t.Fatalf("got: %v; want: %v", got, test.want)
// }
// })
// }
// }
//
// [package-level examples]: https://pkg.go.dev/github.com/jvcoutinho/lit#pkg-examples
package lit