-
Notifications
You must be signed in to change notification settings - Fork 657
/
call-transport.js
170 lines (139 loc) · 4.48 KB
/
call-transport.js
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
// For some reason, I can't turn this off only for functions, so suppress all cases
/* eslint no-use-before-define: 0 */
/**
* Worker functions for making requests to Slack's API.
*/
var partial = require('lodash').partial;
var WEB_CLIENT_EVENTS = require('../events/client').WEB;
/**
*
* @param err
* @param retryOp
* @param apiCb
* @returns {boolean}
*/
var handleTransportError = function handleTransportError(err, retryOp, apiCb) {
if (!retryOp.retry(err)) {
apiCb(retryOp.mainError(), null);
return true;
}
return false;
};
/**
*
* @param retryArgs
* @param headers
* @returns {boolean}
*/
var handleRateLimitResponse = function handleRateLimitResponse(retryArgs, headers) {
var client = retryArgs.client;
var retryAfter = parseInt(headers['retry-after'], 10);
var headerSecs = Number.isInteger(retryAfter) ? retryAfter : 60;
var headerMs = headerSecs * 1000;
client.logger('info', 'Rate limited, will retry %s seconds', headerSecs);
client.requestQueue.pause();
setTimeout(function retryRateLimitedRequest() {
// Don't retry limit requests that were rejected due to retry-after
client.transport(retryArgs.task.args, partial(handleTransportResponse, retryArgs));
client.requestQueue.resume();
}, headerMs);
client.emit(WEB_CLIENT_EVENTS.RATE_LIMITED, headerSecs);
return false;
};
/**
*
*
* If this is reached, it means an error outside the normal error logic was received. These
* should be very unusual as standard errors come back with a 200 code and an "error"
* property.
*
* Given that, assume that something really weird happened and retry the request as normal.
*
* @param retryOp
* @param apiCb
* @param statusCode
*/
var handleHttpErr = function handleHttpErr(retryOp, apiCb, statusCode) {
var httpErr = new Error('Unable to process request, received bad ' + statusCode + ' error');
if (!retryOp.retry(httpErr)) {
apiCb(httpErr, null);
return true;
}
return false;
};
/**
*
* @param body
* @param client
* @param apiCb
* @returns {boolean}
*/
var handleHttpResponse = function handleHttpResponse(body, headers, client, apiCb) {
var jsonResponse;
var jsonError;
try {
jsonResponse = JSON.parse(body);
} catch (parseErr) {
// TODO(leah): Emit an event here?
jsonError = new Error('unable to parse Slack API Response');
return false;
}
if (!jsonError && jsonResponse.warning) {
client.logger('warn', jsonResponse.warning);
}
if (!jsonResponse.ok) {
jsonError = new Error(jsonResponse.error);
client.logger('error', jsonResponse.error);
}
jsonResponse.scopes = (headers['x-oauth-scopes'] === undefined) ?
[] :
headers['x-oauth-scopes'].trim().split(/\s*,\s*/);
jsonResponse.acceptedScopes = (headers['x-accepted-oauth-scopes'] === undefined) ?
[] :
headers['x-accepted-oauth-scopes'].trim().split(/\s*,\s*/);
try {
apiCb(jsonError, jsonResponse);
} catch (callbackErr) {
// Never retry requests that fail in the callback
client.logger('error', callbackErr);
}
return true;
};
/**
*
* @param retryArgs
* @param err
* @param headers
* @param statusCode
* @param body
*/
var handleTransportResponse = function handleTransportResponse(
retryArgs, err, headers, statusCode, body) {
var callQueueCb = false;
if (err) {
callQueueCb = handleTransportError(err, retryArgs.retryOp, retryArgs.task.cb);
} else if (statusCode !== 200) {
// There are only a couple of possible bad cases here:
// - 429: the application is being rate-limited. The client is designed to automatically
// respect this
// - 4xx or 5xx: something bad, but probably recoverable, has happened, so requeue the
// request
if (statusCode === 429) {
callQueueCb = handleRateLimitResponse(retryArgs, headers);
} else {
callQueueCb = handleHttpErr(retryArgs.retryOp, retryArgs.task.cb, statusCode);
}
} else {
callQueueCb = handleHttpResponse(body, headers, retryArgs.client, retryArgs.task.cb);
}
// This is always an empty callback, even if there's an error, as it's used to signal the
// request queue that a request has completed processing, and nothing else.
if (callQueueCb) {
retryArgs.queueCb();
}
};
module.exports.handleTransportResponse = handleTransportResponse;
module.exports.handleTransportError = handleTransportError;
module.exports.handleRateLimitResponse = handleRateLimitResponse;
module.exports.handleHttpErr = handleHttpErr;
module.exports.handleHttpResponse = handleHttpResponse;