/
http.go
220 lines (194 loc) · 5.86 KB
/
http.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
package protocol
import (
"fmt"
"net/http"
"github.com/opencost/opencost/core/pkg/util/json"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
// HTTPProtocol is a struct used as a selector for request/response protocol utility methods
type HTTPProtocol struct{}
// HTTPError represents an http error response
type HTTPError struct {
StatusCode int
Body string
}
// Error returns the error string
func (he HTTPError) Error() string {
return string(he.Body)
}
// BadRequest creates a BadRequest HTTPError
func (hp HTTPProtocol) BadRequest(message string) HTTPError {
return HTTPError{
StatusCode: http.StatusBadRequest,
Body: message,
}
}
// UnprocessableEntity creates an UnprocessableEntity HTTPError
func (hp HTTPProtocol) UnprocessableEntity(message string) HTTPError {
if message == "" {
message = "Unprocessable Entity"
}
return HTTPError{
StatusCode: http.StatusUnprocessableEntity,
Body: message,
}
}
// InternalServerError creates an InternalServerError HTTPError
func (hp HTTPProtocol) InternalServerError(message string) HTTPError {
if message == "" {
message = "Internal Server Error"
}
return HTTPError{
StatusCode: http.StatusInternalServerError,
Body: message,
}
}
// NotFound creates a NotFound HTTPError
func (hp HTTPProtocol) NotFound() HTTPError {
return HTTPError{
StatusCode: http.StatusNotFound,
Body: "Not Found",
}
}
// HTTPResponse represents a data envelope for our HTTP messaging
type HTTPResponse struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Message string `json:"message,omitempty"`
Warning string `json:"warning,omitempty"`
}
// ToResponse accepts a data payload and/or error to encode into a new HTTPResponse instance. Responses
// which should not contain an error should pass nil for err.
func (hp HTTPProtocol) ToResponse(data interface{}, err error) *HTTPResponse {
if err != nil {
return &HTTPResponse{
Code: http.StatusInternalServerError,
Data: data,
Message: err.Error(),
}
}
return &HTTPResponse{
Code: http.StatusOK,
Data: data,
}
}
// WriteData wraps the data payload in an HTTPResponse and writes the resulting response using the
// http.ResponseWriter
func (hp HTTPProtocol) WriteData(w http.ResponseWriter, data interface{}) {
status := http.StatusOK
resp, err := json.Marshal(&HTTPResponse{
Code: status,
Data: data,
})
if err != nil {
status = http.StatusInternalServerError
resp, _ = json.Marshal(&HTTPResponse{
Code: status,
Message: fmt.Sprintf("Error: %s", err),
})
}
w.WriteHeader(status)
w.Write(resp)
}
// WriteDataWithWarning writes the data payload similiar to WriteData except it provides an additional warning message.
func (hp HTTPProtocol) WriteDataWithWarning(w http.ResponseWriter, data interface{}, warning string) {
status := http.StatusOK
resp, err := json.Marshal(&HTTPResponse{
Code: status,
Data: data,
Warning: warning,
})
if err != nil {
status = http.StatusInternalServerError
resp, _ = json.Marshal(&HTTPResponse{
Code: status,
Message: fmt.Sprintf("Error: %s", err),
})
}
w.WriteHeader(status)
w.Write(resp)
}
// WriteDataWithMessage writes the data payload similiar to WriteData except it provides an additional string message.
func (hp HTTPProtocol) WriteDataWithMessage(w http.ResponseWriter, data interface{}, message string) {
status := http.StatusOK
resp, err := json.Marshal(&HTTPResponse{
Code: status,
Data: data,
Message: message,
})
if err != nil {
status = http.StatusInternalServerError
resp, _ = json.Marshal(&HTTPResponse{
Code: status,
Message: fmt.Sprintf("Error: %s", err),
})
}
w.WriteHeader(status)
w.Write(resp)
}
// WriteProtoWithMessage uses the protojson package to convert proto3 response to json response and
// return it to the requester. Proto3 drops messages with default values but overriding the param
// EmitUnpopulated to true it returns default values in the Json response payload. If error is
// encountered it sent InternalServerError and the error why the json conversion failed.
func (hp HTTPProtocol) WriteProtoWithMessage(w http.ResponseWriter, data proto.Message) {
m := protojson.MarshalOptions{
EmitUnpopulated: true,
}
status := http.StatusOK
resp, err := m.Marshal(data)
if err != nil {
status = http.StatusInternalServerError
resp, _ = json.Marshal(&HTTPResponse{
Message: fmt.Sprintf("Error: %s", err),
})
}
w.WriteHeader(status)
w.Write(resp)
}
// WriteDataWithMessageAndWarning writes the data payload similiar to WriteData except it provides a warning and additional message string.
func (hp HTTPProtocol) WriteDataWithMessageAndWarning(w http.ResponseWriter, data interface{}, message string, warning string) {
status := http.StatusOK
resp, err := json.Marshal(&HTTPResponse{
Code: status,
Data: data,
Message: message,
Warning: warning,
})
if err != nil {
status = http.StatusInternalServerError
resp, _ = json.Marshal(&HTTPResponse{
Code: status,
Message: fmt.Sprintf("Error: %s", err),
})
}
w.WriteHeader(status)
w.Write(resp)
}
// WriteError wraps the HTTPError in a HTTPResponse and writes it via http.ResponseWriter
func (hp HTTPProtocol) WriteError(w http.ResponseWriter, err HTTPError) {
status := err.StatusCode
if status == 0 {
status = http.StatusInternalServerError
}
w.WriteHeader(status)
resp, _ := json.Marshal(&HTTPResponse{
Code: status,
Message: err.Body,
})
w.Write(resp)
}
// WriteResponse writes the provided HTTPResponse instance via http.ResponseWriter
func (hp HTTPProtocol) WriteResponse(w http.ResponseWriter, r *HTTPResponse) {
status := r.Code
resp, err := json.Marshal(r)
if err != nil {
status = http.StatusInternalServerError
resp, _ = json.Marshal(&HTTPResponse{
Code: status,
Message: fmt.Sprintf("Error: %s", err),
})
}
w.WriteHeader(status)
w.Write(resp)
}