Skip to content

Commit cdeb573

Browse files
Fix UnhandledPromiseRejection error on failed V2 List operations (#2571)
Fix UnhandlePromiseException on V2 List
1 parent be0b1d5 commit cdeb573

File tree

2 files changed

+68
-19
lines changed

2 files changed

+68
-19
lines changed

src/autoPagination.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ class V1SearchIterator<T> extends V1Iterator<T> {
146146
}
147147

148148
class V2ListIterator<T> implements AsyncIterator<T> {
149-
private currentPageIterator: Promise<Iterator<T>>;
150-
private nextPageUrl: Promise<string | null>;
149+
private firstPagePromise: Promise<PageResult<T>> | null;
150+
private currentPageIterator: Iterator<T> | null;
151+
private nextPageUrl: string | null;
151152
private requestArgs: RequestArgs;
152153
private spec: MethodSpec;
153154
private stripeResource: StripeResourceObject;
@@ -157,32 +158,33 @@ class V2ListIterator<T> implements AsyncIterator<T> {
157158
spec: MethodSpec,
158159
stripeResource: StripeResourceObject
159160
) {
160-
this.currentPageIterator = (async (): Promise<Iterator<T>> => {
161-
const page = await firstPagePromise;
162-
return page.data[Symbol.iterator]();
163-
})();
164-
165-
this.nextPageUrl = (async (): Promise<string | null> => {
166-
const page = await firstPagePromise;
167-
return page.next_page_url || null;
168-
})();
169-
161+
this.firstPagePromise = firstPagePromise;
162+
this.currentPageIterator = null;
163+
this.nextPageUrl = null;
170164
this.requestArgs = requestArgs;
171165
this.spec = spec;
172166
this.stripeResource = stripeResource;
173167
}
168+
private async initFirstPage(): Promise<void> {
169+
if (this.firstPagePromise) {
170+
const page = await this.firstPagePromise;
171+
this.firstPagePromise = null;
172+
this.currentPageIterator = page.data[Symbol.iterator]();
173+
this.nextPageUrl = page.next_page_url || null;
174+
}
175+
}
174176
private async turnPage(): Promise<Iterator<T> | null> {
175-
const nextPageUrl = await this.nextPageUrl;
176-
if (!nextPageUrl) return null;
177-
this.spec.fullPath = nextPageUrl;
177+
if (!this.nextPageUrl) return null;
178+
this.spec.fullPath = this.nextPageUrl;
178179
const page = await this.stripeResource._makeRequest([], this.spec, {});
179-
this.nextPageUrl = Promise.resolve(page.next_page_url);
180-
this.currentPageIterator = Promise.resolve(page.data[Symbol.iterator]());
180+
this.nextPageUrl = page.next_page_url || null;
181+
this.currentPageIterator = page.data[Symbol.iterator]();
181182
return this.currentPageIterator;
182183
}
183184
async next(): Promise<IteratorResult<T>> {
184-
{
185-
const result = (await this.currentPageIterator).next();
185+
await this.initFirstPage();
186+
if (this.currentPageIterator) {
187+
const result = this.currentPageIterator.next();
186188
if (!result.done) return {done: false, value: result.value};
187189
}
188190
const nextPageIterator = await this.turnPage();

test/autoPagination.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {makeAutoPaginationMethods} from '../src/autoPagination.js';
66
import {StripeResource} from '../src/StripeResource.js';
77
import {getMockStripe} from './testUtils.js';
88
import {MethodSpec} from '../src/Types.js';
9+
import {StripeAPIError} from '../src/Error.js';
910

1011
describe('auto pagination', () => {
1112
const testCase = (mockPaginationFn) => ({
@@ -841,6 +842,52 @@ describe('auto pagination', () => {
841842
],
842843
});
843844
});
845+
846+
it('handles firstPagePromise rejection without unhandled promise rejection', async () => {
847+
const spec = {
848+
method: 'GET',
849+
fullPath: '/v2/items',
850+
methodType: 'list',
851+
};
852+
853+
const mockStripe = getMockStripe({}, () => {});
854+
const resource = new StripeResource(mockStripe);
855+
856+
// Track unhandled rejections
857+
const unhandledRejections: Error[] = [];
858+
const unhandledRejectionHandler = (reason: Error): void => {
859+
unhandledRejections.push(reason);
860+
};
861+
process.on('unhandledRejection', unhandledRejectionHandler);
862+
863+
const error = new StripeAPIError({message: 'Something went wrong'});
864+
const rejectingPromise = Promise.reject(error);
865+
866+
const paginator = makeAutoPaginationMethods(
867+
resource,
868+
{},
869+
spec,
870+
rejectingPromise
871+
);
872+
873+
// User code catches the error via autoPagingToArray
874+
try {
875+
await paginator.autoPagingToArray({limit: 10});
876+
expect.fail('Should have thrown an error');
877+
} catch (e) {
878+
expect(e.message).to.equal('Something went wrong');
879+
}
880+
881+
// Give the event loop a chance to process any unhandled rejections
882+
await new Promise((resolve) => setTimeout(resolve, 10));
883+
884+
process.off('unhandledRejection', unhandledRejectionHandler);
885+
886+
expect(
887+
unhandledRejections,
888+
'Should not have any unhandled promise rejections'
889+
).to.have.length(0);
890+
});
844891
});
845892
});
846893

0 commit comments

Comments
 (0)