Discord-InterChat/InterChat

View on GitHub
src/utils/CacheUtils.ts

Summary

Maintainability
A
0 mins
Test Coverage
// TODO: make this into a class
import { RedisKeys } from '#utils/Constants.js';
import getRedis from '#utils/Redis.js';
import Logger from '#utils/Logger.js';
import { Prisma } from '@prisma/client';
import { type Awaitable } from 'discord.js';
import type { ConvertDatesToString } from '#types/Utils.d.ts';

export const cacheData = async (key: string, value: string, expirySecs?: number) => {
  try {
    await getRedis().set(key, value, 'EX', expirySecs ?? 5 * 60);
  }
  catch (e) {
    Logger.error('Failed to set cache: ', e);
  }
};

export const parseKey = (key: string) => {
  const [id, model] = key.split(':');
  return { id, model } as { id: string; model: Prisma.ModelName };
};

export const invalidateCacheForModel = async (model: string) => {
  const redisClient = getRedis();
  const allCacheKeys = await redisClient.keys('*');
  allCacheKeys.forEach(async (key) => {
    if (parseKey(key).model === model) {
      await redisClient.del(`${model}:${key}`);
    }
  });
};

export const serializeCache = <K>(data: string | null): ConvertDatesToString<K> | null => {
  if (!data) return null;
  try {
    return JSON.parse(data);
  }
  catch (e) {
    Logger.error('Failed to parse cache data: ', e);
    return data as ConvertDatesToString<K>;
  }
};

const isCacheable = (data: unknown): boolean =>
  Array.isArray(data) ? data.length > 0 : data !== null && data !== undefined;

export const getCachedData = async <
  T extends { [key: string]: unknown } | { [key: string]: unknown }[],
>(
  key: `${RedisKeys}:${string}`,
  fetchFunction?: (() => Awaitable<T | null>) | null,
  expiry?: number,
) => {
  let data = serializeCache<T>(await getRedis().get(key));
  const fromCache = isCacheable(data);

  if (!fromCache && fetchFunction) {
    data = (await fetchFunction()) as ConvertDatesToString<T>;
    if (isCacheable(data)) await cacheData(key, JSON.stringify(data), expiry);
  }

  return { data, fromCache };
};