/
api.go
294 lines (264 loc) · 8.87 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
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
package zendesk
import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
l "github.com/tylerconlee/slab/log"
)
var log = l.Log
// NumTickets is the number of tickets processed on the last loop
var NumTickets int
// LastProcessed is a timestamp of when the last loop was ran
var LastProcessed time.Time
// GetAllTickets grabs the latest tickets from Zendesk and returns the JSON
// Zendesk Endpoint: /incremental/tickets.json?include=slas
// TODO: Handle paging from the Incremental API
func GetAllTickets() (tickets ZenOutput) {
log.Info("Requesting all tickets from Zendesk for SLA", map[string]interface{}{
"module": "zendesk",
})
t := time.Now().AddDate(0, 0, -3).Unix()
zen := c.Zendesk.URL + "/api/v2/incremental/tickets.json?include=slas,metric_events&start_time=" + strconv.FormatInt(t, 10)
resp := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, zen)
tickets = parseTicketJSON(resp)
nextPage := ""
if tickets.NextPage != nil && tickets.NextPage != "<nil>" {
if tickets.NextPage.(string) != zen && strings.Contains(tickets.NextPage.(string), "page") {
nextPage = tickets.NextPage.(string)
}
// While NextPage is blank
for nextPage != "" && strings.Contains(nextPage, "page") {
// Run GetNextPage and store the results in output
output := getNextPage(nextPage)
// Append the results to the main tickets list
tickets.Tickets = append(tickets.Tickets, output.Tickets...)
// Reset next page to blank to avoid unnecessary calls
nextPage = ""
// Check to see if NextPage from output is blank
if output.NextPage != nil {
l.Log.Info("Ticket output contains a non-nil next page", map[string]interface{}{
"module": "zendesk",
"ticket": tickets.NextPage,
"output": output.NextPage,
})
// If the output's next page is different from the ticket's next page
if output.NextPage.(string) != tickets.NextPage.(string) {
// Set the next page as the output's next page to run the loop again
nextPage = output.NextPage.(string)
}
}
}
} else {
l.Log.Info("Ticket output contains a nil next page", map[string]interface{}{
"module": "zendesk",
"ticket": tickets.NextPage,
})
}
NumTickets = len(tickets.Tickets)
LastProcessed = time.Now()
log.Info("Request Complete for getAllTickets(). Parsing Ticket Data", map[string]interface{}{
"module": "zendesk",
"num_tickets": NumTickets,
})
return tickets
}
func getNextPage(nextURL string) (output ZenOutput) {
nextResp := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, nextURL)
l.Log.Info("Requesting next page of Zendesk tickets", map[string]interface{}{
"module": "zendesk",
"nextpage": nextURL,
})
return parseTicketJSON(nextResp)
}
// GetTicketEvents grabs the latest ticket events from Zendesk and returns the
// JSON
// Zendesk Endpoint: /api/v2/incremental/ticket_events.json
func GetTicketEvents() (tickets EventOutput) {
log.Info("Requesting latest ticket events for updates", map[string]interface{}{
"module": "zendesk",
})
hour := time.Duration(1 * time.Hour)
t := time.Now().Add(-hour)
zen := c.Zendesk.URL + "/api/v2/incremental/ticket_events.json?start_time=" + strconv.FormatInt(t.Unix(), 10)
resp := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, zen)
tickets = parseEventJSON(resp)
log.Info("Request Complete for getTicketEvents(). Parsing Ticket Data", map[string]interface{}{
"module": "zendesk",
"num_tickets": len(tickets.Event),
})
return tickets
}
// GetTicket gets the details about an individual ticket from Zendesk
func GetTicket(id int) (ticket Ticket) {
log.Info("Requesting data on individual ticket", map[string]interface{}{
"module": "zendesk",
"ticket": id,
})
zen := c.Zendesk.URL + "/api/v2/tickets/" + strconv.Itoa(id) + ".json"
resp := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, zen)
bytes := json.RawMessage(resp)
tick := TicketGroup{}
err := json.Unmarshal(bytes, &tick)
if err != nil {
log.Error("Error parsing Zendesk JSON", map[string]interface{}{
"module": "zendesk",
"error": err,
"resp": bytes,
})
}
log.Info("Request Complete for GetTicket(). Parsing Ticket Data", map[string]interface{}{
"module": "zendesk",
})
return tick.Ticket
}
// GetTicketRequester takes the requester ID from the tickets grabbed in
// GetAllTickets and sends a request to Zendesk for the user info
// Zendesk Endpoint /users/{USER-ID}.json
func GetTicketRequester(user int) (output User) {
log.Info("Starting request to Zendesk for user info", map[string]interface{}{
"module": "zendesk",
"user": user,
})
zen := c.Zendesk.URL + "/api/v2/users/" + strconv.Itoa(user) + ".json"
data := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, zen)
log.Info("Request Complete for GetTicketRequester(). Parsing Ticket Data", map[string]interface{}{
"module": "zendesk",
"user": user,
})
resp := json.RawMessage(data)
users := Users{}
err := json.Unmarshal(resp, &users)
if err != nil {
log.Error("Error with Zendesk requestor parsing", map[string]interface{}{
"module": "zendesk",
"error": err,
})
}
return users.User
}
// GetOrganization takes the org ID from the tickets grabbed in
// GetAllTickets and sends a request to Zendesk for the Org information
// Zendesk Endpoint /users/{USER-ID}/organizations.json
func GetOrganization(user int) (org Orgs) {
log.Info("Starting request to Zendesk for organization info", map[string]interface{}{
"module": "zendesk",
"user": user,
})
zen := c.Zendesk.URL + "/api/v2/users/" + strconv.Itoa(user) + "/organizations.json"
data := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, zen)
log.Info("Request Complete for GetOrganization(). Parsing Organization Data", map[string]interface{}{
"module": "zendesk",
"user": user,
})
resp := json.RawMessage(data)
orgs := Organizations{}
err := json.Unmarshal(resp, &orgs)
if err != nil {
log.Error(
"Error with Zendesk org parsing.", map[string]interface{}{
"module": "zendesk",
"error": err,
})
}
return orgs.Orgs
}
// GetRequestedTickets takes a user ID and sends a request to Zendesk to grab
// the IDs of tickets requested by that user
// Zendesk Endpoint /users/{USER-ID}/tickets/requested.json
func GetRequestedTickets(user int) (output ZenOutput) {
log.Info("Starting request to Zendesk for requested ticket info", map[string]interface{}{
"module": "zendesk",
"user": user,
})
zen := c.Zendesk.URL + "/api/v2/users/" + strconv.Itoa(user) + "/tickets/requested.json"
data := makeRequest(c.Zendesk.User, c.Zendesk.APIKey, zen)
log.Info("Request Complete for GetRequestedTickets(). Parsing Organization Data", map[string]interface{}{
"module": "zendesk",
"user": user,
})
resp := json.RawMessage(data)
err := json.Unmarshal(resp, &output)
if err != nil {
log.Error("Error with Zendesk ticket parsing", map[string]interface{}{
"module": "zendesk",
"error": err,
})
}
return
}
// makeRequests takes the Zendesk auth information and sends the curl request
// to Zendesk and returns a JSON blob
func makeRequest(user string, key string, url string) (responseData []byte) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Error("Error forming HTTP request", map[string]interface{}{
"module": "zendesk",
"error": err,
})
}
req.SetBasicAuth(user, key)
// create custom http.Client to manually set timeout and disable keepalive
// in an attempt to avoid EOF errors
var netClient = &http.Client{
Timeout: time.Second * 480,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
},
}
resp, err := netClient.Do(req)
if err != nil {
log.Error("Error reaching Zendesk", map[string]interface{}{
"module": "zendesk",
"error": err,
})
return
}
log.Info("Zendesk request received", map[string]interface{}{
"module": "zendesk",
"status": resp.StatusCode,
})
defer resp.Body.Close()
responseData, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("Error reading Zendesk response", map[string]interface{}{
"module": "zendesk",
"error": err,
})
return
}
return responseData
}
// parseJSON takes the JSON from makeRequest and unmarshals it into the
// ZenOutput struct, allowing the data to be accessed
func parseTicketJSON(data []byte) (output ZenOutput) {
// Read response from HTTP client
bytes := json.RawMessage(data)
err := json.Unmarshal(bytes, &output)
if err != nil {
log.Error("Error parsing Zendesk JSON", map[string]interface{}{
"module": "zendesk",
"error": err,
"resp": bytes,
})
}
return output
}
// parseJSON takes the JSON from makeRequest and unmarshals it into the
// ZenOutput struct, allowing the data to be accessed
func parseEventJSON(data []byte) (output EventOutput) {
// Read response from HTTP client
bytes := json.RawMessage(data)
err := json.Unmarshal(bytes, &output)
if err != nil {
log.Error("Error parsing Zendesk JSON", map[string]interface{}{
"module": "zendesk",
"error": err,
})
}
return output
}