Skip to content

Commit

Permalink
Merge pull request #543 from bcgov/ccof-3694-puppeteer-debug
Browse files Browse the repository at this point in the history
Allow simultaneous PDFs to be generated at once
  • Loading branch information
trev-dev authored Oct 21, 2024
2 parents 46c5f89 + d282b5e commit ea3949a
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 61 deletions.
107 changes: 52 additions & 55 deletions backend/src/components/application.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable quotes */
'use strict';

const { compress } = require('compress-pdf');
const {
getOperation,
postOperation,
Expand Down Expand Up @@ -39,8 +40,7 @@ const {
} = require('../util/mapping/Mappings');
const {getCCFRIClosureDates} = require('./facility');
const {mapFundingObjectForFront} = require('./funding');
const puppeteer = require('puppeteer');
const {compress} = require('compress-pdf');
const { getBrowserContext, closeBrowser } = require('../util/browser');

const { ChangeRequestMappings, ChangeActionRequestMappings, NewFacilityMappings, MtfiMappings } = require('../util/mapping/ChangeRequestMappings');

Expand Down Expand Up @@ -431,6 +431,32 @@ async function submitApplication(req, res) {
}
}

async function postPdf(req, buffer) {
let payload;
if (req.params.applicationId) {
payload = {
ccof_applicationid: req.params.applicationId,
filename: `${req.body.summaryDeclarationApplicationName}_Summary_Declaration_${getCurrentDateForPdfFileName()}.pdf`,
filesize: buffer.byteLength,
subject: 'APPLICATION SUMMARY',
documentbody: buffer.toString('base64')
};

await postApplicationSummaryDocument(payload);
} else {
payload = {
ccof_change_requestid: req.params.changeRequestId,
filename: `Change_Request_Summary_Declaration_${getCurrentDateForPdfFileName()}.pdf`,
filesize: buffer.byteLength,
subject: 'CHANGE REQUEST SUMMARY',
documentbody: buffer.toString('base64')
};

await postChangeRequestSummaryDocument(payload);
}
return payload;
}

async function printPdf(req, numOfRetries = 0) {
let url = `${req.headers.referer}/printable`;

Expand All @@ -439,29 +465,14 @@ async function printPdf(req, numOfRetries = 0) {
log.info('printPdf :: applicationId is', req.params.applicationId);
log.verbose('printPdf :: url path is', url);

const browser = await puppeteer.launch({
headless: true, //setting this to 'new' will crash on openshift
dumpio: true,
args: [
'--no-sandbox',
'--disable-software-rasterizer',
'--disable-dev-shm-usage',
'--disable-gpu',
],
userDataDir: process.env.CHROME_DATA_DIR,
}); //to debug locally add {headless: false, devtools: true} in options <-make sure they are boolean and not string

const browserProcess = browser.process();
browserProcess
.on('exit', code => log.info(`printPdf :: browser process exited, status: ${code}`));
browserProcess
.on('close', code => log.info(`printPdf :: browser process closed, status: ${code}`));
const browserContext = await getBrowserContext();

try {
log.info('printPdf :: starting new page');
const page = await browser.newPage();
log.info('printPdf :: starting new browser page');
const page = await browserContext.newPage();

page.setDefaultTimeout(300000); //set navigation timeouts to 5 mins. So large organizations waiting to load do not throw error.
// set navigation timeouts to 5 mins. So large organizations waiting to load do not throw error.
page.setDefaultTimeout(300000);
await page.setRequestInterception(true);

page.on('request', (request) => {
Expand All @@ -471,50 +482,36 @@ async function printPdf(req, numOfRetries = 0) {
});

log.info('printPdf :: starting page load');
await page.goto(url, {waitUntil: 'networkidle0'});
await page.waitForSelector('#signatureTextField', {visible: true});
await page.goto(url, { waitUntil: 'networkidle0' });
await page.waitForSelector('#signatureTextField', { visible: true });

log.info('printPdf :: page loaded starting pdf creation');
const pdfBuffer = await page.pdf({displayHeaderFooter: false, printBackground: true, timeout: 300000, width: 1280});
const pdfBuffer = await page.pdf({
displayHeaderFooter: false,
printBackground: true,
timeout: 300000,
width: 1280
});

log.info('printPdf :: pdf buffer created starting compression');
const compressedPdfBuffer = await compress(pdfBuffer, {gsModule: process.env.GHOSTSCRIPT_PATH}); //this is set in dockerfile to fix ghostscript error on deploy
const compressedPdfBuffer = await compress(pdfBuffer, {
gsModule: process.env.GHOSTSCRIPT_PATH // this is set in dockerfile to fix ghostscript error on deploy
}
);
log.info('printPdf :: compression completed for applicationId', req.params.applicationId);

let payload;
//if the body contains an application ID, the summary dec is for PCF. Else, it should be a change request.
//if we want to, we could explicitly check the body for a change request id?
if(req.params.applicationId){
payload = {
ccof_applicationid: req.params.applicationId,
filename: `${req.body.summaryDeclarationApplicationName}_Summary_Declaration_${getCurrentDateForPdfFileName()}.pdf`,
filesize: compressedPdfBuffer.byteLength,
subject: 'APPLICATION SUMMARY',
documentbody: compressedPdfBuffer.toString('base64')
};

await postApplicationSummaryDocument(payload);
}
else {
payload = {
ccof_change_requestid: req.params.changeRequestId,
filename: `Change_Request_Summary_Declaration_${getCurrentDateForPdfFileName()}.pdf`,
filesize: compressedPdfBuffer.byteLength,
subject: 'CHANGE REQUEST SUMMARY',
documentbody: compressedPdfBuffer.toString('base64')
};
const payload = await postPdf(req, compressedPdfBuffer);
await browserContext.close();
closeBrowser();

await postChangeRequestSummaryDocument(payload);
}
browser.close();
return payload;
} catch (e) {
log.error(e);

if (browser.process() !== null) { await browser.close(); }
await browserContext?.close();

if (numOfRetries >= 3) {
log.info('printPdf :: maximum number of retries reached');
log.error(`printPdf :: unable to save pdf for application id ${req.params.applicationId}`);

} else {
let retryCount = numOfRetries + 1;
log.info('printPdf :: failed retrying');
Expand Down
62 changes: 62 additions & 0 deletions backend/src/util/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const puppeteer = require('puppeteer');
const log = require('../components/logger');

/** @typedef {import('puppeteer').Browser} Browser - The puppeteer browser instance */

/** @type {Browser|null} */
let browser = null;

/**
* Boostrap a browser instance and return a fresh browser context.
*
* @returns {Promise<import('puppeteer').BrowserContext|null>} The browser instance
*/
async function getBrowserContext() {
try {
if (browser instanceof puppeteer.Browser && browser.process() !== null) {
return browser;
}
// To debug locally add {headless: false, devtools: true} in options
// make sure they are boolean and not string
log.info('Puppeteer :: getBrowserContext launching new browser process');
browser = await puppeteer.launch({
headless: true, // setting this to 'new' will crash on openshift
devtools: false,
dumpio: true,
args: [
'--no-sandbox',
'--disable-software-rasterizer',
'--disable-dev-shm-usage',
'--disable-gpu',
],
userDataDir: process.env.CHROME_DATA_DIR,
});

const browserProcess = browser.process();
browserProcess
.on('exit', code => log.info(`Puppeteer :: browser process exited, status: ${code}`));
browserProcess
.on('close', code => log.info(`Puppeteer :: browser process closed, status: ${code}`));

return browser.createBrowserContext();
} catch (e) {
log.error('Puppeteer :: Browser process could not be retrieved', e);
return null;
}
}

/**
* Gracefully close the browser and all of its pages/contexts.
*/
async function closeBrowser() {
if (browser.pages().length === 0) {
await browser.close();
} else {
log.warn('Puppeteer :: closeBrowser was called with pages open');
}
}

module.exports = {
getBrowserContext,
closeBrowser
};
7 changes: 7 additions & 0 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,11 @@ h1 {
url('assets/font/BC-Sans/BCSans-BoldItalic.woff2') format('woff2'),
/* Optimized for very modern browsers */ url('assets/font/BC-Sans/BCSans-BoldItalic.woff') format('woff'); /* Modern Browsers */
}
@media print {
.v-main {
padding-top: 0;
padding-bottom: 0;
}
}
</style>
6 changes: 5 additions & 1 deletion frontend/src/components/Footer.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-footer absolute color="#003366" dark>
<v-footer id="app-footer" absolute color="#003366" dark>
<v-row justify="center" align="center">
<v-btn id="footer-home" variant="text" href="https://www.gov.bc.ca/"> Home </v-btn>
<v-btn id="footer-about" variant="text" href="https://www2.gov.bc.ca/gov/content/about-gov-bc-ca">
Expand Down Expand Up @@ -34,4 +34,8 @@ export default {
.v-btn.footer:hover:before {
background: none;
}
@media print {
#app-footer { display: none; }
}
</style>
8 changes: 6 additions & 2 deletions frontend/src/components/Header.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-app-bar color="#003366" class="px-5 px-md-10 app-header" height="64">
<v-app-bar id="app-header" color="#003366" class="px-5 px-md-10" height="64">
<!-- Navbar content -->
<v-container :class="{ sizingForIconXLScreen: $vuetify.display.xl }">
<v-row class="justify-space-between">
Expand Down Expand Up @@ -110,7 +110,7 @@ a {
color: #fff;
text-decoration: none;
}
.app-header {
#app-header {
border-bottom: 2px solid rgb(252, 186, 25) !important;
z-index: 1002;
}
Expand Down Expand Up @@ -155,4 +155,8 @@ a {
display: none;
}
}
@media print {
#app-header { display: none; }
}
</style>
11 changes: 9 additions & 2 deletions frontend/src/components/SummaryDeclaration.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<v-container fluid>
<v-form ref="form" v-model="isValidForm">
<v-form id="printable-form" ref="form" v-model="isValidForm">
<div class="text-center">
<div class="text-h4">
Child Care Operating Funding Program - {{ formattedProgramYear }} Program Confirmation Form
Expand Down Expand Up @@ -219,7 +219,7 @@
>
Funding Agreement Number: {{ getFundingAgreementNumber }}
</v-row>
<v-row justify="center" class="ma-8 pb-12">
<v-row justify="center" class="pb-12" :class="printableVersion ? 'ma-0' : 'ma-8'">
<v-card class="py-0 px-3 mx-0 mt-10 rounded-lg col-11" elevation="4">
<v-row>
<v-col class="pa-0">
Expand Down Expand Up @@ -964,4 +964,11 @@ li {
.special {
margin-top: 5vh !important;
}
@media print {
#printable-form .v-card {
margin-left: 0 !important;
margin-right: 0 !important;
}
}
</style>
6 changes: 5 additions & 1 deletion frontend/src/components/TheEnvBar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-app-bar v-if="bannerColor" :color="bannerColor" height="20" class="env-bar">
<v-app-bar id="app-env-bar" v-if="bannerColor" :color="bannerColor" height="20" class="env-bar">
<div>
<h3 class="env-text">{{ bannerEnvironment }} Environment</h3>
</div>
Expand Down Expand Up @@ -28,4 +28,8 @@ export default {
color: #ffffff;
font-size: 0.8rem;
}
@media print {
#app-env-bar { display: none; }
}
</style>

0 comments on commit ea3949a

Please sign in to comment.