Skip to content

Commit

Permalink
feat: add new polling util (#1265)
Browse files Browse the repository at this point in the history
This PR adds a new poll util which accepts a generic request and validation function and will execute the request until the validation condition(s) have been met or a timeout is reached.
  • Loading branch information
juliannemarik authored Oct 6, 2023
1 parent 26dd47a commit e36c142
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/common/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from "./dasherize";
export * from "./titleize";
export * from "./memoize";
export * from "./getEnvironmentFromPortalUrl";
export * from "./poll";
46 changes: 46 additions & 0 deletions packages/common/src/utils/poll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Function to poll a provided request until a validation
* state or timeout is reached
* @param requestFn
* @param validationFn
* @param opts
*/
export const poll = async (
requestFn: () => any,
validationFn: (resp: any) => boolean,
opts?: {
timeout?: number;
timeBetweenRequests?: number;
}
): Promise<any> => {
const options =
opts || /* istanbul ignore next - must pass in overrides for tests */ {};
const timeout = options.timeout || 30000;
const timeBetweenRequests =
options.timeBetweenRequests ||
/* istanbul ignore next - cannot delay by this much in tests */ 3000;

let resp: any;
let requestCount = 0;

do {
// On subsequent requests, check if the timeout has been reached
// If YES: throw an error
// If NO: delay before the next request
if (requestCount > 0) {
if (requestCount * timeBetweenRequests >= timeout) {
throw new Error("Polling timeout");
}
await delay(timeBetweenRequests);
}

resp = await requestFn();
requestCount++;
} while (!validationFn(resp));

return resp;
};

const delay = (milliseconds: number) => {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
};
35 changes: 35 additions & 0 deletions packages/common/test/utils/poll.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { poll } from "../../src/utils/poll";

describe("poll", () => {
it("polls the request function until the validation function succeeds", async () => {
const validationFn = (resp: any) => resp && resp.id;
const requestFnSpy = jasmine
.createSpy()
.and.returnValues(Promise.resolve({}), Promise.resolve({ id: "00c" }));

const chk = await poll(
requestFnSpy,
validationFn,
// set delay to 0 for testing purposes
{ timeBetweenRequests: 0 }
);
expect(requestFnSpy).toHaveBeenCalledTimes(2);
expect(chk.id).toBe("00c");
});
it("throws an error when the timeout is reached", async () => {
const validationFn = (resp: any) => resp && resp.id;
const requestFnSpy = jasmine
.createSpy()
.and.returnValue(Promise.resolve({}));

try {
await poll(requestFnSpy, validationFn, {
timeBetweenRequests: 100,
timeout: 200,
});
} catch (error) {
expect(requestFnSpy).toHaveBeenCalledTimes(2);
expect((error as Error).message).toBe("Polling timeout");
}
});
});

0 comments on commit e36c142

Please sign in to comment.