Skip to content

Commit

Permalink
Merge pull request #412 from univapay/feature/pierreh/merchant-2855/a…
Browse files Browse the repository at this point in the history
…dd-poll-iteration-callback

Add poll iteration callback and more tests
  • Loading branch information
YusukeFukushima committed May 30, 2022
2 parents c5aaf4d + 6ba1d56 commit b94b437
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 18 deletions.
20 changes: 11 additions & 9 deletions src/api/RestAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,28 +296,30 @@ export class RestAPI extends EventEmitter {
/**
* @internal
*/
async longPolling<A>(
promise: PromiseCreator<A>,
condition: (response: A) => boolean,
cancelCondition?: (response: A) => boolean,
callback?: ResponseCallback<A>,
async longPolling<Response>(
promise: PromiseCreator<Response>,
condition: (response: Response) => boolean,
cancelCondition?: (response: Response) => boolean,
callback?: ResponseCallback<Response>,
timeout: number = POLLING_TIMEOUT,
interval: number = POLLING_INTERVAL
): Promise<A> {
interval: number = POLLING_INTERVAL,
iterationCallback?: (response: Response) => void
): Promise<Response> {
return execRequest(async () => {
let timedOut = false;

return Promise.race([
// Timeout
new Promise<A>((_, reject) => {
new Promise<Response>((_, reject) => {
setTimeout(() => {
timedOut = true;
reject(new TimeoutError(timeout));
}, timeout);
}),
// Repeater
(async function repeater(): Promise<A> {
(async function repeater(): Promise<Response> {
const result = await promise();
iterationCallback?.(result);

if (cancelCondition?.(result)) {
return null;
Expand Down
13 changes: 11 additions & 2 deletions src/resources/Cancels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,20 @@ export class Cancels extends CRUDResource {
* Condition to cancel the polling and return `null`,
*/
cancelCondition?: (response: ResponseCancel) => boolean,
successCondition: ({ status }: ResponseCancel) => boolean = ({ status }) => status !== CancelStatus.PENDING
successCondition: ({ status }: ResponseCancel) => boolean = ({ status }) => status !== CancelStatus.PENDING,
iterationCallback?: (response: ResponseCancel) => void
): Promise<ResponseCancel> {
const pollingData = { ...data, polling: true };
const promise: () => Promise<ResponseCancel> = () => this.get(storeId, chargeId, id, pollingData, auth);

return this.api.longPolling(promise, successCondition, cancelCondition, callback);
return this.api.longPolling(
promise,
successCondition,
cancelCondition,
callback,
undefined,
undefined,
iterationCallback
);
}
}
13 changes: 11 additions & 2 deletions src/resources/Charges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,20 @@ export class Charges extends CRUDResource {
* Condition for the resource to be successfully loaded. Default to pending status check.
*/
cancelCondition?: (response: ResponseCharge) => boolean,
successCondition: ({ status }: ResponseCharge) => boolean = ({ status }) => status !== ChargeStatus.PENDING
successCondition: ({ status }: ResponseCharge) => boolean = ({ status }) => status !== ChargeStatus.PENDING,
iterationCallback?: (response: ResponseCharge) => void
): Promise<ResponseCharge> {
const pollingData = { ...data, polling: true };
const promise: () => Promise<ResponseCharge> = () => this.get(storeId, id, pollingData, auth);

return this.api.longPolling(promise, successCondition, cancelCondition, callback);
return this.api.longPolling(
promise,
successCondition,
cancelCondition,
callback,
undefined,
undefined,
iterationCallback
);
}
}
13 changes: 11 additions & 2 deletions src/resources/Refunds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,20 @@ export class Refunds extends CRUDResource {
* Condition for the resource to be successfully loaded. Default to pending status check.
*/
cancelCondition?: (response: ResponseRefund) => boolean,
successCondition: ({ status }: ResponseRefund) => boolean = ({ status }) => status !== RefundStatus.PENDING
successCondition: ({ status }: ResponseRefund) => boolean = ({ status }) => status !== RefundStatus.PENDING,
iterationCallback?: (response: ResponseRefund) => void
): Promise<ResponseRefund> {
const pollData = { ...data, polling: true };
const promise: () => Promise<ResponseRefund> = () => this.get(storeId, chargeId, id, pollData, auth);

return this.api.longPolling(promise, successCondition, cancelCondition, callback);
return this.api.longPolling(
promise,
successCondition,
cancelCondition,
callback,
undefined,
undefined,
iterationCallback
);
}
}
13 changes: 11 additions & 2 deletions src/resources/Subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,21 @@ export class Subscriptions extends CRUDResource {
*/
cancelCondition?: (response: ResponseSubscription) => boolean,
successCondition: ({ status }: ResponseSubscription) => boolean = ({ status }) =>
status !== SubscriptionStatus.UNVERIFIED
status !== SubscriptionStatus.UNVERIFIED,
iterationCallback?: (response: ResponseSubscription) => void
): Promise<ResponseSubscription> {
const pollData = { ...data, polling: true };
const promise: () => Promise<ResponseSubscription> = () => this.get(storeId, id, pollData, auth);

return this.api.longPolling(promise, successCondition, cancelCondition, callback);
return this.api.longPolling(
promise,
successCondition,
cancelCondition,
callback,
undefined,
undefined,
iterationCallback
);
}

async pollSubscriptionWithFirstCharge(
Expand Down
46 changes: 45 additions & 1 deletion test/specs/cancels.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,54 @@ describe("Cancels", () => {

const request = cancels.poll(uuid(), uuid(), uuid());

sandbox.clock.tick(POLLING_TIMEOUT);
await sandbox.clock.tick(POLLING_TIMEOUT);

await expect(request).to.eventually.be.rejectedWith(TimeoutError);
});

it("cancel polling", async () => {
const recordPendingData = { ...recordData, status: CancelStatus.PENDING };

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: recordPendingData,
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "pending",
}
);

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: { ...recordData, status: CancelStatus.FAILED },
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "failed",
}
);

const request = cancels.poll(
uuid(),
uuid(),
uuid(),
undefined,
undefined,
undefined,
({ status }) => status === CancelStatus.FAILED
);
await sandbox.clock.tickAsync(POLLING_INTERVAL);
await sandbox.clock.tickAsync(POLLING_INTERVAL);

await expect(request).to.eventually.eql(null);
});
});

it("should return request error when parameters for route are invalid", async () => {
Expand Down
43 changes: 43 additions & 0 deletions test/specs/charges.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,49 @@ describe("Charges", () => {

await expect(request).to.eventually.be.rejectedWith(TimeoutError);
});

it("cancel polling", async () => {
const recordPendingData = { ...recordData, status: ChargeStatus.PENDING };

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: recordPendingData,
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "pending",
}
);

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: { ...recordData, status: ChargeStatus.FAILED },
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "failed",
}
);

const request = charges.poll(
uuid(),
uuid(),
undefined,
undefined,
undefined,
({ status }) => status === ChargeStatus.FAILED
);
await sandbox.clock.tickAsync(POLLING_INTERVAL);
await sandbox.clock.tickAsync(POLLING_INTERVAL);

await expect(request).to.eventually.eql(null);
});
});

context("GET /stores/:storeId/charges/:chargeId/issuerToken", () => {
Expand Down
44 changes: 44 additions & 0 deletions test/specs/refunds.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,50 @@ describe("Refunds", () => {

await expect(request).to.eventually.be.rejectedWith(TimeoutError);
});

it("cancel polling", async () => {
const recordPendingData = { ...recordData, status: RefundStatus.PENDING };

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: recordPendingData,
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "pending",
}
);

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: { ...recordData, status: RefundStatus.FAILED },
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "failed",
}
);

const request = refunds.poll(
uuid(),
uuid(),
uuid(),
undefined,
undefined,
undefined,
({ status }) => status === RefundStatus.FAILED
);
await sandbox.clock.tickAsync(POLLING_INTERVAL);
await sandbox.clock.tickAsync(POLLING_INTERVAL);

await expect(request).to.eventually.eql(null);
});
});

context("PATCH /stores/:storeId/charges/:chargeId/refunds/:id", () => {
Expand Down
43 changes: 43 additions & 0 deletions test/specs/subscriptions.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,49 @@ describe("Subscriptions", () => {

await expect(request).to.eventually.be.rejectedWith(TimeoutError);
});

it("cancel polling", async () => {
const recordPendingData = { ...recordData, status: SubscriptionStatus.UNVERIFIED };

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: recordPendingData,
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "pending",
}
);

fetchMock.getOnce(
recordPathMatcher,
{
status: 200,
body: { ...recordData, status: SubscriptionStatus.SUSPENDED },
headers: { "Content-Type": "application/json" },
},
{
method: HTTPMethod.GET,
name: "failed",
}
);

const request = subscriptions.poll(
uuid(),
uuid(),
undefined,
undefined,
undefined,
({ status }) => status === SubscriptionStatus.SUSPENDED
);
await sandbox.clock.tickAsync(POLLING_INTERVAL);
await sandbox.clock.tickAsync(POLLING_INTERVAL);

await expect(request).to.eventually.eql(null);
});
});

context("PATCH /stores/:storeId/subscriptions/:id", () => {
Expand Down

0 comments on commit b94b437

Please sign in to comment.