"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisQueryResultCache = void 0;
const PlatformTools_1 = require("../platform/PlatformTools");
const TypeORMError_1 = require("../error/TypeORMError");
/**
 * Caches query result into Redis database.
 */
class RedisQueryResultCache {
    // -------------------------------------------------------------------------
    // Constructor
    // -------------------------------------------------------------------------
    constructor(dataSource, clientType) {
        this.dataSource = dataSource;
        this.clientType = clientType;
        this.redis = this.loadRedis();
    }
    // -------------------------------------------------------------------------
    // Public Methods
    // -------------------------------------------------------------------------
    /**
     * Creates a connection with given cache provider.
     */
    async connect() {
        const cacheOptions = this.dataSource.options.cache;
        if (this.clientType === "redis") {
            const clientOptions = {
                ...cacheOptions?.options,
            };
            this.client = this.redis.createClient(clientOptions);
            if (typeof this.dataSource.options.cache === "object" &&
                this.dataSource.options.cache.ignoreErrors) {
                this.client.on("error", (err) => {
                    this.dataSource.logger.log("warn", err);
                });
            }
            await this.client.connect();
        }
        else if (this.clientType === "ioredis") {
            if (cacheOptions && cacheOptions.port) {
                if (cacheOptions.options) {
                    this.client = new this.redis(cacheOptions.port, cacheOptions.options);
                }
                else {
                    this.client = new this.redis(cacheOptions.port);
                }
            }
            else if (cacheOptions && cacheOptions.options) {
                this.client = new this.redis(cacheOptions.options);
            }
            else {
                this.client = new this.redis();
            }
        }
        else if (this.clientType === "ioredis/cluster") {
            if (cacheOptions &&
                cacheOptions.options &&
                Array.isArray(cacheOptions.options)) {
                this.client = new this.redis.Cluster(cacheOptions.options);
            }
            else if (cacheOptions &&
                cacheOptions.options &&
                cacheOptions.options.startupNodes) {
                this.client = new this.redis.Cluster(cacheOptions.options.startupNodes, cacheOptions.options.options);
            }
            else {
                throw new TypeORMError_1.TypeORMError(`options.startupNodes required for ${this.clientType}.`);
            }
        }
    }
    /**
     * Disconnects the connection
     */
    async disconnect() {
        const client = this.client;
        this.client = undefined;
        await client.quit();
    }
    /**
     * Creates table for storing cache if it does not exist yet.
     * @param queryRunner
     */
    async synchronize(queryRunner) { }
    /**
     * Get data from cache.
     * Returns cache result if found.
     * Returns undefined if result is not cached.
     * @param options
     * @param queryRunner
     */
    async getFromCache(options, queryRunner) {
        const key = options.identifier || options.query;
        if (!key)
            return undefined;
        const result = await this.client.get(key);
        return result ? JSON.parse(result) : undefined;
    }
    /**
     * Checks if cache is expired or not.
     * @param savedCache
     */
    isExpired(savedCache) {
        return savedCache.time + savedCache.duration < Date.now();
    }
    /**
     * Stores given query result in the cache.
     * @param options
     * @param savedCache
     * @param queryRunner
     */
    async storeInCache(options, savedCache, queryRunner) {
        const key = options.identifier || options.query;
        if (!key)
            return;
        const value = JSON.stringify(options);
        const duration = options.duration;
        if (this.isNodeRedisClient()) {
            await this.client.set(key, value, {
                expiration: {
                    type: "PX",
                    value: duration,
                },
            });
        }
        else {
            await this.client.set(key, value, "PX", duration);
        }
    }
    /**
     * Clears everything stored in the cache.
     * @param queryRunner
     */
    async clear(queryRunner) {
        if (this.isNodeRedisClient()) {
            await this.client.flushDb();
        }
        else {
            await this.client.flushdb();
        }
    }
    /**
     * Removes all cached results by given identifiers from cache.
     * @param identifiers
     * @param queryRunner
     */
    async remove(identifiers, queryRunner) {
        await this.client.del(identifiers);
    }
    // -------------------------------------------------------------------------
    // Protected Methods
    // -------------------------------------------------------------------------
    /**
     * Removes a single key from redis database.
     * @param key
     */
    async deleteKey(key) {
        await this.client.del([key]);
    }
    /**
     * Loads redis dependency.
     */
    loadRedis() {
        try {
            if (this.clientType === "ioredis/cluster") {
                return PlatformTools_1.PlatformTools.load("ioredis");
            }
            else {
                return PlatformTools_1.PlatformTools.load(this.clientType);
            }
        }
        catch {
            throw new TypeORMError_1.TypeORMError(`Cannot use cache because ${this.clientType} is not installed. Please run "npm i ${this.clientType}".`);
        }
    }
    isNodeRedisClient() {
        return this.clientType === "redis";
    }
}
exports.RedisQueryResultCache = RedisQueryResultCache;
//# sourceMappingURL=RedisQueryResultCache.js.map