lisaogren / axios-cache-adapter

Caching adapter for axios. Store request results in a configurable store to prevent unneeded network requests.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Code in documentation fails: `Use redis as cache store`

OiYouYeahYou opened this issue · comments

The code example Use redis as cache store when configured correctly

  • Node: 16.6.1
  • axios-cache-adapter: 2.7.3
  • redis: 6
  • Ubuntu: 20.04

The error stems from this code:
https://github.com/RasCarlito/axios-cache-adapter/blob/2d51cee4070ff88f2272533f9593fd41a392f52c/src/redis.js#L7-L17

Could this assertion be removed to allow more flexibility and reduce the chance future bugs based on minor version bump changes of variable names?

So digging into the issue more, and the constructor name is Commander instead of RedisClient

It looks as though node-redis had a major version bump recently, causing this issue. A couple of options:

  1. Use node-redis v3
  2. Write your own version of RedisStore, using a redis client of your choosing (ioredis is another option)

@timminss, Is this really a solution though? Would a better solution to be to remove the assertion or to update the documentation?

It's adapter for radis 4.0.4
You can replace import { RedisDefaultStore } from 'axios-cache-adapter' with import RedisDefaultStore from './RedisDefaultStore';.

RedisDefaultStore.ts

import { RedisClientType } from 'redis';
import { RedisDefaultOptions } from 'axios-cache-adapter';

interface IEntry {
  expires: number;
  data: string;
}

class RedisDefaultStore {
  private client: RedisClientType;
  private prefix: string | any;
  private maxScanCount: number;
  private getAsync: any;
  private psetexAsync: any;
  private delAsync: any;
  private scanAsync: any;

  constructor(
    client: RedisClientType | any,
    options: RedisDefaultOptions = {},
  ) {
    client.connect();
    this.client = client;
    this.prefix = options.prefix || 'axios-cache';
    this.maxScanCount = options.maxScanCount || 1000;
    this.getAsync = client.get.bind(client);
    this.psetexAsync = client.set.bind(client);
    this.delAsync = client.del.bind(client);
    this.scanAsync = client.scan.bind(client);
  }

  calculateTTL(value: IEntry) {
    const now = Date.now();

    if (value.expires && value.expires > now) {
      return value.expires - now;
    }

    return -1;
  }

  transformKey(key: string) {
    return this.prefix + '_' + key;
  }

  async getItem(key: string) {
    const item = (await this.getAsync(this.transformKey(key))) || null;

    return JSON.parse(item);
  }

  async setItem(key: string, value: IEntry) {
    const computedKey = this.transformKey(key);

    const ttl = this.calculateTTL(value);

    if (ttl > 0) {
      await this.psetexAsync(computedKey, JSON.stringify(value), { EX: ttl });
    }

    return value;
  }

  async removeItem(key: string) {
    await this.delAsync(this.transformKey(key));
  }

  async scan(operation) {
    let cursor = '0';

    do {
      const reply = await this.scanAsync(
        cursor,
        'MATCH',
        this.transformKey('*'),
        'COUNT',
        this.maxScanCount,
      );

      cursor = reply[0];

      await operation(reply[1]);
    } while (cursor !== '0');
  }

  async clear() {
    await this.scan((keys) => this.delAsync(keys));
  }

  async length() {
    let length = 0;

    await this.scan((keys) => {
      length += keys.length;
    });

    return length;
  }

  async iterate(fn) {
    async function runFunction(key) {
      const item = (await this.getAsync(key)) || null;

      const value = JSON.parse(item);

      return await fn(value, key);
    }

    await this.scan((keys) => Promise.all(keys.map(runFunction.bind(this))));

    return Promise.resolve([]);
  }
}

export default RedisDefaultStore;