"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdapterEndpoint = exports.DEFAULT_TRANSPORT_NAME = void 0;
const response_1 = require("../cache/response");
const transports_1 = require("../transports");
const util_1 = require("../util");
const validation_1 = require("../validation");
const error_1 = require("../validation/error");
const logger = (0, util_1.makeLogger)('AdapterEndpoint');
exports.DEFAULT_TRANSPORT_NAME = 'default_single_transport';
/**
 * Main class to represent an endpoint within an External Adapter
 */
class AdapterEndpoint {
    constructor(params) {
        this.name = params.name;
        this.aliases = params.aliases;
        // These ifs are annoying but it's to make it type safe
        if ('transportRoutes' in params) {
            this.transportRoutes = params.transportRoutes;
            this.customRouter = params.customRouter;
            this.defaultTransport = params.defaultTransport;
        }
        else {
            this.transportRoutes = new transports_1.TransportRoutes().register(exports.DEFAULT_TRANSPORT_NAME, params.transport);
        }
        this.inputParameters = params.inputParameters || new validation_1.InputParameters({});
        this.rateLimiting = params.rateLimiting;
        this.cacheKeyGenerator = params.cacheKeyGenerator;
        this.customInputValidation = params.customInputValidation;
        this.customOutputValidation = params.customOutputValidation;
        this.overrides = params.overrides;
        this.requestTransforms = [this.symbolOverrider.bind(this), ...(params.requestTransforms || [])];
    }
    /**
     * Performs all necessary initialization processes that are async or need async initialized dependencies
     *
     * @param dependencies - all dependencies initialized at the adapter level
     * @param adapterSettings - configuration for the adapter
     */
    async initialize(adapterName, dependencies, adapterSettings) {
        this.adapterName = adapterName;
        const responseCache = new response_1.ResponseCache({
            dependencies,
            adapterSettings: adapterSettings,
            adapterName,
            endpointName: this.name,
            inputParameters: this.inputParameters,
        });
        const transportDependencies = {
            ...dependencies,
            responseCache,
        };
        logger.debug(`Initializing transports for endpoint "${this.name}"...`);
        for (const [transportName, transport] of this.transportRoutes.entries()) {
            await transport.initialize(transportDependencies, adapterSettings, this.name, transportName);
        }
    }
    /**
     * Takes the incoming request and applies all request transforms in the adapter
     *
     * @param req - the current adapter request
     * @returns the request after passing through all request transforms
     */
    runRequestTransforms(req, settings) {
        for (const transform of this.requestTransforms) {
            transform(req, settings);
        }
    }
    getRequestOverrides(data, overrides) {
        const overrideAdapterName = (0, util_1.getCanonicalAdapterName)(data['adapterNameOverride']);
        const adapterName = (0, util_1.getCanonicalAdapterName)(this.adapterName);
        const canonicalOverrides = (0, util_1.canonicalizeAdapterNameKeys)(overrides);
        return canonicalOverrides?.[overrideAdapterName] || canonicalOverrides?.[adapterName];
    }
    /**
     * Default request transform that takes requests and manipulates base params
     *
     * @param adapter - the current adapter
     * @param req - the current adapter request
     * @returns the modified (or new) request
     */
    symbolOverrider(req) {
        const data = req.requestContext.data;
        const rawRequestBody = req.body;
        const requestOverrides = this.getRequestOverrides(data, rawRequestBody.data?.overrides);
        const base = data['base'];
        if (requestOverrides?.[base]) {
            // Perform overrides specified in the request payload
            data['base'] = requestOverrides[base];
        }
        else if (this.overrides?.[base]) {
            // Perform hardcoded adapter overrides
            data['base'] = this.overrides[base];
        }
        return req;
    }
    getTransportNameForRequest(req, settings) {
        // If there's only one transport, return it
        if (this.transportRoutes.get(exports.DEFAULT_TRANSPORT_NAME)) {
            return exports.DEFAULT_TRANSPORT_NAME;
        }
        // Attempt to get the transport to use from:
        //   1. Custom router (whatever logic the user wrote)
        //   2. Default router (try to get it from the input params)
        //   3. Default transport (if it was specified in the instance params)
        const rawTransportName = (this.customRouter && this.customRouter(req, settings)) ||
            this.defaultRouter(req) ||
            this.defaultTransport;
        if (!rawTransportName) {
            throw new error_1.AdapterError({
                statusCode: 400,
                message: `No result was fetched from a custom router, no transport was specified in the input parameters, and this endpoint does not have a default transport set.`,
            });
        }
        const transportName = rawTransportName.toLowerCase();
        if (!this.transportRoutes.get(transportName)) {
            throw new error_1.AdapterError({
                statusCode: 400,
                message: `No transport found for key "${transportName}", must be one of ${JSON.stringify(this.transportRoutes.routeNames())}`,
            });
        }
        logger.debug(`Request will be routed to transport "${transportName}"`);
        return transportName;
    }
    /**
     * Default routing strategy. Will try and use the transport override if present
     * or transport input parameter in the request body.
     *
     * @param req - The current adapter request
     * @returns the transport param or override if present
     */
    defaultRouter(req) {
        // DefaultRouter is called before customInputValidation, so we don't have
        // the validation data on the requestContext yet.
        const data = {};
        const rawRequestBody = req.body;
        const requestOverrides = this.getRequestOverrides(data, rawRequestBody.data?.overrides);
        // Transport override
        if (requestOverrides?.['transport']) {
            return requestOverrides['transport'];
        }
        return rawRequestBody.data?.transport;
    }
}
exports.AdapterEndpoint = AdapterEndpoint;
//# sourceMappingURL=endpoint.js.map