-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.go
160 lines (127 loc) · 3.86 KB
/
client.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
package service
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
)
// Client defines the interface exposed by our API.
type Client interface {
AddContact(contact AddContactRequest) (*Contact, error)
GetContactByEmail(email string) (*Contact, error)
}
// ErrorResponse is returned by our service when an error occurs.
type ErrorResponse struct {
StatusCode int `json:"status_code"`
Message string `json:"message"`
}
func (e ErrorResponse) Error() string {
return fmt.Sprintf("%v: %v", e.StatusCode, e.Message)
}
// NewClient creates a Client that accesses a service at the given base URL.
func NewClient(baseURL string) Client {
httpClient := http.DefaultClient
return &DefaultClient{
http: httpClient,
BaseURL: baseURL,
}
}
// ===== DefaultClient =================================================================================================
// DefaultClient provides an implementation of the Client interface.
type DefaultClient struct {
http *http.Client
BaseURL string
}
// performRequestMethod constructs a request and uses `performRequest` to execute it.
func (c *DefaultClient) performRequestMethod(method string, path string, headers map[string]string, data interface{}, response interface{}) error {
req, err := c.newRequest(method, path, headers, data)
if err != nil {
return err
}
return c.performRequest(req, response)
}
// performRequest executes the given request, and uses `response` to parse the JSON response.
func (c *DefaultClient) performRequest(req *http.Request, response interface{}) error {
// perform the request
httpResponse, err := c.http.Do(req)
if err != nil {
return err
}
defer httpResponse.Body.Close()
// read the response
var responseBody []byte
if responseBody, err = ioutil.ReadAll(httpResponse.Body); err != nil {
return err
}
if httpResponse.StatusCode >= 400 {
contentTypeHeader := httpResponse.Header["Content-Type"]
if len(contentTypeHeader) != 0 && contentTypeHeader[0] == "application/json" {
var errResponse ErrorResponse
err := json.Unmarshal(responseBody, &errResponse)
if err == nil {
return errResponse
}
}
return &ErrorResponse{
StatusCode: httpResponse.StatusCode,
Message: httpResponse.Status,
}
}
// map the response to an object value
if err := json.Unmarshal(responseBody, &response); err != nil {
return err
}
return nil
}
// newRequest builds a new request using the given parameters.
func (c *DefaultClient) newRequest(method string, path string, headers map[string]string, data interface{}) (*http.Request, error) {
// Construct request body
var body io.Reader
if data != nil {
requestJSON, err := json.Marshal(data)
if err != nil {
return nil, err
}
body = bytes.NewReader(requestJSON)
}
// construct the request
req, err := http.NewRequest(method, c.BaseURL+path, body)
if err != nil {
return nil, err
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
for k, v := range headers {
req.Header.Set(k, v)
}
return req, nil
}
// ----- Add Contact ---------------------------------------------------------------------------------------------------
type AddContactRequest struct {
Email string `json:"email"`
Name string `json:"name"`
}
type ContactResponse struct {
Contact *Contact `json:"contact"`
}
func (c *DefaultClient) AddContact(contact AddContactRequest) (*Contact, error) {
var response ContactResponse
err := c.performRequestMethod(http.MethodPost, "/contacts", nil, contact, &response)
if err != nil {
return nil, err
}
return response.Contact, nil
}
func (c *DefaultClient) GetContactByEmail(email string) (*Contact, error) {
var response ContactResponse
var path = fmt.Sprintf("/contacts/%v", url.QueryEscape(email))
err := c.performRequestMethod(http.MethodGet, path, nil, nil, &response)
if err != nil {
return nil, err
}
return response.Contact, nil
}