-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
main.go
249 lines (217 loc) · 8.51 KB
/
main.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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package main
import (
"context"
"fmt"
"strings"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/x/errors"
"github.com/kataras/iris/v12/x/errors/validation"
"github.com/kataras/iris/v12/x/pagination"
)
func main() {
app := iris.New()
// Create a new service and pass it to the handlers.
service := new(myService)
app.Post("/", errors.Intercept(afterServiceCallButBeforeDataSent), createHandler(service)) // OR: errors.CreateHandler(service.Create)
app.Get("/", listAllHandler(service)) // OR errors.Handler(service.ListAll, errors.Value(ListRequest{}))
app.Post("/page", listHandler(service)) // OR: errors.ListHandler(service.ListPaginated)
app.Delete("/{id:string}", deleteHandler(service)) // OR: errors.NoContentOrNotModifiedHandler(service.DeleteWithFeedback, errors.PathParam[string]("id"))
app.Listen(":8080")
}
func createHandler(service *myService) iris.Handler {
return func(ctx iris.Context) {
// What it does?
// 1. Reads the request body and binds it to the CreateRequest struct.
// 2. Calls the service.Create function with the given request body.
// 3. If the service.Create returns an error, it sends an appropriate error response to the client.
// 4. If the service.Create returns a response, it sets the status code to 201 (Created) and sends the response as a JSON payload to the client.
//
// Useful for create operations.
errors.Create(ctx, service.Create)
}
}
func listAllHandler(service *myService) iris.Handler {
return func(ctx iris.Context) {
// What it does?
// 1. If the 3rd variadic (optional) parameter is empty (not our case here), it reads the request body and binds it to the ListRequest struct,
// otherwise (our case) it calls the service.ListAll function directly with the given input parameter (empty ListRequest struct value in our case).
// 2. Calls the service.ListAll function with the ListRequest value.
// 3. If the service.ListAll returns an error, it sends an appropriate error response to the client.
// 4. If the service.ListAll returns a response, it sets the status code to 200 (OK) and sends the response as a JSON payload to the client.
//
// Useful for get single, fetch multiple and search operations.
errors.OK(ctx, service.ListAll, ListRequest{})
}
}
func listHandler(service *myService) iris.Handler {
return func(ctx iris.Context) {
errors.List(ctx, service.ListPaginated)
}
}
func deleteHandler(service *myService) iris.Handler {
return func(ctx iris.Context) {
id := ctx.Params().Get("id")
// What it does?
// 1. Calls the service.DeleteWithFeedback function with the given input parameter.
// 2. If the service.DeleteWithFeedback returns an error, it sends an appropriate error response to the client.
// 3.If the service.DeleteWithFeedback doesn't return an error then it sets the status code to 204 (No Content) and
// sends the response as a JSON payload to the client.
// errors.NoContent(ctx, service.Delete, id)
// OR:
// 1. Calls the service.DeleteWithFeedback function with the given input parameter.
// 2. If the service.DeleteWithFeedback returns an error, it sends an appropriate error response to the client.
// 3. If the service.DeleteWithFeedback returns true, it sets the status code to 204 (No Content).
// 4. If the service.DeleteWithFeedback returns false, it sets the status code to 304 (Not Modified).
//
// Useful for update and delete operations.
errors.NoContentOrNotModified(ctx, service.DeleteWithFeedback, id)
}
}
type (
myService struct{}
CreateRequest struct {
Fullname string `json:"fullname"`
Age int `json:"age"`
Hobbies []string `json:"hobbies"`
}
CreateResponse struct {
ID string `json:"id"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Age int `json:"age"`
Hobbies []string `json:"hobbies"`
}
)
// HandleRequest implements the errors.RequestHandler interface.
// It validates the request body and returns an error if the request body is invalid.
// You can also alter the "r" CreateRequest before calling the service method,
// e.g. give a default value to a field if it's empty or set an ID based on a path parameter.
// OR
// Custom function per route:
//
// r.Post("/", errors.Validation(validateCreateRequest), createHandler(service))
// [more code here...]
//
// func validateCreateRequest(ctx iris.Context, r *CreateRequest) error {
// return validation.Join(
// validation.String("fullname", r.Fullname).NotEmpty().Fullname().Length(3, 50),
// validation.Number("age", r.Age).InRange(18, 130),
// validation.Slice("hobbies", r.Hobbies).Length(1, 10),
// )
// }
func (r *CreateRequest) HandleRequest(ctx iris.Context) error {
// To pass custom validation functions:
// return validation.Join(
// validation.String("fullname", r.Fullname).Func(customStringFuncHere),
// OR
// validation.Field("any_field", r.AnyFieldValue).Func(customAnyFuncHere))
return validation.Join(
validation.String("fullname", r.Fullname).Fullname().Length(3, 50),
validation.Number("age", r.Age).InRange(18, 130),
validation.Slice("hobbies", r.Hobbies).Length(1, 10),
)
/* Example Output:
{
"http_error_code": {
"canonical_name": "INVALID_ARGUMENT",
"status": 400
},
"message": "validation failure",
"details": "fields were invalid",
"validation": [
{
"field": "fullname",
"value": "",
"reason": "must not be empty, must contain first and last name, must be between 3 and 50 characters"
},
{
"field": "age",
"value": 0,
"reason": "must be in range of [18, 130]"
},
{
"field": "hobbies",
"value": null,
"reason": "must be between 1 and 10 elements"
}
]
}
*/
}
/*
// HandleResponse implements the errors.ResponseHandler interface.
func (r *CreateRequest) HandleResponse(ctx iris.Context, resp *CreateResponse) error {
fmt.Printf("request got: %+v\nresponse sent: %#+v\n", r, resp)
return nil // fmt.Errorf("let's fire an internal server error just for the shake of the example") // return nil to continue.
}
*/
func afterServiceCallButBeforeDataSent(ctx iris.Context, req CreateRequest, resp *CreateResponse) error {
fmt.Printf("intercept: request got: %+v\nresponse sent: %#+v\n", req, resp)
return nil
}
func (s *myService) Create(ctx context.Context, in CreateRequest) (CreateResponse, error) {
arr := strings.Split(in.Fullname, " ")
firstname, lastname := arr[0], arr[1]
id := "test_id"
resp := CreateResponse{
ID: id,
Firstname: firstname,
Lastname: lastname,
Age: in.Age,
Hobbies: in.Hobbies,
}
return resp, nil // , errors.New("create: test error")
}
type ListRequest struct {
}
func (s *myService) ListAll(ctx context.Context, in ListRequest) ([]CreateResponse, error) {
resp := []CreateResponse{
{
ID: "test-id-1",
Firstname: "test first name 1",
Lastname: "test last name 1",
},
{
ID: "test-id-2",
Firstname: "test first name 2",
Lastname: "test last name 2",
},
{
ID: "test-id-3",
Firstname: "test first name 3",
Lastname: "test last name 3",
},
}
return resp, nil //, errors.New("list all: test error")
}
type ListFilter struct {
Firstname string `json:"firstname"`
}
func (s *myService) ListPaginated(ctx context.Context, opts pagination.ListOptions, filter ListFilter) ([]CreateResponse, int /* any number type */, error) {
all, err := s.ListAll(ctx, ListRequest{})
if err != nil {
return nil, 0, err
}
filteredResp := make([]CreateResponse, 0)
for _, v := range all {
if strings.Contains(v.Firstname, filter.Firstname) {
filteredResp = append(filteredResp, v)
}
if len(filteredResp) == opts.GetLimit() {
break
}
}
return filteredResp, len(all), nil // errors.New("list paginated: test error")
}
func (s *myService) GetByID(ctx context.Context, id string) (CreateResponse, error) {
return CreateResponse{Firstname: "Gerasimos"}, nil // errors.New("get by id: test error")
}
func (s *myService) Delete(ctx context.Context, id string) error {
return nil // errors.New("delete: test error")
}
func (s *myService) Update(ctx context.Context, req CreateRequest) (bool, error) {
return true, nil // false, errors.New("update: test error")
}
func (s *myService) DeleteWithFeedback(ctx context.Context, id string) (bool, error) {
return true, nil // false, errors.New("delete: test error")
}