-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.go
157 lines (128 loc) · 4.41 KB
/
api.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
package hrvst
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/pkg/errors"
httpErrors "github.com/sergeykuzmich/harvestapp-sdk/http_errors"
)
const clientVersion = "1.0.0"
const harvestDomain = "api.harvestapp.com"
const harvestAPIVersion = "v2"
// API is main Harvest API Client instance of this SDK.
type API struct {
client *http.Client
apiURL string
AccountID string
AccessToken string
}
// Client initializes Harvest API worker with auth credentials:
// - Account ID;
// - API Token.
func Client(accountID string, accessToken string) *API {
a := API{}
a.client = http.DefaultClient
a.apiURL = "https://" + harvestDomain + "/" + harvestAPIVersion
a.AccountID = accountID
a.AccessToken = accessToken
return &a
}
// addHeaders applies relevant User-Agent, Accept & Authorization.
func (a *API) addHeaders(req *http.Request) {
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("User-Agent", "github.com/sergeykuzmich/harvestapp-sdk v"+clientVersion)
req.Header.Set("Harvest-Account-Id", a.AccountID)
req.Header.Set("Authorization", "Bearer "+a.AccessToken)
}
// decodeBody reads response JSON to provided target interface.
func (a *API) decodeBody(jsonBody []byte, target interface{}) error {
err := json.Unmarshal(jsonBody, target)
if err != nil {
return errors.Wrapf(err, "JSON decode failed: `%s`", string(jsonBody))
}
return nil
}
// createRequest prepares & fills http.Request with URI, query, body & headers.
func (a *API) createRequest(method string, path string, queryData Arguments, postData interface{}) *http.Request {
url := fmt.Sprintf("%s%s", a.apiURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, queryData.toURLValues().Encode())
buffer := new(bytes.Buffer)
if postData != nil {
err := json.NewEncoder(buffer).Encode(postData)
if err != nil {
panic(err)
}
}
req, err := http.NewRequest(method, urlWithParams, buffer)
if err != nil {
panic(err)
}
a.addHeaders(req)
return req
}
// doRequest performs http request & returns raw body
func (a *API) doRequest(req *http.Request) (body []byte, err error) {
res, err := a.client.Do(req)
if err != nil {
return nil, errors.Wrapf(err, "HTTP request failed: `%s`", req.URL.Path)
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode > 299 {
return nil, httpErrors.CreateFromResponse(res)
}
body, err = ioutil.ReadAll(res.Body)
return body, nil
}
// Get performs direct GET request to Harvest API with:
// - path - https://API.harvestapp.com/v2/{path};
// - args - as query variables;
// - target - interface response should be placed to;
// ** - auth headers are included.
func (a *API) Get(path string, args Arguments, target interface{}) error {
req := a.createRequest("GET", path, args, nil)
body, err := a.doRequest(req)
if err != nil {
return err
}
return a.decodeBody(body, target)
}
// Delete performs direct DELETE request to Harvest API with:
// - path - https://API.harvestapp.com/v2/{path};
// - args - as query variables;
// ** - auth headers are included.
func (a *API) Delete(path string, args Arguments) error {
req := a.createRequest("DELETE", path, args, nil)
_, err := a.doRequest(req)
return err
}
// Shared PATCH, POST, PUT codebase.
// Used as a wrapper to make API request with body.
func (a *API) ppp(method string, path string, args Arguments, body interface{}, target interface{}) error {
req := a.createRequest(method, path, args, body)
responseBody, err := a.doRequest(req)
if err != nil {
return err
}
return a.decodeBody(responseBody, target)
}
// Post performs direct POST request to Harvest API with:
// - path - https://API.harvestapp.com/v2/{path};
// - args - as query variables;
// - body - as body;
// - target - interface response should be placed to;
// ** - auth headers are included.
func (a *API) Post(path string, args Arguments, body interface{}, target interface{}) error {
return a.ppp("POST", path, args, body, target)
}
// Patch performs direct PATCH request to Harvest API with:
// - path - https://API.harvestapp.com/v2/{path};
// - args - as query variables;
// - body - as body;
// - target - interface response should be placed to;
// ** - auth headers are included.
func (a *API) Patch(path string, args Arguments, body interface{}, target interface{}) error {
return a.ppp("PATCH", path, args, body, target)
}