-
Notifications
You must be signed in to change notification settings - Fork 175
/
request_decorator.go
136 lines (106 loc) · 3.35 KB
/
request_decorator.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
package rest
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/onflow/flow-go/engine/access/rest/middleware"
"github.com/onflow/flow-go/model/flow"
)
// a convenience wrapper around the http request to make it easy to read request query params
type request struct {
*http.Request
expandFields map[string]bool
selectFields []string
}
// decorateRequest takes http request and applies functions to produce our custom
// request object decorated with values we need
func decorateRequest(r *http.Request) *request {
decoratedReq := &request{
Request: r,
}
if expandFields, found := middleware.GetFieldsToExpand(r); found {
decoratedReq.expandFields = sliceToMap(expandFields)
}
if selectFields, found := middleware.GetFieldsToSelect(r); found {
decoratedReq.selectFields = selectFields
}
return decoratedReq
}
func sliceToMap(values []string) map[string]bool {
valueMap := make(map[string]bool, len(values))
for _, v := range values {
valueMap[v] = true
}
return valueMap
}
func (rd *request) expands(field string) bool {
return rd.expandFields[field]
}
func (rd *request) selects() []string {
return rd.selectFields
}
func (rd *request) getVar(name string) string {
vars := mux.Vars(rd.Request)
return vars[name] // todo(sideninja) consider returning err if non-existing
}
func (rd *request) getQueryParam(name string) string {
return rd.Request.URL.Query().Get(name) // todo(sideninja) consider returning err if non-existing
}
func (rd *request) getQueryParams(name string) ([]string, error) {
param := rd.Request.URL.Query().Get(name)
return toStringArray(param)
}
func (rd *request) bodyAs(dst interface{}) error {
//todo(sideninja) validate size
dec := json.NewDecoder(rd.Body)
dec.DisallowUnknownFields()
err := dec.Decode(&dst)
if err != nil {
var syntaxError *json.SyntaxError
var unmarshalTypeError *json.UnmarshalTypeError
switch {
case errors.As(err, &syntaxError):
err := fmt.Errorf("request body contains badly-formed JSON (at position %d)", syntaxError.Offset)
return NewBadRequestError(err)
case errors.Is(err, io.ErrUnexpectedEOF):
err := fmt.Errorf("request body contains badly-formed JSON")
return NewBadRequestError(err)
case errors.As(err, &unmarshalTypeError):
err := fmt.Errorf("request body contains an invalid value for the %q field (at position %d)", unmarshalTypeError.Field, unmarshalTypeError.Offset)
return NewBadRequestError(err)
case strings.HasPrefix(err.Error(), "json: unknown field "):
fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ")
err := fmt.Errorf("Request body contains unknown field %s", fieldName)
return NewBadRequestError(err)
case errors.Is(err, io.EOF):
err := fmt.Errorf("request body must not be empty")
return NewBadRequestError(err)
default:
return err
}
}
if dst == nil {
return NewBadRequestError(fmt.Errorf("request body must not be empty"))
}
err = dec.Decode(&struct{}{})
if err != io.EOF {
err := fmt.Errorf("request body must only contain a single JSON object")
return NewBadRequestError(err)
}
return nil
}
func (rd *request) ids() ([]flow.Identifier, error) {
rawIDs := rd.getVar("id")
ids, err := toStringArray(rawIDs)
if err != nil {
return nil, err
}
return toIDs(ids)
}
func (rd *request) id() (flow.Identifier, error) {
return toID(rd.getVar("id"))
}