Skip to content

Instantly share code, notes, and snippets.

@dbani-dev
Last active April 8, 2022 01:18
Show Gist options
  • Save dbani-dev/d78d08da0a7b5214d5b3f78082721932 to your computer and use it in GitHub Desktop.
Save dbani-dev/d78d08da0a7b5214d5b3f78082721932 to your computer and use it in GitHub Desktop.
class PollingReports {
constructor(
dataProviderId,
siteSelections,
values,
pollingInterval,
pollingMaxAttempts
) {
super();
this.dataProviderId = dataProviderId;
this.siteSelections = siteSelections;
this.values = values;
this.cancelledPolling = false;
this.pollingInterval = pollingInterval;
this.pollingMaxAttempts = pollingMaxAttempts;
}
poll = async ({ fn, validate, interval, maxAttempts = 10 }) => {
const sleep = (ms) => {
return new Promise(
(resolve) => (this.pollingTimeout = setTimeout(resolve, ms))
);
};
for (let attempts = 0; attempts < maxAttempts; attempts++) {
try {
const result = await fn();
if (validate(result)) {
return result;
}
} catch (err) {
throw new Error(`function error: ${err.message}`);
}
await sleep(interval);
}
throw new Error("polling: exceeded max attempts");
};
requestDataFromS3 = async (signedDataUrl) => {
try {
if (!this.cancelledPolling) {
this.requestSignedPromise = fetch(signedDataUrl, { method: "GET" });
const requestSigned = await this.requestSignedPromise;
logInfo(`polling: signed url`, { requestSigned });
if (requestSigned.status === 200) {
return requestSigned.json();
} else {
throw new Error("Getting data from S3 failed");
}
}
} catch (err) {
throw err;
}
};
requestAdd = async () => {
try {
if (!this.cancelledPolling) {
const waitForToken = await window.pmpGetAuthToken();
this.requestAddPromise = window.api.post(
"api",
"reportrequest/addrequest",
{
body: {
requestType: "report",
format: "data", // "data", "csv" etc
parameters: {
dataProviderId: this.dataProviderId,
siteSelections: this.siteSelections,
parameters: this.values,
},
},
}
);
return await this.requestAddPromise;
}
} catch (err) {
throw err;
}
};
requestStatus = async (requestId) => {
try {
const status = async () => {
if (!this.cancelledPolling) {
try {
this.requestStatusPromise = window.api.get(
"api",
`reportrequest/getrequeststatus?id=${requestId}`
);
const requestStatus = await this.requestStatusPromise;
console.log({ requestStatus });
if (requestStatus.signedDataUrl) {
return {
status: requestStatus.status,
signedDataUrl: requestStatus.signedDataUrl,
};
} else {
return {
status: requestStatus.status,
};
}
} catch (err) {
throw err;
}
}
};
const validate = (result) => {
logInfo(`polling: status`, { status: result.status });
if (!this.cancelledPolling && result.status === "Success") {
return true;
}
return false;
};
return this.poll({
fn: status,
validate: validate,
interval: this.pollingInterval,
maxAttempts: this.pollingMaxAttempts,
});
} catch (err) {
throw err;
}
};
startPolling = async () => {
try {
logSuccess(`polling: start`, {
values: this.values,
siteSelections: this.siteSelections,
dataProviderId: this.dataProviderId,
});
const requestAdd = await this.requestAdd();
const requestStatus = await this.requestStatus(requestAdd.id);
const requestSigned = await this.requestDataFromS3(
requestStatus.signedDataUrl
);
return requestSigned;
} catch (err) {
throw err;
}
};
cancelPolling = () => {
this.cancelledPolling = true;
if (this.requestAddPromise) {
window.api.cancel(this.requestAddPromise, "promise cancelled");
}
if (this.requestStatusPromise) {
window.api.cancel(this.requestStatusPromise, "promise cancelled");
}
if (this.requestSignedPromise) {
window.api.cancel(this.requestSignedPromise, "promise cancelled");
}
if (this.pollTimeout) {
clearTimeout(this.pollTimeout);
}
};
}
// test
// ERROR: thrown: "Exceeded timeout of 5000 ms for a test. Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
// Timers don't seem to play well with promises
describe("Polling Reports", () => {
it("polling cancelled", async () => {
jest.useFakeTimers();
const polling = new PollingReports(
"###",
[
{
accountNumber: "###",
siteId: null,
meterDeviceIds: [],
name: "1 Ardrossan Road",
text: "1 Ardrossan Road",
type: "account",
display: true,
iconlink: "",
},
],
{
fromDate: "15/12/2021",
toDate: "15/3/2022",
aggregate: "2",
detailedData: false,
},
100, // pollingInterval
4 // maxAttempts
);
// requestAdd
window.api.post.mockResolvedValue({
id: "###",
});
// requestStatus
window.api.get
.mockResolvedValueOnce({
status: "Requested",
})
.mockResolvedValueOnce({
status: "Requested",
})
.mockResolvedValueOnce({
status: "Success",
signedDataUrl: "http://",
});
// requestSigned
fetch.mockResponseOnce(JSON.stringify({ data: "12345" }));
await polling.startPolling();
jest.advanceTimersByTime(100);
expect(window.api.post).toHaveBeenCalledTimes(1);
expect(window.api.get).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledTimes(0);
jest.advanceTimersByTime(200);
expect(window.api.post).toHaveBeenCalledTimes(1);
expect(window.api.get).toHaveBeenCalledTimes(2);
expect(fetch).toHaveBeenCalledTimes(0);
polling.cancelPolling();
expect(window.api.post).toHaveBeenCalledTimes(1);
expect(window.api.get).toHaveBeenCalledTimes(2);
expect(fetch).toHaveBeenCalledTimes(0);
jest.useRealTimers();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment