"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.pollResponseFromCache = exports.calculateFeedId = exports.calculateAdapterName = exports.calculateHttpRequestKey = exports.calculateCacheKey = void 0;
const crypto_1 = __importDefault(require("crypto"));
const util_1 = require("../util");
__exportStar(require("./factory"), exports);
__exportStar(require("./local"), exports);
__exportStar(require("./redis"), exports);
const logger = (0, util_1.makeLogger)('Cache');
// Uses calculateKey to generate a unique key from the endpoint name, data, and input parameters
const calculateCacheKey = ({ data, adapterName, endpointName, adapterSettings, transportName, }) => {
    const calculatedKey = calculateKey({
        data,
        adapterSettings,
        endpointName,
        transportName,
    });
    const cachePrefix = adapterSettings.CACHE_PREFIX ? `${adapterSettings.CACHE_PREFIX}-` : '';
    const cacheKey = `${cachePrefix}${adapterName}-${calculatedKey}`;
    (0, util_1.censorLogs)(() => logger.trace(`Generated cache key for request: "${cacheKey}"`));
    return cacheKey;
};
exports.calculateCacheKey = calculateCacheKey;
// Used to coalesce HTTP requests within the same endpoint
const calculateHttpRequestKey = ({ data, context, transportName, }) => {
    const key = calculateKey({
        data,
        transportName,
        adapterSettings: context.adapterSettings,
        endpointName: context.endpointName,
    });
    (0, util_1.censorLogs)(() => logger.trace(`Generated HTTP request queue key: "${key}"`));
    return key;
};
exports.calculateHttpRequestKey = calculateHttpRequestKey;
const calculateKey = ({ data, endpointName, transportName, adapterSettings, }) => {
    const paramsKey = Object.keys(data).length
        ? calculateParamsKey(data, adapterSettings.MAX_COMMON_KEY_SIZE)
        : adapterSettings.DEFAULT_CACHE_KEY;
    return `${endpointName}-${transportName}-${paramsKey}`;
};
const calculateAdapterName = (adapterName, data) => {
    if (Object.keys(data).length === 0) {
        logger.trace('Cannot generate Adapter Name without data, using default');
        return adapterName;
    }
    if (data && typeof data !== 'object') {
        logger.trace('Cannot generate Adapter Name without valid data, using default');
        return adapterName;
    }
    if (data['adapterNameOverride'] && typeof data['adapterNameOverride'] === 'string') {
        return data['adapterNameOverride'].toUpperCase();
    }
    else {
        return adapterName;
    }
};
exports.calculateAdapterName = calculateAdapterName;
const calculateFeedId = ({ adapterSettings, }, data) => {
    if (Object.keys(data).length === 0) {
        logger.trace(`Cannot generate Feed ID without data`);
        return 'N/A';
    }
    return calculateParamsKey(data, adapterSettings.MAX_COMMON_KEY_SIZE);
};
exports.calculateFeedId = calculateFeedId;
/**
 * Calculates a unique key from the provided data.
 *
 * @param data - the request data/body, i.e. the adapter input params
 * @param maxSize - the max length for the cache key params section
 * @returns the calculated unique key
 *
 * @example
 * ```
 * calculateKey({ base: 'ETH', quote: 'BTC' })
 * // equals `{"base":"eth","quote":"btc"}`
 * ```
 */
const calculateParamsKey = (data, maxSize) => {
    if (data && typeof data !== 'object') {
        throw new Error('Data to calculate cache key should be an object');
    }
    const cacheKey = JSON.stringify(data, (_, value) => {
        if (value && typeof value === 'string') {
            return value.toLowerCase();
        }
        return value;
    });
    if (cacheKey.length > maxSize) {
        logger.debug(`Generated cache key for adapter request is bigger than the MAX_COMMON_KEY_SIZE and will be hashed`);
        const shasum = crypto_1.default.createHash('sha1');
        shasum.update(cacheKey);
        return shasum.digest('base64');
    }
    return cacheKey;
};
/**
 * Polls the provided Cache for an AdapterResponse set in the provided key. If the maximum
 * amount of retries is exceeded, it returns undefined instead.
 *
 * @param cache - a Cache instance
 * @param key - the key generated from an AdapterRequest that corresponds to the desired AdapterResponse
 * @param retry - current retry, only for internal use
 * @returns the AdapterResponse if found, else undefined
 */
const pollResponseFromCache = async (cache, key, options, retry = 0) => {
    if (retry > options.maxRetries) {
        // Ideally this shouldn't happen often (p99 of reqs should be found in the cache)
        logger.debug('Exceeded max cache polling retries');
        return undefined;
    }
    logger.trace('Getting response from cache...');
    const response = await cache.get(key);
    if (response) {
        logger.trace('Got response from cache');
        return response;
    }
    if (options.maxRetries === 0) {
        logger.debug(`Response not found, retries disabled`);
        return undefined;
    }
    logger.debug(`Response not found, sleeping ${options.sleep} milliseconds...`);
    await (0, util_1.sleep)(options.sleep);
    return (0, exports.pollResponseFromCache)(cache, key, options, retry + 1);
};
exports.pollResponseFromCache = pollResponseFromCache;
//# sourceMappingURL=index.js.map