forked from schmorrison/Zoho
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http.go
145 lines (120 loc) · 4.35 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
package zoho
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"reflect"
)
// Endpoint defines the data required to interact with most Zoho REST api endpoints
type Endpoint struct {
Method HTTPMethod
URL string
Name string
ResponseData interface{}
RequestBody interface{}
URLParameters map[string]Parameter
}
// Parameter is used to provide URL Parameters to zoho endpoints
type Parameter string
// HTTPRequest is the function which actually performs the request to a Zoho endpoint as specified by the provided endpoint
func (z *Zoho) HTTPRequest(endpoint *Endpoint) (err error) {
if reflect.TypeOf(endpoint.ResponseData).Kind() != reflect.Ptr {
return fmt.Errorf("Failed, you must pass a pointer in the ResponseData field of endpoint")
}
dataType := reflect.TypeOf(endpoint.ResponseData).Elem()
data := reflect.New(dataType).Interface()
endpointURL := endpoint.URL
q := url.Values{}
for k, v := range endpoint.URLParameters {
if v != "" {
q.Set(k, string(v))
}
}
var reqBody io.Reader
if endpoint.RequestBody != nil {
b, err := json.Marshal(endpoint.RequestBody)
if err != nil {
return fmt.Errorf("Failed to create json from request body")
}
reqBody = bytes.NewReader(b)
}
req, err := http.NewRequest(string(endpoint.Method), fmt.Sprintf("%s?%s", endpointURL, q.Encode()), reqBody)
if err != nil {
return fmt.Errorf("Failed to create a request for %s: %s", endpoint.Name, err)
}
req.Header.Add("Authorization", "Zoho-oauthtoken "+z.oauth.token.AccessToken)
// Add mandatory header for expense apis
if z.organizationID != "" {
req.Header.Add("X-com-zoho-expense-organizationid", z.organizationID)
}
resp, err := z.client.Do(req)
if err != nil {
return fmt.Errorf("Failed to perform request for %s: %s", endpoint.Name, err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("Failed to read body of response for %s: got status %s: %s", endpoint.Name, resolveStatus(resp), err)
}
err = json.Unmarshal(body, data)
if err != nil {
return fmt.Errorf("Failed to unmarshal data from response for %s: got status %s: %s", endpoint.Name, resolveStatus(resp), err)
}
endpoint.ResponseData = data
return nil
}
// HTTPStatusCode is a type for resolving the returned HTTP Status Code Message
type HTTPStatusCode int
// HTTPStatusCodes is a map of possible HTTP Status Code and Messages
var HTTPStatusCodes = map[HTTPStatusCode]string{
200: "The API request is successful.",
201: "Request fulfilled for single record insertion.",
202: "Request fulfilled for multiple records insertion.",
204: "There is no content available for the request.",
304: "The requested page has not been modified. In case \"If-Modified-Since\" header is used for GET APIs",
400: "The request or the authentication considered is invalid.",
401: "Invalid API key provided.",
403: "No permission to do the operation.",
404: "Invalid request.",
405: "The specified method is not allowed.",
413: "The server did not accept the request while uploading a file, since the limited file size has exceeded.",
415: "The server did not accept the request while uploading a file, since the media/ file type is not supported.",
429: "Number of API requests per minute/day has exceeded the limit.",
500: "Generic error that is encountered due to an unexpected server error.",
}
func resolveStatus(r *http.Response) string {
if v, ok := HTTPStatusCodes[HTTPStatusCode(r.StatusCode)]; ok {
return v
}
return ""
}
// HTTPHeader is a type for defining possible HTTPHeaders that zoho request could return
type HTTPHeader string
const (
rateLimit HTTPHeader = "X-RATELIMIT-LIMIT"
rateLimitRemaining HTTPHeader = "X-RATELIMIT-REMAINING"
rateLimitReset HTTPHeader = "X-RATELIMIT-RESET"
)
func checkHeaders(r http.Response, header HTTPHeader) string {
value := r.Header.Get(string(header))
if value != "" {
return value
}
return ""
}
// HTTPMethod is a type for defining the possible HTTP request methods that can be used
type HTTPMethod string
const (
// HTTPGet is the GET method for http requests
HTTPGet HTTPMethod = "GET"
// HTTPPost is the POST method for http requests
HTTPPost HTTPMethod = "POST"
// HTTPPut is the PUT method for http requests
HTTPPut HTTPMethod = "PUT"
// HTTPDelete is the DELETE method for http requests
HTTPDelete HTTPMethod = "DELETE"
)