Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match count bugfix and process state backoff polling helper #197

Merged
merged 5 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/api/collectionsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const collectionsService = httpService({
url: 'services/collections',
});

type ProcessState = 'processing' | 'failed' | 'complete';
export type ProcessState = 'processing' | 'failed' | 'complete';
type UPA = string;
// Collections-specific item ID strings (not an UPA or Data Object)
type KBaseId = string;
Expand Down
14 changes: 4 additions & 10 deletions src/features/collections/MatchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import { listObjects } from '../../common/api/workspaceApi';
import { getNarratives } from '../../common/api/searchApi';
import { parseError } from '../../common/api/utils/parseError';
import { useUpdateAppParams } from '../params/hooks';
import { useAppDispatch, useBackoffPolling } from '../../common/hooks';
import { useAppDispatch } from '../../common/hooks';
import {
setLocalSelection,
useCurrentSelection,
useMatchId,
} from './collectionsSlice';
import { store } from '../../app/store';
import { useParamsForNarrativeDropdown } from './hooks';
import { useParamsForNarrativeDropdown, useProcessStatePolling } from './hooks';
import { MatcherUserParams } from './MatcherUserParams';
import Ajv from 'ajv';
import { Modal, useModalControls } from '../layout/Modal';
Expand Down Expand Up @@ -54,14 +54,8 @@ const ViewMatch = ({ collectionId }: { collectionId: string }) => {
const matchQuery = getMatch.useQuery(matchId || '', {
skip: !matchId,
});
useBackoffPolling(
matchQuery,
(result) =>
!(
Boolean(result.error) ||
(Boolean(result.data?.state) && result.data?.state !== 'processing')
)
);
useProcessStatePolling(matchQuery, ['state'], { skipPoll: !matchId });

const match = matchQuery.data;
const matchStategy =
match?.state === 'complete'
Expand Down
18 changes: 9 additions & 9 deletions src/features/collections/collectionsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import {
getSelection,
} from '../../common/api/collectionsApi';
import { parseError } from '../../common/api/utils/parseError';
import {
useAppDispatch,
useAppSelector,
useBackoffPolling,
} from '../../common/hooks';
import { useAppDispatch, useAppSelector } from '../../common/hooks';
import { useAppParam } from '../params/hooks';
import { useProcessStatePolling } from './hooks';

interface SelectionState {
current: string[];
Expand Down Expand Up @@ -348,12 +345,15 @@ export const useGenerateSelectionId = (
skip: !_pendingId || !!_verifiedId,
});

useBackoffPolling(validateSelection, (result) => {
if (result.error) toast(parseError(result.error).message);
if (result.data?.state === 'processing') return true;
return false;
useProcessStatePolling(validateSelection, ['state'], {
skipPoll: !_pendingId || !!_verifiedId,
});

useEffect(() => {
if (validateSelection.error)
toast(parseError(validateSelection.error).message);
}, [validateSelection.error]);

useEffect(() => {
if (validateSelection.data?.state === 'complete') {
if (
Expand Down
15 changes: 5 additions & 10 deletions src/features/collections/data_products/Biolog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import {
import { parseError } from '../../../common/api/utils/parseError';
import { DataViewLink } from '../../../common/components';
import { Pagination, usePageBounds } from '../../../common/components/Table';
import { useAppDispatch, useBackoffPolling } from '../../../common/hooks';
import { useAppDispatch } from '../../../common/hooks';
import { formatNumber } from '../../../common/utils/stringUtils';
import { useAppParam } from '../../params/hooks';
import classes from '../Collections.module.scss';
import { useGenerateSelectionId } from '../collectionsSlice';
import { useProcessStatePolling } from '../hooks';
import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap';

export const Biolog: FC<{
Expand Down Expand Up @@ -148,15 +149,9 @@ const useBiolog = (collection_id: string | undefined) => {
skip: !collection_id,
});
const biolog = biologQuery.data;
useBackoffPolling(
biologQuery,
(result) => {
if (matchId && result?.data?.match_state === 'processing') return true;
if (selId && result?.data?.selection_state === 'processing') return true;
return false;
},
{ skipPoll: !collection_id || !(matchId || selId) }
);
useProcessStatePolling(biologQuery, ['match_state', 'selection_state'], {
skipPoll: !collection_id || !(matchId || selId),
});

//cache last row of each page, we should implement better backend pagination this is silly
useEffect(() => {
Expand Down
15 changes: 5 additions & 10 deletions src/features/collections/data_products/Microtrait.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {
import { parseError } from '../../../common/api/utils/parseError';
import { DataViewLink } from '../../../common/components';
import { Pagination, usePageBounds } from '../../../common/components/Table';
import { useAppDispatch, useBackoffPolling } from '../../../common/hooks';
import { useAppDispatch } from '../../../common/hooks';
import { formatNumber } from '../../../common/utils/stringUtils';
import classes from '../Collections.module.scss';
import { useMatchId, useGenerateSelectionId } from '../collectionsSlice';
import { useProcessStatePolling } from '../hooks';
import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap';

const getUPAFromEncoded = (encoded: string) => encoded.replaceAll('_', '/');
Expand Down Expand Up @@ -151,15 +152,9 @@ const useMicrotrait = (collection_id: string | undefined) => {
skip: !collection_id,
});
const microtrait = microtraitQuery.data;
useBackoffPolling(
microtraitQuery,
(result) => {
if (matchId && result?.data?.match_state === 'processing') return true;
if (selId && result?.data?.selection_state === 'processing') return true;
return false;
},
{ skipPoll: !collection_id || !(matchId || selId) }
);
useProcessStatePolling(microtraitQuery, ['match_state', 'selection_state'], {
skipPoll: !collection_id || !(matchId || selId),
});

//cache last row of each page, we should implement better backend pagination this is silly
useEffect(() => {
Expand Down
25 changes: 17 additions & 8 deletions src/features/collections/data_products/SampleAttribs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
usePageBounds,
useTableColumns,
} from '../../../common/components/Table';
import { useAppDispatch, useBackoffPolling } from '../../../common/hooks';
import { useAppDispatch } from '../../../common/hooks';
import {
clearAllFilters,
setFilter,
Expand All @@ -31,7 +31,7 @@ import classes from './../Collections.module.scss';
import { Alert, Grid, Paper, PaperProps, Stack, Link } from '@mui/material';
import { formatNumber } from '../../../common/utils/stringUtils';
import { filterContextMode, useFilterContexts } from '../Filters';
import { useTableViewParams } from '../hooks';
import { useProcessStatePolling, useTableViewParams } from '../hooks';

export const SampleAttribs: FC<{
collection_id: string;
Expand Down Expand Up @@ -92,7 +92,8 @@ export const SampleAttribs: FC<{
[attribParams]
);
// Current Data
const { data, isFetching } = getSampleAttribs.useQuery(attribParams);
const result = getSampleAttribs.useQuery(attribParams);
const { data, isFetching } = result;
const { data: countData } = getSampleAttribs.useQuery(countParams);
const allCountParams = useMemo(
() => ({
Expand Down Expand Up @@ -127,11 +128,19 @@ export const SampleAttribs: FC<{
selectData.data?.selection_state === 'processing'
);

useBackoffPolling(
selectData,
(result) => selectionPending || !result.data?.selection_state,
{ skipPoll: !selectData.data?.selection_state }
);
// Polling needed for main result, as it is not a primary dataproduct like genomes
const pollFor: ('match_state' | 'selection_state')[] = [
'match_state',
'selection_state',
];
if (!attribParams.match_id) pollFor.splice(pollFor.indexOf('match_state'), 1);
if (!attribParams.selection_id)
pollFor.splice(pollFor.indexOf('selection_state'), 1);
useProcessStatePolling(result, pollFor, {
skipPoll: !(attribParams.match_id || attribParams.selection_id),
});
// Also poll for the selection state
useProcessStatePolling(selectData, ['selection_state']);

useFilterContexts(collection_id, [
{
Expand Down
9 changes: 2 additions & 7 deletions src/features/collections/data_products/TaxaCount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import {
} from '../../../common/api/collectionsApi';
import { Loader } from '../../../common/components/Loader';
import { Select, SelectOption } from '../../../common/components/Select';
import { useBackoffPolling } from '../../../common/hooks';
import { snakeCaseToHumanReadable } from '../../../common/utils/stringUtils';
import { useMatchId, useSelectionId } from '../collectionsSlice';
import classes from './TaxaCount.module.scss';
import { Paper, PaperProps, Stack } from '@mui/material';
import { useFilterContexts } from '../Filters';
import { useProcessStatePolling } from '../hooks';

export const TaxaCount: FC<{
collection_id: string;
Expand Down Expand Up @@ -105,12 +105,7 @@ export const TaxaCount: FC<{
skip: !rank,
});

useBackoffPolling(countsQuery, (result) => {
if (matchId && result?.data?.match_state === 'processing') return true;
if (selectionId && result?.data?.selection_state === 'processing')
return true;
return false;
});
useProcessStatePolling(countsQuery, ['match_state', 'selection_state']);

const taxa = countsQuery.data?.data || [];

Expand Down
41 changes: 40 additions & 1 deletion src/features/collections/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { QueryDefinition } from '@reduxjs/toolkit/dist/query';
import { UseQueryHookResult } from '@reduxjs/toolkit/dist/query/react/buildHooks';
import { useMemo } from 'react';
import { toast } from 'react-hot-toast';
import { ProcessState } from '../../common/api/collectionsApi';
import { getNarratives } from '../../common/api/searchApi';
import { useAppSelector } from '../../common/hooks';
import { useAppSelector, useBackoffPolling } from '../../common/hooks';
import {
FilterContext,
useFilters,
Expand Down Expand Up @@ -90,3 +94,38 @@ export const useTableViewParams = (
]
);
};

export const useProcessStatePolling = <
StateKey extends string,
Result extends { [processStateKey in StateKey]: ProcessState },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
R extends UseQueryHookResult<QueryDefinition<unknown, any, any, Result>>
>(
result: R,
processStateKeys: StateKey[],
options?: { baseInterval?: number; rate?: number; skipPoll?: boolean }
) => {
useBackoffPolling<R>(
result,
(result) => {
for (let i = 0; i < processStateKeys.length; i++) {
const processStateKey = processStateKeys[i];
if (result.error || !result.data?.[processStateKey]) return false;
if (result.data[processStateKey] === 'processing') {
continue;
} else if (result.data[processStateKey] === 'complete') {
return false;
} else if (result.data[processStateKey] === 'failed') {
toast('ProcessState Polling Failed, see console for more info');
// eslint-disable-next-line no-console
console.error('ProcessState Polling Failed', { result });
return false;
} else {
return false;
}
}
return true;
},
options
);
};
Loading