Skip to content

Commit

Permalink
fix(emails): retry sending, improve whitespace
Browse files Browse the repository at this point in the history
Improves the text of the email by including the institution name and a
sign off by the BHIMA team.  Removes unnecessary whitespace from the
email as well, to make it look more professional.

Additionally, we've improved the sending by only sending a single email
per cron report by putting all the contacts in the BCC field.  This
allows us to further make the refinements to retry sending the message
multiple times if internet timeout issues occur using the p-retry
module.

Finally, further logging has been added to assist in our debugging.

Partially addresses #3967.
  • Loading branch information
jniles committed Jan 20, 2020
1 parent 2e0b11c commit 66e47d7
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 22 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"cross-env": "^6.0.3",
"csvtojson": "^2.0.8",
"debug": "^4.1.1",
"delay": "^4.3.0",
"dotenv": "^8.0.0",
"excel4node": "^1.7.0",
"express": "^4.16.4",
Expand All @@ -92,6 +93,7 @@
"mysql": "^2.16.0",
"ng-file-upload": "^12.2.13",
"ngstorage": "^0.3.11",
"p-retry": "^4.2.0",
"puppeteer": "^2.0.0",
"q": "~1.5.1",
"stream-to-promise": "^2.2.0",
Expand Down
80 changes: 59 additions & 21 deletions server/controllers/admin/cronEmailReport/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
const debug = require('debug')('bhima:cron');
const Cron = require('cron').CronJob;
const pRetry = require('p-retry');
const delay = require('delay');

const db = require('../../../lib/db');
const BhMoment = require('../../../lib/bhMoment');
Expand All @@ -15,6 +17,10 @@ const dbReports = require('../../report.handlers');

const CURRENT_JOBS = new Map();

// TODO(@jniles) - move this into a session variable
const DEVELOPER_ADDRESS = 'developers@imaworldhealth.org';
const RETRY_COUNT = 5;

function find(options = {}) {
const filters = new FilterParser(options, { tableAlias : 'cer' });
const sql = `
Expand Down Expand Up @@ -150,6 +156,7 @@ function addJob(frequency, cb, ...params) {
*/
async function launchCronEmailReportJobs() {
try {
debug('beginning scan of saved automated email reports.');
const session = await loadSession();
if (!session.enterprise.settings.enable_auto_email_report) { return; }

Expand All @@ -176,43 +183,74 @@ function createEmailReportJob(record, cb, ...params) {
return updateCronEmailReportNextSend(record.id, job);
}

/* eslint-disable max-len */
const content = `
To whom it may concern,
You are subscribed to automated reports from the BHIMA software installation at %ENTERPRISE%. Please find attached the following reports:
- %FILENAME%
Thank you,
The BHIMA team
`;
/* eslint-enable max-len */

/**
* @function sendEmailReportDocument
* @param {object} record A row of cron email report
* @param {object} options The report options
*/
async function sendEmailReportDocument(record) {
try {
const contacts = await loadContacts(record.entity_group_uuid);

if (contacts.length === 0) {
debug(`No contacts found for automated report ${record.label} (${record.report_id}).`);
debug(`Report will not send. Exiting.`);
return;
}

const reportOptions = JSON.parse(record.params);
// dynamic dates in the report params if needed
const options = addDynamicDatesOptions(record.cron_id, record.has_dynamic_dates, reportOptions);
const fn = dbReports[record.report_key];
const contacts = await loadContacts(record.entity_group_uuid);


const session = await loadSession();
const document = await fn(options, session);
const filename = replaceSlash(document.headers.filename);

if (contacts.length) {
const attachments = [
{ filename, stream : document.report },
];
const content = `
Hi,
We have attached to this email the ${filename} file
Thank you,
`;
const mails = contacts.map(c => {
return mailer.email(c, record.label, content, {
attachments,
});
});

await Promise.all(mails);
await updateCronEmailReportLastSend(record.id);
debug(`(${record.label}) report sent by email to ${contacts.length} contacts`);
const attachments = [
{ filename, stream : document.report },
];

// template in the temporary variables
const body = content
.replace('%ENTERPRISE%', session.enterprise.name)
.replace('%FILENAME%', filename)
.trim();

const addresses = contacts.map(addr => addr.trim());

// eslint-disable-next-line
function sendEmailToSubscribers() {
return mailer.email(DEVELOPER_ADDRESS, record.label, body, { attachments, bcc : addresses });
}

await pRetry(sendEmailToSubscribers, {
retries : RETRY_COUNT,
onFailedAttempt : async (error) => {
// eslint-disable-next-line
debug(`(${record.label}) Sending report failed with ${error.name}. Attempt ${error.attemptNumber} of ${RETRY_COUNT}.`);

// delay by 10 seconds
await delay(10000);
},
});

await updateCronEmailReportLastSend(record.id);
debug(`(${record.label}) report sent by email to ${contacts.length} contacts`);
} catch (e) {
debug(e);
}
Expand Down
4 changes: 4 additions & 0 deletions server/lib/mailer.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ exports.email = function email(address, subject, message, options = {}) {
text : message,
};

if (options.bcc) {
Object.assign(mail, { bcc : options.bcc });
}

return sendp(mail);
});
};
20 changes: 19 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,11 @@
"@types/bunyan" "*"
"@types/node" "*"

"@types/retry@^0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==

"@types/selenium-webdriver@^3.0.0":
version "3.0.16"
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.16.tgz#50a4755f8e33edacd9c406729e9b930d2451902a"
Expand Down Expand Up @@ -2742,6 +2747,11 @@ del@^5.0.0:
rimraf "^3.0.0"
slash "^3.0.0"

delay@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/delay/-/delay-4.3.0.tgz#efeebfb8f545579cb396b3a722443ec96d14c50e"
integrity sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA==

delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
Expand Down Expand Up @@ -7373,6 +7383,14 @@ p-map@^3.0.0:
dependencies:
aggregate-error "^3.0.0"

p-retry@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.2.0.tgz#ea9066c6b44f23cab4cd42f6147cdbbc6604da5d"
integrity sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==
dependencies:
"@types/retry" "^0.12.0"
retry "^0.12.0"

p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
Expand Down Expand Up @@ -8668,7 +8686,7 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==

retry@0.12.0:
retry@0.12.0, retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
Expand Down

0 comments on commit 66e47d7

Please sign in to comment.