-
Notifications
You must be signed in to change notification settings - Fork 0
/
json.go
162 lines (144 loc) · 5.09 KB
/
json.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
package chttp
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
)
// JSONClient is an HTTP client wrapper around the Client
// with automated marshaling of the request body and unmarshaling response body.
type JSONClient struct {
*Client
}
// JSON wraps Client with the JSONClient.
func JSON(client *Client) *JSONClient {
return &JSONClient{
Client: client,
}
}
// NewJSON creates a JSONClient with new Client based on given http.Client.
func NewJSON(client *http.Client, options ...Option) *JSONClient {
return &JSONClient{
Client: NewClient(client, options...),
}
}
// Request prepares the request by marshaling request body and tries to unmarshal response
// with the JSONClient.UnmarshalHTTPResponse function.
func (c *JSONClient) Request(
ctx context.Context,
method string,
url string,
body interface{},
result interface{},
) (err error) {
data, err := marshal(body)
if err != nil {
return err
}
res, err := c.Client.Request(ctx, method, url, data)
return c.UnmarshalHTTPResponse(res, err, &result)
}
// UnmarshalHTTPResponse tries to unmarshal the response body into the given result interface.
// Result should be reference type and not nil.
func (c *JSONClient) UnmarshalHTTPResponse(response *http.Response, httpErr error, result interface{}) (err error) {
if httpErr != nil {
return newError(response, nil, fmt.Errorf("requesting error: %w", err))
}
defer func() {
if response != nil && response.Body != nil {
_ = response.Body.Close()
}
}()
data, err := io.ReadAll(response.Body)
if err != nil {
return newError(response, data, fmt.Errorf("reading response body error: %w", err))
}
if response.StatusCode >= http.StatusMultipleChoices {
return newError(response, data, nil)
}
if len(data) > 0 && result != nil {
err = json.Unmarshal(data, &result)
if err != nil {
return newError(response, data, fmt.Errorf("unmarshaling response error: %w", err))
}
}
return nil
}
// Method returns a function implementation of the HTTP Method from the Client by its name.
// Returns Client.GET as the default method.
func (c *JSONClient) Method(
method string,
) func(ctx context.Context, url string, body interface{}, result interface{}) error {
switch method {
case http.MethodHead:
return c.HEAD
case http.MethodPost:
return c.POST
case http.MethodPut:
return c.PUT
case http.MethodPatch:
return c.PATCH
case http.MethodDelete:
return c.DELETE
case http.MethodConnect:
return c.CONNECT
case http.MethodOptions:
return c.OPTIONS
case http.MethodTrace:
return c.TRACE
case http.MethodGet:
fallthrough
default:
return c.GET
}
}
// GET is an alias to do the Request with the http.MethodGet method.
func (c *JSONClient) GET(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodGet, url, body, &result)
}
// HEAD is an alias to do the Request with the http.MethodHead method.
func (c *JSONClient) HEAD(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodHead, url, body, &result)
}
// POST is an alias to do the Request with the http.MethodPost method.
func (c *JSONClient) POST(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodPost, url, body, &result)
}
// PUT is an alias to do the Request with the http.MethodPut method.
func (c *JSONClient) PUT(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodPut, url, body, &result)
}
// PATCH is an alias to do the Request with the http.MethodPatch method.
func (c *JSONClient) PATCH(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodPatch, url, body, &result)
}
// DELETE is an alias to do the Request with the http.MethodDelete method.
func (c *JSONClient) DELETE(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodDelete, url, body, &result)
}
// CONNECT is an alias to do the Request with the http.MethodConnect method.
func (c *JSONClient) CONNECT(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodConnect, url, body, &result)
}
// OPTIONS is an alias to do the Request with the http.MethodOptions method.
func (c *JSONClient) OPTIONS(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodOptions, url, body, &result)
}
// TRACE is an alias to do the Request with the http.MethodTrace method.
func (c *JSONClient) TRACE(ctx context.Context, url string, body interface{}, result interface{}) error {
return c.Request(ctx, http.MethodTrace, url, body, &result)
}
// Clone will clone an instance of the JSONClient without references to the old one.
func (c *JSONClient) Clone() *JSONClient {
return JSON(c.Client.Clone())
}
func marshal(body interface{}) (data []byte, err error) {
if body != nil {
data, err = json.Marshal(body)
if err != nil {
return nil, newError(nil, nil, fmt.Errorf("marshaling request error: %w", err))
}
}
return data, nil
}