All files / src/utils CachedFetch.ts

82.35% Statements 42/51
80.64% Branches 25/31
88.88% Functions 8/9
82% Lines 41/50

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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95          66x 847x 847x 847x     847x 847x     847x 847x 77x   770x 770x   675x           95x     95x     85x 85x 85x         76x 76x 76x 76x   76x             85x         66x 847x 847x 847x 847x         77x 77x 77x 77x                 66x 171x 171x 171x     66x 923x     66x 941x   941x    
import {
  GetRequestBundleWaitingSeconds, GetRequestCacheExpirySeconds, PendingRequestsCacheKey
} from '@config/base';
 
// eslint-disable-next-line  @typescript-eslint/no-explicit-any
export const cachedFetch = async (url: string, options: any): Promise<any> => {
  let expiry = GetRequestCacheExpirySeconds;
  const cacheKey = url;
  Iif (typeof options === 'number') {
    expiry = options;
    options = undefined;
  } else Eif (typeof options === 'object') {
    expiry = options.seconds || expiry;
  }
 
  const resp = getCachedResponse(cacheKey, expiry);
  if (resp != null) {
    return await resp;
  }
  const pendingRequests: Set<string> = getPendingRequests();
  if (pendingRequests.has(url)) {
    //If currently there is a same request on going, just wait for a while
    await new Promise(resolve => setTimeout(resolve, GetRequestBundleWaitingSeconds * 1000));
    const resp = getCachedResponse(cacheKey, expiry);
    if (resp != null) {
      return await resp;
    }
  } else {
    updatePendingRequestList(url, "add");
  }
 
  return fetch(url, options).then(response => {
    // let's only store in cache if the content-type is JSON or something
    // non-binary
    Eif (response.status === 200) {
      const ct = response.headers.get('Content-Type');
      if (ct && (ct.match(/application\/json/i) || ct.match(/text\//i))) {
        // There is a .json() instead of .text() but we're going to store it in
        // sessionStorage as string anyway. If we don't clone the response, it
        // will be consumed by the time it's returned. This way we're being
        // un-intrusive.
        response.clone().text().then(content => {
          localStorage.setItem(cacheKey, content);
          localStorage.setItem(getTsCacheKey(cacheKey), `${Date.now()}`);
          updatePendingRequestList(url, "delete");
          // Auto delete the cache after GetRequestCacheExpirySeconds + 1 seconds
          setTimeout(() => {
            localStorage.removeItem(cacheKey);
            localStorage.removeItem(getTsCacheKey(cacheKey));
          }, (GetRequestCacheExpirySeconds + 3) * 1000);
        });
      }
    }
    return response;
  });
};
 
// eslint-disable-next-line  @typescript-eslint/no-explicit-any
const getCachedResponse = (cacheKey: string, expiry: number): Promise<any> | undefined => {
  const cached = localStorage.getItem(cacheKey);
  const tsCacheKey = getTsCacheKey(cacheKey);
  const whenCached = parseInt(localStorage.getItem(tsCacheKey) ?? "0");
  if (cached !== null && whenCached !== null) {
    // it was in sessionStorage! Yay!
    // Even though 'whenCached' is a string, this operation
    // works because the minus sign tries to convert the
    // string to an integer and it will work.
    const age = (Date.now() - whenCached) / 1000;
    if (age < expiry) {
      const response = new Response(new Blob([cached]));
      return Promise.resolve(response);
    } else E{
      // We need to clean up this old key
      localStorage.removeItem(cacheKey);
      localStorage.removeItem(tsCacheKey);
    }
  }
};
 
const updatePendingRequestList = (url: string, operation: "delete" | "add"): void => {
  const newPendingRequests = getPendingRequests();
  (operation === "delete") ? newPendingRequests.delete(url) : newPendingRequests.add(url);
  localStorage.setItem(PendingRequestsCacheKey, JSON.stringify(Array.from(newPendingRequests)));
};
 
const getTsCacheKey = (cacheKey: string): string => {
  return cacheKey + ":ts";
};
 
const getPendingRequests = (): Set<string> => {
  const pendingRequests: Set<string> = new Set(
    JSON.parse(localStorage.getItem(PendingRequestsCacheKey) ?? '[]'));
  return pendingRequests;
};