fix: support "in progress" status for lib upload

When uploading a library archive file during the creation of a new
library, the code prior to this commit did not properly handle the "In
Progress" state, which is when the celery task doing the archive
processing is actively running. Note that this is distinct from the
"Pending" state, which is when the task is waiting in the queue to be
run (which in practice should almost never happen unless there is an
operational issue).

Since celery tasks run in-process during local development, the task
was always finished by the time that the browser made a call to check
on the status. The problem only happened on slower sandboxes, where
processing truly runs asynchronously and might take a few seconds.
Because this case wasn't handled, the frontend would never poll for
updates either, so the upload was basically lost as far as the user
was concerned.
This commit is contained in:
David Ormsbee
2025-12-10 23:22:54 -05:00
parent a596bf5d38
commit 225c82d037
4 changed files with 35 additions and 2 deletions

View File

@@ -253,7 +253,8 @@ export const CreateLibrary = ({
{(restoreTaskId || isError || restoreMutation.isError) && (
<div className="mb-4">
{restoreStatus?.state === LibraryRestoreStatus.Pending && (
{(restoreStatus?.state === LibraryRestoreStatus.Pending
|| restoreStatus?.state === LibraryRestoreStatus.InProgress) && (
<Alert variant="info">
{intl.formatMessage(messages.restoreInProgress)}
</Alert>

View File

@@ -173,6 +173,34 @@ describe('create library apiHooks', () => {
expect(axiosMock.history.get[0].url).toEqual(`http://localhost:18010/api/libraries/v2/restore/?task_id=${taskId}`);
});
it('should handle in-progress status with refetch interval', async () => {
const taskId = 'in-progress-task-id';
const inProgressResult = {
state: LibraryRestoreStatus.InProgress,
result: null,
error: null,
error_log: null,
};
const expectedResult = {
state: LibraryRestoreStatus.InProgress,
result: null,
error: null,
errorLog: null,
};
axiosMock.onGet(`http://localhost:18010/api/libraries/v2/restore/?task_id=${taskId}`).reply(200, inProgressResult);
const { result } = renderHook(() => useGetLibraryRestoreStatus(taskId), { wrapper });
await waitFor(() => {
expect(result.current.isLoading).toBeFalsy();
});
expect(result.current.data).toEqual(expectedResult);
expect(axiosMock.history.get[0].url).toEqual(`http://localhost:18010/api/libraries/v2/restore/?task_id=${taskId}`);
});
it('should handle failed status', async () => {
const taskId = 'failed-task-id';
const failedResult = {

View File

@@ -41,7 +41,10 @@ export const useGetLibraryRestoreStatus = (taskId: string) => useQuery<GetLibrar
queryKey: libraryRestoreQueryKeys.restoreStatus(taskId),
queryFn: () => getLibraryRestoreStatus(taskId),
enabled: !!taskId, // Only run the query if taskId is provided
refetchInterval: (query) => (query.state.data?.state === LibraryRestoreStatus.Pending ? 2000 : false),
refetchInterval: (query) => (
(query.state.data?.state === LibraryRestoreStatus.Pending
|| query.state.data?.state === LibraryRestoreStatus.InProgress
) ? 2000 : false),
});
export const useCreateLibraryRestore = () => useMutation<CreateLibraryRestoreResponse, Error, File>({

View File

@@ -32,6 +32,7 @@ export interface GetLibraryRestoreStatusResponse {
export enum LibraryRestoreStatus {
Pending = 'Pending',
InProgress = 'In Progress',
Succeeded = 'Succeeded',
Failed = 'Failed',
}