Modified 'printResume' Firebase cloud function: implemented basic retry mechanism with waitUntil 'networkidle0' and 'networkidle2', enhanced error handling to return more detailed information

This commit is contained in:
gianantoniopini
2021-05-04 11:30:12 +02:00
parent 1ac828b532
commit 14ea8de709

View File

@ -54,42 +54,100 @@ const deleteUserFunctionHandler = async (_, { auth }) => {
}
};
exports.deleteUser = functions
.runWith({ memory: '256MB' })
.https.onCall(deleteUserFunctionHandler);
/**
* Tries to navigate the page to a given URL.
*
* @param {puppeteer.Page} page Page.
* @param {string} url URL to navigate page to.
* @param {puppeteer.PuppeteerLifeCycleEvent} waitUntil When to consider navigation succeeded.
* @returns {Promise<string>} Returns null if no error occurred, otherwise returns the error message.
*/
const tryGotoPage = async (page, url, waitUntil) => {
let httpResponse;
exports.printResume = functions
.runWith({ memory: '1GB' })
.https.onCall(async ({ id, type }, { auth }) => {
if (!id) {
throw new functions.https.HttpsError(
'invalid-argument',
'The function must be called with argument "id" containing the resume ID.',
);
try {
httpResponse = await page.goto(url, {
waitUntil,
});
} catch (error) {
return `page.goto (waitUntil: "${waitUntil}") threw an error with message "${error.message}"`;
}
if (httpResponse === null) {
return `page.goto (waitUntil: "${waitUntil}") returned a null response`;
}
if (!httpResponse.ok()) {
return `page.goto (waitUntil: "${waitUntil}") returned a response with HTTP status ${httpResponse.status()} "${httpResponse.statusText()}"`;
}
return null;
};
/**
* Creates a page and navigates to a given URL.
*
* @param {puppeteer.Browser} browser Browser.
* @param {string} url URL to navigate to.
* @returns {Promise<{page: puppeteer.Page, errors: string[]}>} Returns an object with the page if no error occurred, otherwise returns an object with the list of error messages.
*/
const gotoPage = async (browser, url) => {
const errors = [];
const waitUntilArray = ['networkidle0', 'networkidle2'];
for (let index = 0; index < waitUntilArray.length; index++) {
/* eslint-disable no-await-in-loop */
const waitUntil = waitUntilArray[index];
const page = await browser.newPage();
await page.setCacheEnabled(false);
const error = await tryGotoPage(page, url, waitUntil);
if (!error) {
return { page, errors: null };
}
if (!type) {
throw new functions.https.HttpsError(
'invalid-argument',
'The function must be called with argument "type" containing the type of resume.',
);
}
errors.push(error);
await page.close();
}
if (!auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.',
);
}
return { page: null, errors };
};
const printResumeFunctionHandler = async ({ id, type }, { auth }) => {
if (!id) {
throw new functions.https.HttpsError(
'invalid-argument',
'The function must be called with argument "id" containing the resume ID.',
);
}
if (!type) {
throw new functions.https.HttpsError(
'invalid-argument',
'The function must be called with argument "type" containing the type of resume.',
);
}
if (!auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.',
);
}
try {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox'],
});
const page = await browser.newPage();
await page.goto(BASE_URL + id, {
waitUntil: 'networkidle0',
});
const url = BASE_URL + id;
const { page, errors } = await gotoPage(browser, url);
if (errors && errors.length > 0) {
throw new Error(errors.join(' - '));
}
await timeout(6000);
await page.emulateMediaType('print');
let pdf;
@ -124,4 +182,15 @@ exports.printResume = functions
await browser.close();
return Buffer.from(pdf).toString('base64');
});
} catch (error) {
throw new functions.https.HttpsError('internal', error.message);
}
};
exports.deleteUser = functions
.runWith({ memory: '256MB' })
.https.onCall(deleteUserFunctionHandler);
exports.printResume = functions
.runWith({ memory: '1GB' })
.https.onCall(printResumeFunctionHandler);