-
Notifications
You must be signed in to change notification settings - Fork 2
/
context.go
190 lines (146 loc) · 4.71 KB
/
context.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
package webfmwk
import (
"context"
"encoding/json"
"errors"
"log/slog"
"net/http"
validator "github.com/go-playground/validator/v10"
"github.com/gorilla/schema"
"github.com/valyala/fasthttp"
)
const _prettyTag = "pretty"
type (
// InputHandling interface introduce I/O actions.
InputHandling interface {
// FetchContent extract the json content from the body into the content interface.
FetchContent(content interface{}) ErrorHandled
// Validate is used to validate a content of the content params.
// See https://go-playground/validator.v10 for more.
Validate(content interface{}) ErrorHandled
// FetchAndValidateContent fetch the content then validate it.
FetchAndValidateContent(content interface{}) ErrorHandled
// DecodeQP load the query param in the content object.
// Seee https://github.com/gorilla/query for more.
DecodeQP(content interface{}) ErrorHandled
// DecodeAndValidateQP load the query param in the content object and then validate it.
DecodeAndValidateQP(content interface{}) ErrorHandled
}
// ContextLogger interface implement the context Logger needs.
ContextLogger interface {
// SetStructuredLogger set context' structured logger.
SetStructuredLogger(logger *slog.Logger) Context
// GetStructuredLogger return context' structured logger.
GetStructuredLogger() *slog.Logger
}
// Context interface implement the context used in this project.
Context interface {
SendResponse
InputHandling
ContextLogger
// GetFastContext return a pointer to the internal fasthttp.RequestCtx.
GetFastContext() *fasthttp.RequestCtx
// GetContext return the request context.Context.
GetContext() context.Context
// GetVar return the url var parameters. An empty string for missing case.
GetVar(key string) (val string)
// GetQueries return the queries into a fasthttp.Args object.
GetQuery() *fasthttp.Args
// GetQuery fetch the query object key
// GetQuery(key string) (val string, ok bool)
}
// icontext implement the Context interface
// It hold the data used by the request
icontext struct {
*fasthttp.RequestCtx
slog *slog.Logger
ctx context.Context //nolint:containedctx
}
)
const UnprocessablePayloadErrorStr = `{"message":"unprocessable payload content","status":422}`
var (
// decoder annotation : `schema` : gorilla
decoder = schema.NewDecoder()
errUnprocessablePayload = NewUnprocessable(NewError("unprocessable payload content"))
)
// GetVar implement Context
func (c *icontext) GetVar(key string) string {
v, ok := c.UserValue(key).(string)
if !ok {
return ""
}
return v
}
// GetQuery implement Context.
func (c *icontext) GetQuery() *fasthttp.Args {
return c.QueryArgs()
}
// GetFastContext implement Context.
func (c *icontext) GetFastContext() *fasthttp.RequestCtx {
return c.RequestCtx
}
// SetStructuredLogger implement Context.
func (c *icontext) SetStructuredLogger(logger *slog.Logger) Context {
c.slog = logger
return c
}
// GetLogger implement Context
func (c *icontext) GetStructuredLogger() *slog.Logger {
return c.slog
}
// GetContext implement Context
func (c *icontext) GetContext() context.Context {
return c.ctx
}
// FetchContent implement Context.
// It load payload in the dest interface{} using the system json library.
func (c *icontext) FetchContent(dest interface{}) ErrorHandled {
b := c.PostBody()
if e := json.Unmarshal(b, &dest); e != nil {
c.slog.Error("fetching payload", slog.Any("error", e))
return errUnprocessablePayload
}
return nil
}
// Validate implement Context
// this implemtation use validator to anotate & check struct
func (c *icontext) Validate(dest interface{}) ErrorHandled {
if err := validate.Struct(dest); err != nil {
var ev validator.ValidationErrors
errors.As(err, &ev)
return NewUnprocessable(ValidationError{
Status: http.StatusUnprocessableEntity,
Error: TranslateAndUseFieldName(ev),
})
}
return nil
}
// FetchAndValidateContent implemt Context.
// It sucesively call FetchContent then Validate on the dest param
func (c *icontext) FetchAndValidateContent(dest interface{}) ErrorHandled {
if e := c.FetchContent(&dest); e != nil {
return e
}
return c.Validate(dest)
}
// DecodeQP implement Context
func (c *icontext) DecodeQP(dest interface{}) ErrorHandled {
m := map[string][]string{}
c.QueryArgs().VisitAll(func(k, v []byte) {
key := string(k)
m[key] = make([]string, 1)
m[key][0] = string(v)
})
if e := decoder.Decode(dest, m); e != nil {
c.slog.Error("validating query params", slog.Any("error", e))
return NewUnprocessable(NewErrorFromError(e))
}
return nil
}
// DecodeAndValidateQP implement Context
func (c *icontext) DecodeAndValidateQP(qp interface{}) ErrorHandled {
if e := c.DecodeQP(qp); e != nil {
return e
}
return c.Validate(qp)
}