Skip to content

Commit

Permalink
Fix polling in case of error response; continue status polling on sta…
Browse files Browse the repository at this point in the history
…rtup (#1160)
  • Loading branch information
azangru authored Aug 6, 2024
1 parent 70d0287 commit 20915fa
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ import { vepFormSubmit } from 'src/content/app/tools/vep/state/vep-api/vepApiSli
import {
updateSubmission,
changeSubmissionId,
deleteSubmission
deleteSubmission,
restoreVepSubmissions,
type VepSubmissionsState
} from 'src/content/app/tools/vep/state/vep-submissions/vepSubmissionsSlice';

import VepSubmissionStatusPolling from 'src/content/app/tools/vep/state/vep-action-listeners/vepSubmissionStatusPolling';

import type { VepSubmissionWithoutInputFile } from 'src/content/app/tools/vep/types/vepSubmission';
import type {
AppStartListening,
AppListenerEffectAPI
Expand Down Expand Up @@ -98,6 +101,48 @@ const vepFormUnsuccessfulSubmissionListener = {
}
};

const vepSubmissionsRestoreListener = {
actionCreator: restoreVepSubmissions.fulfilled,
effect: async (
action: PayloadAction<VepSubmissionsState>,
listenerApi: AppListenerEffectAPI
) => {
// only respond to the first action
// (this action should happen only once over the lifetime of the page; but due to double execution useEffect in React StrictMode, it will be called twice in dev)
listenerApi.unsubscribe();
const { dispatch } = listenerApi;

const unresolvedSubmissions: VepSubmissionWithoutInputFile[] = [];
const interruptedSubmissions: VepSubmissionWithoutInputFile[] = [];

for (const submission of Object.values(action.payload)) {
if (submission.status === 'SUBMITTING') {
interruptedSubmissions.push(submission);
} else if (['SUBMITTED', 'RUNNING'].includes(submission.status)) {
unresolvedSubmissions.push(submission);
}
}

vepSubmissionStatusPolling.processSubmissionsOnStartup({
submissions: unresolvedSubmissions,
dispatch
});

// If at startup there are submissions with a "SUBMITTING" status,
// it means that the browser was refreshed/closed
// before submission data has been successfully transmitted to the server.
// Mark these submissions as failed.
for (const submission of interruptedSubmissions) {
await dispatch(
updateSubmission({
submissionId: submission.id,
fragment: { status: 'UNSUCCESSFUL_SUBMISSION' }
})
);
}
}
};

const vepSubmissionDeleteListener = {
actionCreator: deleteSubmission.fulfilled,
effect: async (
Expand All @@ -111,8 +156,8 @@ const vepSubmissionDeleteListener = {
};

export const startVepListeners = (startListening: AppStartListening) => {
// startListening(vepFormConfigQueryListener);
startListening(vepFormSuccessfulSubmissionListener);
startListening(vepFormUnsuccessfulSubmissionListener as any);
startListening(vepSubmissionDeleteListener);
startListening(vepSubmissionsRestoreListener);
};
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,83 @@ describe('getSubmissionStatusFetcher', () => {
});
});

it('continues polling if server responds with a 500 error', async () => {
// Arrange
const submissionId = 'my-submission';
let actualStatusPollCount = 0;

server.use(
http.get(
'http://tools-api-url/vep/submissions/:submissionId/status',
() => {
actualStatusPollCount++;

if (actualStatusPollCount === 1) {
// return a 500 error
return new HttpResponse(null, { status: 500 });
} else {
return HttpResponse.json({ status: 'SUCCEEDED' });
}
}
)
);

// Act
const vepStatusPolling = new VepSubmissionStatusPolling();
vepStatusPolling.enqueueSubmission({
submission: { id: submissionId, status: 'SUBMITTED' },
dispatch: jest.fn()
});

// Assert
await jest.runAllTimersAsync();

expect(actualStatusPollCount).toBe(2);
expect(updateSubmission as any).toHaveBeenCalledWith({
submissionId,
fragment: { status: 'SUCCEEDED' }
});
});

it('fails submission if server responds with a 404 error', async () => {
// Arrange
const submissionId = 'my-submission';
let actualStatusPollCount = 0;

server.use(
http.get(
'http://tools-api-url/vep/submissions/:submissionId/status',
() => {
actualStatusPollCount++;

if (actualStatusPollCount === 1) {
// return a 404 error
return new HttpResponse(null, { status: 404 });
} else {
// this should not be reached
return HttpResponse.json({ status: 'SUCCEEDED' });
}
}
)
);

// Act
const vepStatusPolling = new VepSubmissionStatusPolling();
vepStatusPolling.enqueueSubmission({
submission: { id: submissionId, status: 'SUBMITTED' },
dispatch: jest.fn()
});

// Assert
await jest.runAllTimersAsync();

expect(actualStatusPollCount).toBe(1);
expect(updateSubmission as any).toHaveBeenCalledWith({
submissionId,
fragment: { status: 'FAILED' }
});
});

it('checks statuses of all submissions without artificial delay when they are submitted as a batch', async () => {
// Arrange
const lateSubmissionId = 'late-submission-id';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type { SubmissionStatus } from 'src/content/app/tools/vep/types/vepSubmis

export const POLLING_INTERVAL = 15 * 1000;

type PolledSubmission = {
export type PolledSubmission = {
id: string;
status: SubmissionStatus;
};
Expand Down Expand Up @@ -109,12 +109,14 @@ class VepSubmissionStatusPolling {
if (response.status >= 500) {
// try later
this.queue.unshift(submissionId);
} else if (response.status === 404)
} else if (response.status === 404) {
// fail submission
this.reportSubmissionStatus({
submissionId,
status: 'FAILED'
});
}
return;
}

const { status } = await response.json();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {

import type { VepSubmissionWithoutInputFile } from 'src/content/app/tools/vep/types/vepSubmission';

type VepSubmissionsState = Record<string, VepSubmissionWithoutInputFile>;
export type VepSubmissionsState = Record<string, VepSubmissionWithoutInputFile>;

export const restoreVepSubmissions = createAsyncThunk(
'vep-submissions/restoreSubmissions',
Expand Down

0 comments on commit 20915fa

Please sign in to comment.