fix: fetchFromMultipleSources returns errorOccurred only if all sources fail

This commit is contained in:
2024-06-07 23:42:41 -04:00
parent c35c9b9e09
commit 1ccd004c77
5 changed files with 21 additions and 25 deletions

View File

@@ -49,7 +49,7 @@ const app = new OpenAPIHono<Env>();
app.openapi(route, async (c) => { app.openapi(route, async (c) => {
const aniListId = Number(c.req.param("aniListId")); const aniListId = Number(c.req.param("aniListId"));
const { result: episodes, errors } = await fetchFromMultipleSources([ const { result: episodes, errorOccurred } = await fetchFromMultipleSources([
() => { () => {
const isAnifyEnabled = readEnvVariable<boolean>(c.env, "ENABLE_ANIFY"); const isAnifyEnabled = readEnvVariable<boolean>(c.env, "ENABLE_ANIFY");
return getEpisodesFromAnify(isAnifyEnabled, aniListId); return getEpisodesFromAnify(isAnifyEnabled, aniListId);
@@ -64,7 +64,7 @@ app.openapi(route, async (c) => {
), ),
]); ]);
if (errors?.length > 0) { if (errorOccurred) {
return c.json(ErrorResponse, { status: 500 }); return c.json(ErrorResponse, { status: 500 });
} }

View File

@@ -39,14 +39,14 @@ app.openapi(route, async (c) => {
const page = Number(c.req.query("page") ?? 1); const page = Number(c.req.query("page") ?? 1);
const limit = Number(c.req.query("limit") ?? 10); const limit = Number(c.req.query("limit") ?? 10);
const { result: response, errors } = await fetchFromMultipleSources([ const { result: response, errorOccurred } = await fetchFromMultipleSources([
() => fetchSearchResultsFromAnilist(query, page, limit), () => fetchSearchResultsFromAnilist(query, page, limit),
() => fetchSearchResultsFromAmvstrm(query, page, limit), () => fetchSearchResultsFromAmvstrm(query, page, limit),
]); ]);
if (!response) { if (!response) {
return c.json({ return c.json({
success: (errors ?? []).length === 0, success: !errorOccurred,
results: [], results: [],
hasNextPage: false, hasNextPage: false,
}); });

View File

@@ -48,12 +48,12 @@ app.openapi(route, async (c) => {
const aniListId = Number(c.req.query("id")); const aniListId = Number(c.req.query("id"));
const aniListToken = c.req.header("X-AniList-Token"); const aniListToken = c.req.header("X-AniList-Token");
const { result: title, errors } = await fetchFromMultipleSources([ const { result: title, errorOccurred } = await fetchFromMultipleSources([
() => fetchTitleFromAnilist(aniListId, aniListToken ?? undefined), () => fetchTitleFromAnilist(aniListId, aniListToken ?? undefined),
() => fetchTitleFromAmvstrm(aniListId), () => fetchTitleFromAmvstrm(aniListId),
]); ]);
if (errors?.length > 0) { if (errorOccurred) {
return c.json(ErrorResponse, { status: 500 }); return c.json(ErrorResponse, { status: 500 });
} }

View File

@@ -21,8 +21,8 @@ describe("fetchFromMultipleSources", () => {
expect(result).toEqual(2); expect(result).toEqual(2);
}); });
it("has promises with valid responses, contains no errors", async () => { it("has promises with valid responses, no error occurred", async () => {
const { errors } = await fetchFromMultipleSources<number>([ const { errorOccurred } = await fetchFromMultipleSources<number>([
() => Promise.resolve(undefined), () => Promise.resolve(undefined),
() => Promise.resolve(null), () => Promise.resolve(null),
() => Promise.reject(), () => Promise.reject(),
@@ -30,28 +30,25 @@ describe("fetchFromMultipleSources", () => {
() => Promise.resolve(3), () => Promise.resolve(3),
]); ]);
expect(errors).toBeNull(); expect(errorOccurred).toBeFalse();
}); });
it("has promises with no valid responses, returns null", async () => { it("has promises that all throw, returns null", async () => {
const { result } = await fetchFromMultipleSources<number>([ const { result } = await fetchFromMultipleSources<number>([
() => Promise.resolve(null),
() => Promise.reject("error"), () => Promise.reject("error"),
() => Promise.resolve(undefined), () => Promise.reject(new Error("error")),
]); ]);
expect(result).toBeNull(); expect(result).toBeNull();
}); });
it("has promises with no valid responses, contains error", async () => { it("has promises that all throw, contains error", async () => {
const { errors } = await fetchFromMultipleSources<number>([ const { errorOccurred } = await fetchFromMultipleSources<number>([
() => Promise.resolve(null),
() => Promise.reject("error"), () => Promise.reject("error"),
() => Promise.reject(new Error("error")), () => Promise.reject(new Error("error")),
() => Promise.resolve(undefined),
]); ]);
expect(errors).toEqual(["error", new Error("error")]); expect(errorOccurred).toBeTrue();
}); });
it("has promises but cache has value, returns cached value", async () => { it("has promises but cache has value, returns cached value", async () => {
@@ -71,7 +68,7 @@ describe("fetchFromMultipleSources", () => {
}); });
it("has promises but cache has value, contains no errors", async () => { it("has promises but cache has value, contains no errors", async () => {
const { errors } = await fetchFromMultipleSources<number>( const { errorOccurred } = await fetchFromMultipleSources<number>(
[ [
() => Promise.resolve(null), () => Promise.resolve(null),
() => Promise.reject("error"), () => Promise.reject("error"),
@@ -83,7 +80,7 @@ describe("fetchFromMultipleSources", () => {
}, },
); );
expect(errors).toBeNull(); expect(errorOccurred).toBeFalse();
}); });
it("has promises, no cached value, no valid response, should not save in cache", async () => { it("has promises, no cached value, no valid response, should not save in cache", async () => {

View File

@@ -10,7 +10,7 @@ type OptionalArgs<T> =
interface FetchFromMultipleSourcesResult<T> { interface FetchFromMultipleSourcesResult<T> {
result: T | null; result: T | null;
errors: (Error | string | undefined)[] | null; errorOccurred: boolean;
} }
export async function fetchFromMultipleSources<T>( export async function fetchFromMultipleSources<T>(
@@ -19,21 +19,21 @@ export async function fetchFromMultipleSources<T>(
): Promise<FetchFromMultipleSourcesResult<T>> { ): Promise<FetchFromMultipleSourcesResult<T>> {
let result = await args?.fetchFromCache?.(); let result = await args?.fetchFromCache?.();
if (result) { if (result) {
return { result, errors: null }; return { result, errorOccurred: false };
} }
if (fetchPromises.length === 0) { if (fetchPromises.length === 0) {
throw new Error("fetchPromises cannot be empty"); throw new Error("fetchPromises cannot be empty");
} }
let errors: Record<number, Error> = {}; let errorCount = 0;
for (let i = 0; i < fetchPromises.length; i++) { for (let i = 0; i < fetchPromises.length; i++) {
const promise = fetchPromises[i]; const promise = fetchPromises[i];
try { try {
result = await promise(); result = await promise();
if (result) break; if (result) break;
} catch (e) { } catch (e) {
errors[i] = e as Error; errorCount++;
} }
} }
@@ -44,7 +44,6 @@ export async function fetchFromMultipleSources<T>(
result = result ?? null; result = result ?? null;
return { return {
result, result,
errors: errorOccurred: errorCount === fetchPromises.length,
!result && Object.keys(errors).length > 0 ? Object.values(errors) : null,
}; };
} }