@slack/client
Version:
A library for creating a Slack client
158 lines (129 loc) • 4.16 kB
JavaScript
// 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, client, apiCb) {
var jsonResponse;
var jsonParseErr;
try {
jsonResponse = JSON.parse(body);
} catch (parseErr) {
// TODO(leah): Emit an event here?
jsonParseErr = new Error('unable to parse Slack API Response');
return false;
}
if (!jsonParseErr && jsonResponse.warning) {
client.logger('warn', jsonResponse.warning);
}
try {
apiCb(jsonParseErr, 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, 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;