diff --git a/src/libs/fetchFromMultipleSources.spec.ts b/src/libs/fetchFromMultipleSources.spec.ts index e217dbf..9cc6731 100644 --- a/src/libs/fetchFromMultipleSources.spec.ts +++ b/src/libs/fetchFromMultipleSources.spec.ts @@ -9,35 +9,56 @@ describe("fetchFromMultipleSources", () => { ); }); - it("has promises, returns first one with value", async () => { - const actual = await fetchFromMultipleSources([ + it("has promises with valid responses, returns first one with value", async () => { + const { result } = await fetchFromMultipleSources([ () => Promise.resolve(undefined), () => Promise.resolve(null), () => Promise.reject(), () => Promise.resolve(2), () => Promise.resolve(3), ]); - const expected = 2; - expect(actual).toEqual(expected); + expect(result).toEqual(2); }); - it("has promises, no valid responses, returns null", async () => { - const actual = await fetchFromMultipleSources([ + it("has promises with valid responses, contains no errors", async () => { + const { errors } = await fetchFromMultipleSources([ + () => Promise.resolve(undefined), () => Promise.resolve(null), () => Promise.reject(), - () => Promise.resolve(undefined), + () => Promise.resolve(2), + () => Promise.resolve(3), ]); - const expected = null; - expect(actual).toBe(expected); + expect(errors).toBeNull(); }); - it("has promises, has cached value, returns cached value", async () => { - const actual = await fetchFromMultipleSources( + it("has promises with no valid responses, returns null", async () => { + const { result } = await fetchFromMultipleSources([ + () => Promise.resolve(null), + () => Promise.reject("error"), + () => Promise.resolve(undefined), + ]); + + expect(result).toBeNull(); + }); + + it("has promises with no valid responses, contains error", async () => { + const { errors } = await fetchFromMultipleSources([ + () => Promise.resolve(null), + () => Promise.reject("error"), + () => Promise.reject(new Error("error")), + () => Promise.resolve(undefined), + ]); + + expect(errors).toEqual(["error", new Error("error")]); + }); + + it("has promises but cache has value, returns cached value", async () => { + const { result } = await fetchFromMultipleSources( [ () => Promise.resolve(null), - () => Promise.reject(), + () => Promise.reject("error"), () => Promise.resolve(undefined), ], { @@ -45,9 +66,24 @@ describe("fetchFromMultipleSources", () => { saveInCache: async () => {}, }, ); - const expected = -1; - expect(actual).toBe(expected); + expect(result).toEqual(-1); + }); + + it("has promises but cache has value, contains no errors", async () => { + const { errors } = await fetchFromMultipleSources( + [ + () => Promise.resolve(null), + () => Promise.reject("error"), + () => Promise.resolve(undefined), + ], + { + fetchFromCache: () => Promise.resolve(-1), + saveInCache: async () => {}, + }, + ); + + expect(errors).toBeNull(); }); it("has promises, no cached value, no valid response, should not save in cache", async () => { @@ -65,9 +101,8 @@ describe("fetchFromMultipleSources", () => { }, }, ); - const expected = undefined; - expect(actual).toBe(expected); + expect(actual).toBeUndefined(); }); it("has promises, no cached value, has valid response, should save in cache", async () => { @@ -89,4 +124,20 @@ describe("fetchFromMultipleSources", () => { expect(actual).toBe(expected); }); + + it("has promises, no cached value, has valid response, value that was cached is returned", async () => { + const { result } = await fetchFromMultipleSources( + [ + () => Promise.resolve(null), + () => Promise.reject(), + () => Promise.resolve(3), + ], + { + fetchFromCache: () => Promise.resolve(null), + saveInCache: async (value) => {}, + }, + ); + + expect(result).toBe(3); + }); }); diff --git a/src/libs/fetchFromMultipleSources.ts b/src/libs/fetchFromMultipleSources.ts index e875a78..f7f7622 100644 --- a/src/libs/fetchFromMultipleSources.ts +++ b/src/libs/fetchFromMultipleSources.ts @@ -8,29 +8,43 @@ type OptionalArgs = fetchFromCache?: undefined; }; +interface FetchFromMultipleSourcesResult { + result: T | null; + errors: (Error | string | undefined)[] | null; +} + export async function fetchFromMultipleSources( fetchPromises: (() => Promise)[], args?: OptionalArgs, -) { +): Promise> { let result = await args?.fetchFromCache?.(); if (result) { - return result; + return { result, errors: null }; } if (fetchPromises.length === 0) { throw new Error("fetchPromises cannot be empty"); } - for (const promise of fetchPromises) { + let errors: Record = {}; + for (let i = 0; i < fetchPromises.length; i++) { + const promise = fetchPromises[i]; try { result = await promise(); if (result) break; - } catch (e) {} + } catch (e) { + errors[i] = e as Error; + } } if (args?.saveInCache && result) { await args.saveInCache(result); } - return result ?? null; + result = result ?? null; + return { + result, + errors: + !result && Object.keys(errors).length > 0 ? Object.values(errors) : null, + }; }