Skip to content

Commit 063ab40

Browse files
fhennigFelix Hennigclaude
authored
feat(dashboards): don't retry W-ASAP validation errors in fetchWasapPageData (#1362)
#1349 ### Summary Introduce WasapValidationError for config/state validation failures (mode not enabled, no collection selected, etc.) and tell React Query not to retry them, so the UI doesn't spin for ~7s on states that are immediately known to be invalid. ## PR Checklist - ~~All necessary documentation has been adapted.~~ - [x] The implemented feature is covered by an appropriate test. --------- Co-authored-by: Felix Hennig <felix-agent@felixhennig.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0672a6b commit 063ab40

2 files changed

Lines changed: 30 additions & 20 deletions

File tree

website/src/components/views/wasap/useWasapPageData.spec.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import dayjs from 'dayjs';
22
import { http } from 'msw';
33
import { beforeEach, describe, expect, test, vi } from 'vitest';
44

5-
import { fetchWasapPageData, getLapisFilterForTimeFrame } from './useWasapPageData.ts';
5+
import { fetchWasapPageData, getLapisFilterForTimeFrame, WasapValidationError } from './useWasapPageData.ts';
66
import {
77
EXCLUDE_SET_NAME,
88
SEQUENCE_TYPE,
@@ -79,7 +79,7 @@ describe('fetchWasapPageData', () => {
7979
{},
8080
{ mode: WASAP_ANALYSIS_MODE.manual, sequenceType: SEQUENCE_TYPE.nucleotide, mutations: [] },
8181
),
82-
).rejects.toThrow("Cannot fetch data, 'manual' mode is not enabled.");
82+
).rejects.toThrow(WasapValidationError);
8383
});
8484
});
8585

@@ -310,7 +310,7 @@ describe('fetchWasapPageData', () => {
310310
timeFrame: VARIANT_TIME_FRAME.all,
311311
},
312312
),
313-
).rejects.toThrow("Cannot fetch data, 'variant' mode is not enabled.");
313+
).rejects.toThrow(WasapValidationError);
314314
});
315315
});
316316

@@ -411,7 +411,7 @@ describe('fetchWasapPageData', () => {
411411
{},
412412
{ mode: WASAP_ANALYSIS_MODE.untracked, sequenceType: SEQUENCE_TYPE.nucleotide },
413413
),
414-
).rejects.toThrow("Cannot fetch data, 'untracked' mode is not enabled.");
414+
).rejects.toThrow(WasapValidationError);
415415
});
416416
});
417417

@@ -558,7 +558,7 @@ describe('fetchWasapPageData', () => {
558558
{},
559559
{ mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: 42 },
560560
),
561-
).rejects.toThrow("Cannot fetch data, 'covSpectrumCollection' mode is not enabled.");
561+
).rejects.toThrow(WasapValidationError);
562562
});
563563

564564
test('throws when no collection is selected', async () => {
@@ -568,7 +568,7 @@ describe('fetchWasapPageData', () => {
568568
{},
569569
{ mode: WASAP_ANALYSIS_MODE.covSpectrumCollection, collectionId: undefined },
570570
),
571-
).rejects.toThrow('No collection selected');
571+
).rejects.toThrow(WasapValidationError);
572572
});
573573
});
574574

@@ -938,13 +938,13 @@ describe('fetchWasapPageData', () => {
938938

939939
await expect(
940940
fetchWasapPageData(disabledConfig, {}, { mode: WASAP_ANALYSIS_MODE.collection, collectionId: 1 }),
941-
).rejects.toThrow("Cannot fetch data, 'collection' mode is not enabled.");
941+
).rejects.toThrow(WasapValidationError);
942942
});
943943

944944
test('throws when no collection is selected', async () => {
945945
await expect(
946946
fetchWasapPageData(config, {}, { mode: WASAP_ANALYSIS_MODE.collection, collectionId: undefined }),
947-
).rejects.toThrow('No collection selected');
947+
).rejects.toThrow(WasapValidationError);
948948
});
949949
});
950950
});

website/src/components/views/wasap/useWasapPageData.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ import { getLineageFields } from '../../../types/Collection';
2424
import type { FilterObject, Variant } from '../../../types/Collection';
2525
import { validateGenomeOnly } from '../../../util/siloExpressionUtils';
2626

27+
export class WasapValidationError extends Error {
28+
constructor(message: string) {
29+
super(message);
30+
this.name = 'WasapValidationError';
31+
}
32+
}
33+
2734
/**
2835
* Hook that fetches and returns `WasapPageData` for the W-ASAP page,
2936
* depending on the analysis mode and analysis mode settings.
@@ -38,6 +45,7 @@ export function useWasapPageData(
3845
return useQuery({
3946
queryKey: ['wasap', analysis, resistanceMutationsBySet],
4047
queryFn: () => fetchWasapPageData(config, resistanceMutationsBySet, analysis),
48+
retry: (failureCount, error) => !(error instanceof WasapValidationError) && failureCount < 3,
4149
});
4250
}
4351

@@ -64,7 +72,7 @@ export async function fetchWasapPageData(
6472

6573
function fetchManualModeData(config: WasapPageConfig, analysis: WasapManualFilter): WasapMutationsData {
6674
if (!config.manualAnalysisModeEnabled) {
67-
throw Error("Cannot fetch data, 'manual' mode is not enabled.");
75+
throw new WasapValidationError("Cannot fetch data, 'manual' mode is not enabled.");
6876
}
6977
return {
7078
type: 'mutations',
@@ -77,7 +85,7 @@ async function fetchVariantModeData(
7785
analysis: WasapVariantFilter,
7886
): Promise<WasapMutationsData> {
7987
if (!config.variantAnalysisModeEnabled) {
80-
throw Error("Cannot fetch data, 'variant' mode is not enabled.");
88+
throw new WasapValidationError("Cannot fetch data, 'variant' mode is not enabled.");
8189
}
8290
switch (analysis.signatureType) {
8391
case 'computed':
@@ -92,7 +100,7 @@ async function fetchVariantComputedModeData(
92100
analysis: WasapVariantFilter,
93101
): Promise<WasapMutationsData> {
94102
if (!config.variantAnalysisModeEnabled) {
95-
throw Error("Cannot fetch data, 'variant' mode is not enabled.");
103+
throw new WasapValidationError("Cannot fetch data, 'variant' mode is not enabled.");
96104
}
97105
const mutationsWithScore = await getMutationsForVariant(
98106
config.clinicalLapis.lapisBaseUrl,
@@ -124,10 +132,10 @@ async function fetchVariantPredefinedModeData(
124132
analysis: WasapVariantFilter,
125133
): Promise<WasapMutationsData> {
126134
if (!config.variantAnalysisModeEnabled) {
127-
throw Error("Cannot fetch data, 'variant' mode is not enabled.");
135+
throw new WasapValidationError("Cannot fetch data, 'variant' mode is not enabled.");
128136
}
129137
if (analysis.collectionId === undefined) {
130-
throw new Error('No collection selected for predefined variant mode.');
138+
throw new WasapValidationError('No collection selected for predefined variant mode.');
131139
}
132140
const collection = await getBackendServiceForClientside().getCollection({ id: String(analysis.collectionId) });
133141

@@ -141,10 +149,12 @@ async function fetchVariantPredefinedModeData(
141149

142150
const variant = collection.variants.find((v) => v.name === variantName);
143151
if (!variant) {
144-
throw new Error(`Variant "${variantName}" not found in collection ${collection.id}.`);
152+
throw new WasapValidationError(`Variant "${variantName}" not found in collection ${collection.id}.`);
145153
}
146154
if (variant.type !== 'filterObject') {
147-
throw new Error(`Variant "${variantName}" in collection ${collection.id} is not a filterObject variant.`);
155+
throw new WasapValidationError(
156+
`Variant "${variantName}" in collection ${collection.id} is not a filterObject variant.`,
157+
);
148158
}
149159

150160
const mutations =
@@ -199,7 +209,7 @@ async function fetchUntrackedModeData(
199209
analysis: WasapUntrackedFilter,
200210
): Promise<WasapMutationsData> {
201211
if (!config.untrackedAnalysisModeEnabled) {
202-
throw Error("Cannot fetch data, 'untracked' mode is not enabled.");
212+
throw new WasapValidationError("Cannot fetch data, 'untracked' mode is not enabled.");
203213
}
204214
const variantsToExclude =
205215
analysis.excludeSet === 'custom'
@@ -240,10 +250,10 @@ async function fetchCovSpectrumCollectionModeData(
240250
analysis: WasapCovSpectrumCollectionFilter,
241251
): Promise<WasapCollectionData> {
242252
if (!config.covSpectrumCollectionAnalysisModeEnabled) {
243-
throw Error("Cannot fetch data, 'covSpectrumCollection' mode is not enabled.");
253+
throw new WasapValidationError("Cannot fetch data, 'covSpectrumCollection' mode is not enabled.");
244254
}
245255
if (analysis.collectionId === undefined) {
246-
throw Error('No collection selected');
256+
throw new WasapValidationError('No collection selected');
247257
}
248258
const collection = await getCollection(config.collectionsApiBaseUrl, analysis.collectionId);
249259

@@ -270,10 +280,10 @@ async function fetchCollectionModeData(
270280
analysis: WasapCollectionFilter,
271281
): Promise<WasapCollectionData> {
272282
if (!config.collectionAnalysisModeEnabled) {
273-
throw Error("Cannot fetch data, 'collection' mode is not enabled.");
283+
throw new WasapValidationError("Cannot fetch data, 'collection' mode is not enabled.");
274284
}
275285
if (analysis.collectionId === undefined) {
276-
throw Error('No collection selected');
286+
throw new WasapValidationError('No collection selected');
277287
}
278288
const collection = await getBackendServiceForClientside().getCollection({ id: String(analysis.collectionId) });
279289

0 commit comments

Comments
 (0)