All files / src/cache cache-manager.ts

100% Statements 22/22
100% Branches 5/5
100% Functions 6/6
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 702x         2x       38x 38x     38x                 30x   30x 14x     16x 16x   16x 6x   6x     10x                 25x   25x             4x 8x       25x 25x   25x            
import { CacheEntry, CacheKey, DEFAULT_CACHE_LIFE, DEFAULT_NOW_PROVIDER, ICache, WrappedCacheEntry } from './shared'
 
/**
 * Wraps a cache implementation and adds expiration logic
 * */
export class CacheManager {
  readonly nowProvider: () => number | Promise<number>
 
  constructor(
    private cache: ICache,
    private cacheTime: number = DEFAULT_CACHE_LIFE,
    nowProvider?: () => number | Promise<number>
  ) {
    this.nowProvider = nowProvider || DEFAULT_NOW_PROVIDER
  }
 
  /**
   * It gets a cache entry from the cache, and if it's expired, it removes it from the cache
   * @param cacheKey - CacheKey<TExtended>
   * @returns A promise that resolves to a cache entry or undefined.
   */
  async get<TExtended extends boolean>(cacheKey: CacheKey<TExtended>): Promise<CacheEntry<TExtended> | undefined> {
    const wrappedEntry = await this.cache.get<WrappedCacheEntry<TExtended>>(cacheKey.toKey())
 
    if (!wrappedEntry) {
      return
    }
 
    const now = await this.nowProvider()
    const nowSeconds = Math.floor(now / 1000)
 
    if (wrappedEntry.expiresAt < nowSeconds) {
      await this.cache.remove(cacheKey.toKey())
 
      return
    }
 
    return wrappedEntry.body
  }
 
  /**
   * It takes a cache key and a cache entry, wraps the cache entry, and then sets the wrapped cache entry in the cache
   * @param cacheKey - CacheKey<TExtended>
   * @param {CacheEntry} entry - The cache entry to be stored.
   */
  async set<TExtended extends boolean>(cacheKey: CacheKey<TExtended>, entry: CacheEntry): Promise<void> {
    const wrappedEntry = await this.wrapCacheEntry(entry)
 
    await this.cache.set(cacheKey.toKey(), wrappedEntry)
  }
 
  /**
   * It gets all the keys in the cache, and then removes them all
   */
  async clearCache() {
    const keys = await this.cache.allKeys()
    await Promise.all(keys.map((key) => this.cache.remove(key)))
  }
 
  private async wrapCacheEntry(entry: CacheEntry): Promise<WrappedCacheEntry> {
    const now = await this.nowProvider()
    const expiresInTime = Math.floor(now / 1000) + this.cacheTime
 
    return {
      body: entry,
      expiresAt: expiresInTime,
    }
  }
}