Skip to content

Commit

Permalink
Merge pull request #2584 from epam/fix/apicontext-abort-handling
Browse files Browse the repository at this point in the history
Fix Apicontext abort handling
  • Loading branch information
AlekseyManetov authored Oct 25, 2024
2 parents 4a4c872 + e347493 commit 2b88662
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 4 deletions.
5 changes: 5 additions & 0 deletions uui-core/src/services/ApiContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ export class ApiContext extends BaseContext implements IApiContext {
this.resolveCall(call, result);
})
.catch((e) => {
if (e?.name === 'AbortError') {
this.removeFromQueue(call);
return;
}

/* Problem with response JSON parsing */
call.status = 'error';
this.setStatus('error');
Expand Down
91 changes: 87 additions & 4 deletions uui-core/src/services/__tests__/ApiContexts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ describe('ApiContext', () => {
'POST',
testData,
);
await delay(100);
await delay();

const call = context.getActiveCalls()[0];
expect(call.status).toEqual('scheduled');
Expand All @@ -116,7 +116,7 @@ describe('ApiContext', () => {
errorHandling: 'manual',
},
);
await delay(100);
await delay();

const call = context.getActiveCalls()[0];
expect(call.status).toEqual('scheduled');
Expand All @@ -140,7 +140,7 @@ describe('ApiContext', () => {
'POST',
testData,
);
await delay(100);
await delay();

const call = context.getActiveCalls()[0];
expect(call.status).toEqual('scheduled');
Expand All @@ -166,7 +166,7 @@ describe('ApiContext', () => {
errorHandling: 'manual',
},
);
await delay(100);
await delay();

const call = context.getActiveCalls()[0];
expect(call.status).toEqual('scheduled');
Expand Down Expand Up @@ -427,4 +427,87 @@ describe('ApiContext', () => {
},
);
});

it('should be able to cancel calls during fetch() stage', async () => {
const fetchMock = (url, rq: RequestInit) => {
return new Promise((resolve, reject) => {
rq.signal!.addEventListener('abort', () => {
reject(new DOMException('Aborted', 'AbortError'));
});
});
};

global.fetch = fetchMock as any;

const abortController = new AbortController();

context.processRequest<TestData>(
'path',
'POST',
testData,
{
fetchOptions: {
signal: abortController.signal,
},
},
);

await delay(1);

abortController.abort();

await delay(1);

const calls = context.getActiveCalls();
expect(calls.length).toEqual(0);

// If you get a promise, you expect it to be either resolved or rejected.
// Unfortunately, our API doesn't do either historically, as error is handled in ApiContext
// We need to rethink this somehow later.
// await expect(apiPromise).rejects.toHaveAttribute('name', 'AbortError');
});

it('should be able to cancel calls during json() stage', async () => {
const fetchMock = (url, rq: RequestInit) => {
return Promise.resolve({
ok: true,
json: () => {
return new Promise((resolve, reject) => {
rq.signal!.addEventListener('abort', () => {
reject(new DOMException('Aborted', 'AbortError'));
});
});
},
});
};

global.fetch = fetchMock as any;

const abortController = new AbortController();

context.processRequest<TestData>(
'path',
'POST',
testData,
{
fetchOptions: {
signal: abortController.signal,
},
},
);

await delay(1);

abortController.abort();

await delay(1);

const calls = context.getActiveCalls();
expect(calls.length).toEqual(0);

// If you get a promise, you expect it to be either resolved or rejected.
// Unfortunately, our API doesn't do either historically, as error is handled in ApiContext
// We need to rethink this somehow later.
// await expect(apiPromise).rejects.toHaveAttribute('name', 'AbortError');
});
});

0 comments on commit 2b88662

Please sign in to comment.