Home Reference Source

lib/RequestClient/Request.js

import URLHelper from '../Utils/URLHelper';
import RegionHelper from '../Utils/RegionHelper';

function Request(
    config,
    serviceName,
    endpoint,
    methodName,
    httpMethodType,
    limiter,
    body, // POST/PUT
    isTournament = false,
) {
    this.payload = {
        method: httpMethodType || 'GET',
        serviceName,
        endpoint,
        query: [],
        region: '',
        body,
        isTournament,
    };
    if (this.payload.method === 'GET') delete this.payload.body;
    this.config = config;
    this.retriesLeft = this.config.requestOptions.numberOfRetriesBeforeAbort;
    this.methodName = methodName;
    this.limiter = limiter;
}

Request.prototype.region = function(region) {
    if (this.payload.region)
        throw new Error('Do not call Request.region twice.');
    if (!RegionHelper.isValidRegion(region))
        throw new Error('Bad region value in Request.region');
    if (region) this.payload.region = region;
    return this;
};

Request.prototype.query = function(obj) {
    if (typeof obj !== 'object')
        throw new Error('Request.query takes in an object');
    if (obj) this.payload.query.push(obj);
    return this;
};

Request.prototype.then = function then(resolve, reject) {
    const self = this;
    return new Promise((innerResolve, innerReject) =>
        self.callback(
            (err, res) => (err ? innerReject(err) : innerResolve(res)),
        ),
    ).then(resolve, reject);
};

Request.prototype.catch = callback => this.then(null, callback);

Request.prototype.callback = function(cb) {
    if (!this.payload.region) this.payload.region = this.config.region;

    const {
        endpoint,
        query,
        region,
        serviceName,
        body,
        isTournament,
    } = this.payload;
    const url = URLHelper.getURLWithQuery(
        RegionHelper.asPlatformID(region),
        serviceName,
        endpoint,
        query,
        isTournament,
    );
    const { key: token } = this.config;

    this.execute(url, body, token, this.retriesLeft, cb);
};

Request.prototype.execute = async function(url, body, token, retriesLeft, cb) {
    const { cacheOptions, debugOptions, requestOptions } = this.config;
    const fn = async (err, data) => {
        const debugURL = `${url}${
            debugOptions.showKey ? URLHelper.getAPIKey(url, token) : ''
        }`;
        try {
            if (data) {
                if (debugOptions.isEnabled) {
                    debugOptions.loggers.cache.get(debugURL);
                }
                ok(data)(cb);
            } else {
                if (debugOptions.isEnabled) {
                    const { method } = this.payload;
                    let debugString = `[${method}] => ${debugURL}`;
                    if (method !== 'GET') debugString += ' with body: %O';
                    if (body)
                        debugOptions.loggers.request.outgoing(
                            debugString,
                            body,
                        );
                    else debugOptions.loggers.request.outgoing(debugString);
                }
                const res = await this.limiter.executing({
                    url,
                    method: this.payload.method,
                    body,
                    token,
                });

                if (debugOptions.isEnabled) {
                    debugOptions.loggers.request.incoming.success(debugURL);
                }

                try {
                    const blob = JSON.parse(res);
                    if (
                        cacheOptions.cache &&
                        cacheOptions.ttls[this.methodName] > 0
                    ) {
                        cacheOptions.cache.set(
                            {
                                key: url,
                                ttl: cacheOptions.ttls[this.methodName],
                            },
                            blob,
                        );
                        if (debugOptions.isEnabled) {
                            debugOptions.loggers.cache.set(`${url}`);
                        }
                    }
                    ok(blob)(cb);
                } catch (ex) {
                    console.log(ex);
                }
            }
        } catch ({ statusCode }) {
            if (debugOptions.isEnabled) {
                debugOptions.loggers.request.incoming.failure(
                    `[${statusCode}] ${debugURL}`,
                );
            }
            if (requestOptions.shouldRetry && shouldRetry(statusCode)) {
                if (retriesLeft > 0) {
                    return setTimeout(
                        () =>
                            this.execute(url, body, token, retriesLeft - 1, cb),
                        requestOptions.delayBeforeRetry,
                    );
                }
            }
            error(statusCode)(cb);
        }
    };
    if (cacheOptions.cache) {
        cacheOptions.cache.get({ key: url }, fn);
    } else {
        fn();
    }
};

const shouldRetry = statusCode => statusCode === 500 || statusCode === 503;

const ok = blob => cb => cb(null, blob);
const error = statusCode => cb => cb(statusCode, null);

export default Request;