From 1e1729ae30a88c768d173a69046af603dd0607a3 Mon Sep 17 00:00:00 2001 From: Rushil Perera Date: Tue, 14 May 2024 22:44:04 -0400 Subject: [PATCH] feat: create function to handle fetching data from multiple sources This helps since sometimes the data sources have issues occasionally --- src/libs/fetchFromMultipleSources.spec.ts | 92 +++++++++++++++++++++++ src/libs/fetchFromMultipleSources.ts | 36 +++++++++ 2 files changed, 128 insertions(+) create mode 100644 src/libs/fetchFromMultipleSources.spec.ts create mode 100644 src/libs/fetchFromMultipleSources.ts diff --git a/src/libs/fetchFromMultipleSources.spec.ts b/src/libs/fetchFromMultipleSources.spec.ts new file mode 100644 index 0000000..e217dbf --- /dev/null +++ b/src/libs/fetchFromMultipleSources.spec.ts @@ -0,0 +1,92 @@ +import { describe, expect, it } from "bun:test"; + +import { fetchFromMultipleSources } from "./fetchFromMultipleSources"; + +describe("fetchFromMultipleSources", () => { + it("no promises, throws exception", () => { + expect(() => fetchFromMultipleSources([])).toThrow( + "fetchPromises cannot be empty", + ); + }); + + it("has promises, returns first one with value", async () => { + const actual = await fetchFromMultipleSources([ + () => Promise.resolve(undefined), + () => Promise.resolve(null), + () => Promise.reject(), + () => Promise.resolve(2), + () => Promise.resolve(3), + ]); + const expected = 2; + + expect(actual).toEqual(expected); + }); + + it("has promises, no valid responses, returns null", async () => { + const actual = await fetchFromMultipleSources([ + () => Promise.resolve(null), + () => Promise.reject(), + () => Promise.resolve(undefined), + ]); + const expected = null; + + expect(actual).toBe(expected); + }); + + it("has promises, has cached value, returns cached value", async () => { + const actual = await fetchFromMultipleSources( + [ + () => Promise.resolve(null), + () => Promise.reject(), + () => Promise.resolve(undefined), + ], + { + fetchFromCache: () => Promise.resolve(-1), + saveInCache: async () => {}, + }, + ); + const expected = -1; + + expect(actual).toBe(expected); + }); + + it("has promises, no cached value, no valid response, should not save in cache", async () => { + let actual; + await fetchFromMultipleSources( + [ + () => Promise.resolve(null), + () => Promise.reject(), + () => Promise.resolve(undefined), + ], + { + fetchFromCache: () => Promise.resolve(null), + saveInCache: async (value) => { + actual = value; + }, + }, + ); + const expected = undefined; + + expect(actual).toBe(expected); + }); + + it("has promises, no cached value, has valid response, should save in cache", async () => { + let actual: number | undefined; + await fetchFromMultipleSources( + [ + () => Promise.resolve(null), + () => Promise.reject(), + () => Promise.resolve(3), + ], + { + fetchFromCache: () => Promise.resolve(null), + saveInCache: async (value) => { + actual = value; + }, + }, + ); + const expected = 3; + + expect(actual).toBe(expected); + }); +}); diff --git a/src/libs/fetchFromMultipleSources.ts b/src/libs/fetchFromMultipleSources.ts new file mode 100644 index 0000000..e875a78 --- /dev/null +++ b/src/libs/fetchFromMultipleSources.ts @@ -0,0 +1,36 @@ +type OptionalArgs = + | { + saveInCache: (result: NonNullable) => Promise; + fetchFromCache: () => Promise; + } + | { + saveInCache?: undefined; + fetchFromCache?: undefined; + }; + +export async function fetchFromMultipleSources( + fetchPromises: (() => Promise)[], + args?: OptionalArgs, +) { + let result = await args?.fetchFromCache?.(); + if (result) { + return result; + } + + if (fetchPromises.length === 0) { + throw new Error("fetchPromises cannot be empty"); + } + + for (const promise of fetchPromises) { + try { + result = await promise(); + if (result) break; + } catch (e) {} + } + + if (args?.saveInCache && result) { + await args.saveInCache(result); + } + + return result ?? null; +}