diff --git a/packages/server/__tests__/db/seeds/fixtures.js b/packages/server/__tests__/db/seeds/fixtures.js
index 9672afb24..7b9e2c5cf 100644
--- a/packages/server/__tests__/db/seeds/fixtures.js
+++ b/packages/server/__tests__/db/seeds/fixtures.js
@@ -248,7 +248,7 @@ const grants = {
search_terms: '[in title/desc]+',
reviewer_name: 'none',
opportunity_category: 'Discretionary',
- description: '',
+ description: null,
eligibility_codes: '',
opportunity_status: 'posted',
raw_body_json: {
diff --git a/packages/server/__tests__/email/email.test.js b/packages/server/__tests__/email/email.test.js
index ee31fab54..5d4e5f3d4 100644
--- a/packages/server/__tests__/email/email.test.js
+++ b/packages/server/__tests__/email/email.test.js
@@ -258,6 +258,16 @@ describe('Email sender', () => {
expect(sendFake.secondCall.args[0].emailHTML.includes('
{
+ const sendFake = sinon.fake.returns('foo');
+ sinon.replace(email, 'sendGrantAssignedNotficationForAgency', sendFake);
+ const grant = fixtures.grants.noDescOrEligibilityCodes;
+
+ await email.sendGrantAssignedEmail({ grantId: grant.grant_id, agencyIds: [0], userId: 1 });
+
+ expect(sendFake.called).to.equal(true);
+ expect(sendFake.firstCall.args[1].includes('... View on')).to.equal(true);
+ });
});
context('async report email', () => {
it('sendAsyncReportEmail delivers an email with the signedURL for audit report', async () => {
diff --git a/packages/server/src/lib/email.js b/packages/server/src/lib/email.js
index 9570a8d3a..320611d3d 100644
--- a/packages/server/src/lib/email.js
+++ b/packages/server/src/lib/email.js
@@ -5,6 +5,7 @@ const asyncBatch = require('async-batch').default;
const fileSystem = require('fs');
const path = require('path');
const mustache = require('mustache');
+const { log } = require('./logging');
const emailService = require('./email/service-email');
const db = require('../db');
const { notificationType } = require('./email/constants');
@@ -177,7 +178,7 @@ function buildGrantsUrlSafe(emailNotificationType) {
function getGrantDetail(grant, emailNotificationType) {
const grantDetailTemplate = fileSystem.readFileSync(path.join(__dirname, '../static/email_templates/_grant_detail.html'));
- const description = grant.description.substring(0, 380).replace(/(<([^>]+)>)/ig, '');
+ const description = grant.description?.substring(0, 380).replace(/(<([^>]+)>)/ig, '');
const grantDetail = mustache.render(
grantDetailTemplate.toString(), {
title: grant.title,
@@ -258,9 +259,20 @@ async function sendGrantAssignedEmail({ grantId, agencyIds, userId }) {
2b. For each user part of the agency
i. Send email
*/
- const grantDetail = await buildGrantDetail(grantId, notificationType.grantAssignment);
- const agencies = await db.getAgenciesByIds(agencyIds);
- agencies.forEach((agency) => module.exports.sendGrantAssignedNotficationForAgency(agency, grantDetail, userId));
+ try {
+ const grantDetail = await buildGrantDetail(grantId, notificationType.grantAssignment);
+ const agencies = await db.getAgenciesByIds(agencyIds);
+ await asyncBatch(
+ agencies,
+ (agency) => { module.exports.sendGrantAssignedNotficationForAgency(agency, grantDetail, userId); },
+ 2,
+ );
+ } catch (err) {
+ log.error({
+ err, grantId, agencyIds, userId,
+ }, 'Failed to send grant assigned email');
+ throw err;
+ }
}
async function buildDigestBody({ name, openDate, matchedGrants }) {
diff --git a/packages/server/src/routes/grants.js b/packages/server/src/routes/grants.js
index eda8321ee..fcb81502c 100755
--- a/packages/server/src/routes/grants.js
+++ b/packages/server/src/routes/grants.js
@@ -360,7 +360,12 @@ router.put('/:grantId/assign/agencies', requireUser, async (req, res) => {
}
await db.assignGrantsToAgencies({ grantId, agencyIds, userId: user.id });
- email.sendGrantAssignedEmail({ grantId, agencyIds, userId: user.id });
+ try {
+ await email.sendGrantAssignedEmail({ grantId, agencyIds, userId: user.id });
+ } catch {
+ res.sendStatus(500);
+ return;
+ }
res.json({});
});