channelape-sdk
Version:
A client for interacting with ChannelApe's API
146 lines • 7.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const RequestLogger_1 = require("./utils/RequestLogger");
const ChannelApeError_1 = require("./model/ChannelApeError");
const limiter_1 = require("limiter");
const util = require("util");
const GENERIC_ERROR_CODE = -1;
const IMMEDIATELY_FIRE_RATE_LIMITED_CALLBACK = false;
const DEFAULT_API_CALLS_PER_INTERVAL = 20;
const DEFAULT_API_LIMIT_INTERVAL = 'second';
const CHANNEL_APE_API_RETRY_TIMEOUT_MESSAGE = `A problem with the ChannelApe API has been encountered.
Your request was tried a total of %s times over the course of %s milliseconds`;
class RequestClientWrapper {
constructor(client, logLevel, endpoint, maximumRequestRetryTimeout) {
this.client = client;
this.logLevel = logLevel;
this.maximumRequestRetryTimeout = maximumRequestRetryTimeout;
this.requestLogger = new RequestLogger_1.default(this.logLevel, endpoint);
this.limiter =
new limiter_1.RateLimiter(DEFAULT_API_CALLS_PER_INTERVAL, DEFAULT_API_LIMIT_INTERVAL, IMMEDIATELY_FIRE_RATE_LIMITED_CALLBACK);
}
get(uriOrOptions, callbackOrOptionsOrUndefined, callBackOrUndefined) {
const callStart = new Date();
const initialCallCountForThisRequest = 0;
this.makeRequest('get', callStart, initialCallCountForThisRequest, uriOrOptions, callbackOrOptionsOrUndefined, callBackOrUndefined);
}
put(uriOrOptions, callbackOrOptionsOrUndefined, callBackOrUndefined) {
const callStart = new Date();
const initialCallCountForThisRequest = 0;
this.makeRequest('put', callStart, initialCallCountForThisRequest, uriOrOptions, callbackOrOptionsOrUndefined, callBackOrUndefined);
}
makeRequest(method, callStart, numberOfCalls, uriOrOptions, callbackOrOptionsOrUndefined, callBackOrUndefined) {
this.limiter.removeTokens(1, (err, remainingRequest) => {
const callDetails = { callStart, callCountForThisRequest: numberOfCalls };
let callableRequestMethod;
try {
callableRequestMethod = this.getCallableRequestMethod(method);
}
catch (e) {
if (typeof callbackOrOptionsOrUndefined === 'function') {
this.handleResponse(e, {}, {}, callbackOrOptionsOrUndefined, '', undefined, callDetails);
}
else if (typeof callBackOrUndefined === 'function') {
this.handleResponse(e, {}, {}, callBackOrUndefined, '', undefined, callDetails);
}
return;
}
this.requestLogger.logCall(method, uriOrOptions, callbackOrOptionsOrUndefined);
if (typeof uriOrOptions === 'string') {
if (typeof callbackOrOptionsOrUndefined === 'function') {
return callableRequestMethod(uriOrOptions, (error, response, body) => {
this.handleResponse(error, response, body, callbackOrOptionsOrUndefined, uriOrOptions, undefined, callDetails);
});
}
return callableRequestMethod(uriOrOptions, callbackOrOptionsOrUndefined, (error, response, body) => {
this.handleResponse(error, response, body, callBackOrUndefined, uriOrOptions, callbackOrOptionsOrUndefined, callDetails);
});
}
if (typeof callbackOrOptionsOrUndefined === 'function') {
return callableRequestMethod(uriOrOptions, (error, response, body) => {
this.handleResponse(error, response, body, callbackOrOptionsOrUndefined, uriOrOptions.uri.toString(), undefined, callDetails);
});
}
return callableRequestMethod(uriOrOptions);
});
}
getCallableRequestMethod(method) {
let callableRequestMethod;
switch (method.toUpperCase()) {
case ('GET'):
callableRequestMethod = this.client.get;
break;
case ('PUT'):
callableRequestMethod = this.client.put;
break;
default:
throw new ChannelApeError_1.default('HTTP Request Method could not be determined', {}, '', []);
}
return callableRequestMethod;
}
handleResponse(error, response, body, callBackOrUndefined, uri, options, callDetails) {
this.requestLogger.logResponse(error, response, body);
let finalError = null;
if (this.didRequestTimeout(callDetails.callStart)) {
const maximumRetryLimitExceededMessage = this.getMaximumRetryLimitExceededMessage(callDetails.callStart, callDetails.callCountForThisRequest);
finalError = new ChannelApeError_1.default(maximumRetryLimitExceededMessage, response, uri, []);
}
else if (response == null) {
const badResponseMessage = 'No response was received from the server';
finalError = new ChannelApeError_1.default(badResponseMessage, undefined, uri, [{
code: 504,
message: badResponseMessage
}]);
}
else if (this.shouldRequestBeRetried(error, response) && response) {
this.retryRequest(response.method, uri, options, callBackOrUndefined, response, body, callDetails);
return;
}
if (error) {
finalError = new ChannelApeError_1.default(error.message, response, uri, [{
code: GENERIC_ERROR_CODE,
message: error.message
}]);
}
else if (this.isApiError(body) && response && body) {
finalError = new ChannelApeError_1.default(`${response.statusCode} ${response.statusMessage}`, response, uri, body.errors);
}
if (typeof callBackOrUndefined === 'function') {
callBackOrUndefined(finalError, response, body);
}
}
getMaximumRetryLimitExceededMessage(callStart, numberOfCalls) {
const elapsedTimeMs = new Date().getTime() - callStart.getTime();
return util.format(CHANNEL_APE_API_RETRY_TIMEOUT_MESSAGE, numberOfCalls, elapsedTimeMs);
}
didRequestTimeout(callStart) {
const now = new Date();
const totalElapsedTime = now.getTime() - callStart.getTime();
return totalElapsedTime > this.maximumRequestRetryTimeout;
}
shouldRequestBeRetried(error, response) {
if (response == null) {
return false;
}
return (!error && ((response.statusCode >= 500 && response.statusCode <= 599) || response.statusCode === 429));
}
isApiError(body) {
if (typeof body === 'undefined' || typeof body.errors === 'undefined') {
return false;
}
return body.errors.length > 0;
}
retryRequest(method, uri, options, callBackOrUndefined, response, body, callDetails) {
if (method == null) {
if (typeof callBackOrUndefined === 'function') {
const e = new ChannelApeError_1.default('HTTP Request Method could not be determined', response, uri, []);
callBackOrUndefined(e, response, body);
}
return;
}
const newNumberOfCalls = callDetails.callCountForThisRequest + 1;
this.makeRequest(method, callDetails.callStart, newNumberOfCalls, uri, options, callBackOrUndefined);
}
}
exports.default = RequestClientWrapper;
//# sourceMappingURL=RequestClientWrapper.js.map