"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectionManager = exports.DEFAULT_TTL_SECONDS = exports.DEFAULT_SCHEMA = void 0;
const cache_1 = require("../cache");
const secret_manager_1 = require("../secret-manager");
const pg_1 = __importDefault(require("pg"));
const context_1 = require("../context/context");
const connection_1 = require("./connection");
const lodash_1 = __importDefault(require("lodash"));
exports.DEFAULT_SCHEMA = "public";
exports.DEFAULT_TTL_SECONDS = 2;
let instance;
class ConnectionManager {
    constructor(config) {
        this.getCredentials = () => {
            return this.credentials;
        };
        this.cache = new cache_1.CacheContainer(new cache_1.MemoryStorage());
        this.config = config;
        this.credentials = {
            username: process.env[this.config.usernameEnv],
            password: process.env[this.config.passwordEnv],
            host: process.env[this.config.hostEnv],
            port: process.env[this.config.portEnv] || "5432",
            engine: "postgres"
        };
    }
    static Initialize(ctx, config) {
        return __awaiter(this, void 0, void 0, function* () {
            if (instance) {
                return instance;
            }
            const manager = new ConnectionManager(config);
            yield manager.retreieveSecret();
            instance = manager;
            return manager;
        });
    }
    static instance() {
        return instance;
    }
    retreieveSecret() {
        return __awaiter(this, void 0, void 0, function* () {
            const secrets = lodash_1.default.filter(this.config.compositeSecrets, (s) => s && s.length);
            if (!secrets.length) {
                return;
            }
            // let it throw
            const sm = new secret_manager_1.SecretManager({ region: this.config.secretRegion });
            for (const secret of secrets) {
                const partialCredential = yield sm.getSecret(secret);
                this.credentials = Object.assign(Object.assign({}, this.credentials), partialCredential);
            }
        });
    }
    getConnection(ctx, database) {
        return __awaiter(this, void 0, void 0, function* () {
            const cachePolicy = this.config.cachePolicy(database);
            const config = {
                models: this.config.modelsPolicy(database),
                schema: this.config.schemaPolicy(database),
                credentials: this.credentials,
                options: this.config.sequelizeOptions,
                databaseName: database,
            };
            config.options = Object.assign(Object.assign({}, config.options), this.config.customConfiguration(database));
            if (cachePolicy == 'never') {
                return new connection_1.Connection(config);
            }
            let conn = yield this.cache.getItem(ctx, database);
            if (conn) {
                return conn;
            }
            config.releaseFn = () => this.cache.release((0, context_1.newContext)(), database);
            conn = new connection_1.Connection(config);
            const policy = {};
            if (cachePolicy == 'persistent') {
                policy.isCachedForever = true;
            }
            else {
                policy.ttl = cachePolicy;
                policy.isLazy = false;
                policy.borrowable = true;
                policy.borrow = true;
                policy.deletePostHook = (c) => { c.close(); };
            }
            yield this.cache.setItem(ctx, database, conn, policy);
            return conn;
        });
    }
    borrowing(ctx, database, scope) {
        return __awaiter(this, void 0, void 0, function* () {
            const conn = yield this.getConnection(ctx, database);
            return yield scope(conn).finally(() => conn.release());
        });
    }
}
exports.ConnectionManager = ConnectionManager;
ConnectionManager.defaultConfig = () => {
    return {
        usernameEnv: "POSTGRES_LOCAL_USERNAME",
        passwordEnv: "POSTGRES_LOCAL_PASSWORD",
        hostEnv: "POSTGRES_LOCAL_HOST",
        portEnv: "POSTGRES_LOCAL_PORT",
        secretRegion: "eu-west-1",
        compositeSecrets: [],
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        schemaPolicy: (_) => (exports.DEFAULT_SCHEMA),
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        modelsPolicy: (_) => ([]),
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        cachePolicy: (_) => ("never"),
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        customConfiguration: (_) => ({}),
        sequelizeOptions: {
            dialectModule: pg_1.default,
            repositoryMode: true,
            logging: false,
        }
    };
};
//# sourceMappingURL=manager.js.map