From 98a9dd4413a692719b9d997e275d1a648f9da10e Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Fri, 1 Mar 2024 12:06:25 -0800 Subject: [PATCH 1/8] Update emails to point to new grant detail page --- packages/server/src/lib/email.js | 32 +++++++++++++++---- .../static/email_templates/_grant_detail.html | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/server/src/lib/email.js b/packages/server/src/lib/email.js index 27d6c5009..26bbc777e 100644 --- a/packages/server/src/lib/email.js +++ b/packages/server/src/lib/email.js @@ -152,10 +152,17 @@ function sendWelcomeEmail(email, httpOrigin) { }); } -function getGrantDetail(grant, emailNotificationType) { - const grantDetailTemplate = fileSystem.readFileSync(path.join(__dirname, '../static/email_templates/_grant_detail.html')); +function buildGrantDetailUrl(grantId, emailNotificationType) { + const grantDetailUrl = new URL(process.env.WEBSITE_DOMAIN); + grantDetailUrl.pathname = `grant/${grantId}`; + grantDetailUrl.searchParams.set('utm_source', 'usdr-grants'); + grantDetailUrl.searchParams.set('utm_medium', 'email'); + grantDetailUrl.searchParams.set('utm_campaign', emailNotificationType); + grantDetailUrl.searchParams.set('utm_content', 'grant-details'); + return grantDetailUrl; +} - const description = grant.description.substring(0, 380).replace(/(<([^>]+)>)/ig, ''); +function buildGrantsUrl(emailNotificationType) { const grantsUrl = new URL(process.env.WEBSITE_DOMAIN); if (emailNotificationType === notificationType.grantDigest) { grantsUrl.pathname = 'grants'; @@ -165,6 +172,12 @@ function getGrantDetail(grant, emailNotificationType) { grantsUrl.searchParams.set('utm_source', 'subscription'); grantsUrl.searchParams.set('utm_medium', 'email'); grantsUrl.searchParams.set('utm_campaign', emailNotificationType); + return grantsUrl; +} + +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 grantDetail = mustache.render( grantDetailTemplate.toString(), { title: grant.title, @@ -177,8 +190,13 @@ function getGrantDetail(grant, emailNotificationType) { award_ceiling: grant.award_ceiling || 'Not available', // estimated_funding: grant.estimated_funding, TODO: add once field is available in the database. cost_sharing: grant.cost_sharing, - link_url: `https://www.grants.gov/search-results-detail/${grant.grant_id}`, - grants_url: grantsUrl.toString(), + link_url: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED + ? buildGrantDetailUrl(grant.grant_id, notificationType).toString() + : `https://www.grants.gov/search-results-detail/${grant.grant_id}`, + link_description: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED + ? 'Grant Finder' + : 'Grants.gov', + grants_url: buildGrantsUrl(notificationType).toString(), view_grant_label: emailNotificationType === notificationType.grantDigest ? undefined : 'View My Grants', }, ); @@ -218,10 +236,10 @@ async function sendGrantAssignedNotficationForAgency(assignee_agency, grantDetai // TODO: add plain text version of the email const emailPlain = emailHTML.replace(/<[^>]+>/g, ''); const emailSubject = `Grant Assigned to ${assignee_agency.name}`; - const assginees = await db.getSubscribersForNotification(assignee_agency.id, notificationType.grantAssignment); + const assignees = await db.getSubscribersForNotification(assignee_agency.id, notificationType.grantAssignment); const inputs = []; - assginees.forEach((assignee) => inputs.push( + assignees.forEach((assignee) => inputs.push( { toAddress: assignee.email, emailHTML, diff --git a/packages/server/src/static/email_templates/_grant_detail.html b/packages/server/src/static/email_templates/_grant_detail.html index 7bc698c1d..0abc083ba 100644 --- a/packages/server/src/static/email_templates/_grant_detail.html +++ b/packages/server/src/static/email_templates/_grant_detail.html @@ -31,7 +31,7 @@ style="Margin:0;padding-bottom:16px;">

- {{{description}}}... View on Grants.gov + {{{description}}}... View on {{ link_description }}

From b79081fdb32f271e93340c0635c3fac60b1a7e67 Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Fri, 1 Mar 2024 12:11:40 -0800 Subject: [PATCH 2/8] Set up env var in staging/prod --- packages/server/src/lib/email.js | 4 ++-- terraform/prod.tfvars | 3 +++ terraform/staging.tfvars | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/server/src/lib/email.js b/packages/server/src/lib/email.js index 26bbc777e..cf2fb3852 100644 --- a/packages/server/src/lib/email.js +++ b/packages/server/src/lib/email.js @@ -190,10 +190,10 @@ function getGrantDetail(grant, emailNotificationType) { award_ceiling: grant.award_ceiling || 'Not available', // estimated_funding: grant.estimated_funding, TODO: add once field is available in the database. cost_sharing: grant.cost_sharing, - link_url: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED + link_url: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' ? buildGrantDetailUrl(grant.grant_id, notificationType).toString() : `https://www.grants.gov/search-results-detail/${grant.grant_id}`, - link_description: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED + link_description: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' ? 'Grant Finder' : 'Grants.gov', grants_url: buildGrantsUrl(notificationType).toString(), diff --git a/terraform/prod.tfvars b/terraform/prod.tfvars index aed1475fd..e9366a88e 100644 --- a/terraform/prod.tfvars +++ b/terraform/prod.tfvars @@ -72,6 +72,9 @@ api_log_retention_in_days = 30 api_datadog_environment_variables = { DD_PROFILING_ENABLED = true, } +api_container_environment = { + NEW_GRANT_DETAILS_PAGE_ENABLED = false, +} // Postgres postgres_enabled = true diff --git a/terraform/staging.tfvars b/terraform/staging.tfvars index a1fc95e6e..3d8c0ef14 100644 --- a/terraform/staging.tfvars +++ b/terraform/staging.tfvars @@ -66,6 +66,9 @@ api_log_retention_in_days = 14 api_datadog_environment_variables = { DD_PROFILING_ENABLED = true, } +api_container_environment = { + NEW_GRANT_DETAILS_PAGE_ENABLED = true, +} // Postgres postgres_enabled = true From ec4131a2e960dcaf5241fe63676777302752555e Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Fri, 1 Mar 2024 12:28:52 -0800 Subject: [PATCH 3/8] Add tests --- packages/server/__tests__/email/email.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/server/__tests__/email/email.test.js b/packages/server/__tests__/email/email.test.js index 360d4a360..781239ce3 100644 --- a/packages/server/__tests__/email/email.test.js +++ b/packages/server/__tests__/email/email.test.js @@ -417,6 +417,21 @@ describe('Email sender', () => { expect(body).to.include(name); expect(body).to.include(moment(openDate).format('MMMM Do YYYY')); }); + it('links to Grants.gov when Grant Details page is not live', async () => { + const agencies = await db.getAgency(fixtures.agencies.accountancy.id); + const agency = agencies[0]; + agency.matched_grants = [fixtures.grants.healthAide]; + const body = await email.buildDigestBody({ name: 'Saved search test', openDate: '2021-08-05', matchedGrants: agency.matched_grants }); + expect(body).to.include(`https://www.grants.gov/search-results-detail/${fixtures.grants.healthAide.grant_id}`.replaceAll('/', '/')); + }); + it('links to Grant Finder when Grant Details page is live', async () => { + process.env.NEW_GRANT_DETAILS_PAGE_ENABLED = 'true'; + const agencies = await db.getAgency(fixtures.agencies.accountancy.id); + const agency = agencies[0]; + agency.matched_grants = [fixtures.grants.healthAide]; + const body = await email.buildDigestBody({ name: 'Saved search test', openDate: '2021-08-05', matchedGrants: agency.matched_grants }); + expect(body).to.include(`${process.env.WEBSITE_DOMAIN}/grant/${fixtures.grants.healthAide.grant_id}`.replaceAll('/', '/')); + }); }); context('getAndSendGrantForSavedSearch', () => { it('Sends an email for a saved search', async () => { From 6bb119256990e8a00409f3a1cf9a4ef658478430 Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Sat, 2 Mar 2024 10:44:31 -0800 Subject: [PATCH 4/8] Fix terraform fmt issues --- terraform/prod.tfvars | 2 +- terraform/staging.tfvars | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/prod.tfvars b/terraform/prod.tfvars index e9366a88e..8c54b432f 100644 --- a/terraform/prod.tfvars +++ b/terraform/prod.tfvars @@ -73,7 +73,7 @@ api_datadog_environment_variables = { DD_PROFILING_ENABLED = true, } api_container_environment = { - NEW_GRANT_DETAILS_PAGE_ENABLED = false, + NEW_GRANT_DETAILS_PAGE_ENABLED = false, } // Postgres diff --git a/terraform/staging.tfvars b/terraform/staging.tfvars index 3d8c0ef14..b458f9e62 100644 --- a/terraform/staging.tfvars +++ b/terraform/staging.tfvars @@ -67,7 +67,7 @@ api_datadog_environment_variables = { DD_PROFILING_ENABLED = true, } api_container_environment = { - NEW_GRANT_DETAILS_PAGE_ENABLED = true, + NEW_GRANT_DETAILS_PAGE_ENABLED = true, } // Postgres From 900fe87d204fca66c19769b6e74050ca2da39b18 Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Thu, 7 Mar 2024 11:05:09 -0800 Subject: [PATCH 5/8] Update grant detail page URL --- packages/server/__tests__/email/email.test.js | 2 +- packages/server/src/lib/email.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/__tests__/email/email.test.js b/packages/server/__tests__/email/email.test.js index 781239ce3..1fa7c8f58 100644 --- a/packages/server/__tests__/email/email.test.js +++ b/packages/server/__tests__/email/email.test.js @@ -430,7 +430,7 @@ describe('Email sender', () => { const agency = agencies[0]; agency.matched_grants = [fixtures.grants.healthAide]; const body = await email.buildDigestBody({ name: 'Saved search test', openDate: '2021-08-05', matchedGrants: agency.matched_grants }); - expect(body).to.include(`${process.env.WEBSITE_DOMAIN}/grant/${fixtures.grants.healthAide.grant_id}`.replaceAll('/', '/')); + expect(body).to.include(`${process.env.WEBSITE_DOMAIN}/grants/${fixtures.grants.healthAide.grant_id}`.replaceAll('/', '/')); }); }); context('getAndSendGrantForSavedSearch', () => { diff --git a/packages/server/src/lib/email.js b/packages/server/src/lib/email.js index cf2fb3852..799c109f1 100644 --- a/packages/server/src/lib/email.js +++ b/packages/server/src/lib/email.js @@ -154,7 +154,7 @@ function sendWelcomeEmail(email, httpOrigin) { function buildGrantDetailUrl(grantId, emailNotificationType) { const grantDetailUrl = new URL(process.env.WEBSITE_DOMAIN); - grantDetailUrl.pathname = `grant/${grantId}`; + grantDetailUrl.pathname = `grants/${grantId}`; grantDetailUrl.searchParams.set('utm_source', 'usdr-grants'); grantDetailUrl.searchParams.set('utm_medium', 'email'); grantDetailUrl.searchParams.set('utm_campaign', emailNotificationType); From 3f9d39c6e230243ad14994fd42163ae119780b1a Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Fri, 8 Mar 2024 09:28:16 -0800 Subject: [PATCH 6/8] Fix escaping of href attributes --- packages/server/src/lib/email.js | 22 +++++++++---------- .../static/email_templates/_grant_detail.html | 10 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/server/src/lib/email.js b/packages/server/src/lib/email.js index 799c109f1..b4a5dad70 100644 --- a/packages/server/src/lib/email.js +++ b/packages/server/src/lib/email.js @@ -152,17 +152,17 @@ function sendWelcomeEmail(email, httpOrigin) { }); } -function buildGrantDetailUrl(grantId, emailNotificationType) { +function buildGrantDetailUrlSafe(grantId, emailNotificationType) { const grantDetailUrl = new URL(process.env.WEBSITE_DOMAIN); - grantDetailUrl.pathname = `grants/${grantId}`; + grantDetailUrl.pathname = `grants/${mustache.escape(grantId)}`; grantDetailUrl.searchParams.set('utm_source', 'usdr-grants'); grantDetailUrl.searchParams.set('utm_medium', 'email'); - grantDetailUrl.searchParams.set('utm_campaign', emailNotificationType); + grantDetailUrl.searchParams.set('utm_campaign', mustache.escape(emailNotificationType)); grantDetailUrl.searchParams.set('utm_content', 'grant-details'); - return grantDetailUrl; + return grantDetailUrl.toString(); } -function buildGrantsUrl(emailNotificationType) { +function buildGrantsUrlSafe(emailNotificationType) { const grantsUrl = new URL(process.env.WEBSITE_DOMAIN); if (emailNotificationType === notificationType.grantDigest) { grantsUrl.pathname = 'grants'; @@ -171,8 +171,8 @@ function buildGrantsUrl(emailNotificationType) { } grantsUrl.searchParams.set('utm_source', 'subscription'); grantsUrl.searchParams.set('utm_medium', 'email'); - grantsUrl.searchParams.set('utm_campaign', emailNotificationType); - return grantsUrl; + grantsUrl.searchParams.set('utm_campaign', mustache.escape(emailNotificationType)); + return grantsUrl.toString(); } function getGrantDetail(grant, emailNotificationType) { @@ -190,13 +190,13 @@ function getGrantDetail(grant, emailNotificationType) { award_ceiling: grant.award_ceiling || 'Not available', // estimated_funding: grant.estimated_funding, TODO: add once field is available in the database. cost_sharing: grant.cost_sharing, - link_url: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' - ? buildGrantDetailUrl(grant.grant_id, notificationType).toString() - : `https://www.grants.gov/search-results-detail/${grant.grant_id}`, + link_url_safe: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' + ? buildGrantDetailUrlSafe(grant.grant_id, notificationType) + : `https://www.grants.gov/search-results-detail/${mustache.escape(grant.grant_id)}`, link_description: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' ? 'Grant Finder' : 'Grants.gov', - grants_url: buildGrantsUrl(notificationType).toString(), + grants_url_safe: buildGrantsUrlSafe(notificationType), view_grant_label: emailNotificationType === notificationType.grantDigest ? undefined : 'View My Grants', }, ); diff --git a/packages/server/src/static/email_templates/_grant_detail.html b/packages/server/src/static/email_templates/_grant_detail.html index 0abc083ba..82506e575 100644 --- a/packages/server/src/static/email_templates/_grant_detail.html +++ b/packages/server/src/static/email_templates/_grant_detail.html @@ -19,7 +19,7 @@ - +

{{title}}

@@ -31,7 +31,7 @@ style="Margin:0;padding-bottom:16px;">

- {{{description}}}... View on {{ link_description }} + {{{description}}}... View on {{ link_description }}

@@ -60,8 +60,8 @@ - {{view_grant_label}} From 5ab792c0939be87b8601a666390e6b019020ef1d Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Fri, 8 Mar 2024 09:45:41 -0800 Subject: [PATCH 7/8] Fix to use correct notification type value --- packages/server/src/lib/email.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/lib/email.js b/packages/server/src/lib/email.js index b4a5dad70..9570a8d3a 100644 --- a/packages/server/src/lib/email.js +++ b/packages/server/src/lib/email.js @@ -191,12 +191,12 @@ function getGrantDetail(grant, emailNotificationType) { // estimated_funding: grant.estimated_funding, TODO: add once field is available in the database. cost_sharing: grant.cost_sharing, link_url_safe: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' - ? buildGrantDetailUrlSafe(grant.grant_id, notificationType) + ? buildGrantDetailUrlSafe(grant.grant_id, emailNotificationType) : `https://www.grants.gov/search-results-detail/${mustache.escape(grant.grant_id)}`, link_description: process.env.NEW_GRANT_DETAILS_PAGE_ENABLED === 'true' ? 'Grant Finder' : 'Grants.gov', - grants_url_safe: buildGrantsUrlSafe(notificationType), + grants_url_safe: buildGrantsUrlSafe(emailNotificationType), view_grant_label: emailNotificationType === notificationType.grantDigest ? undefined : 'View My Grants', }, ); From 583932afc591e91178df417651da3cec5205f760 Mon Sep 17 00:00:00 2001 From: Jeff Mohan Date: Fri, 8 Mar 2024 09:52:36 -0800 Subject: [PATCH 8/8] Fix the tests that should've been a tipoff something was wrong to begin with...... :P --- packages/server/__tests__/email/email.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/__tests__/email/email.test.js b/packages/server/__tests__/email/email.test.js index 1fa7c8f58..ee31fab54 100644 --- a/packages/server/__tests__/email/email.test.js +++ b/packages/server/__tests__/email/email.test.js @@ -422,7 +422,7 @@ describe('Email sender', () => { const agency = agencies[0]; agency.matched_grants = [fixtures.grants.healthAide]; const body = await email.buildDigestBody({ name: 'Saved search test', openDate: '2021-08-05', matchedGrants: agency.matched_grants }); - expect(body).to.include(`https://www.grants.gov/search-results-detail/${fixtures.grants.healthAide.grant_id}`.replaceAll('/', '/')); + expect(body).to.include(`https://www.grants.gov/search-results-detail/${fixtures.grants.healthAide.grant_id}`); }); it('links to Grant Finder when Grant Details page is live', async () => { process.env.NEW_GRANT_DETAILS_PAGE_ENABLED = 'true'; @@ -430,7 +430,7 @@ describe('Email sender', () => { const agency = agencies[0]; agency.matched_grants = [fixtures.grants.healthAide]; const body = await email.buildDigestBody({ name: 'Saved search test', openDate: '2021-08-05', matchedGrants: agency.matched_grants }); - expect(body).to.include(`${process.env.WEBSITE_DOMAIN}/grants/${fixtures.grants.healthAide.grant_id}`.replaceAll('/', '/')); + expect(body).to.include(`${process.env.WEBSITE_DOMAIN}/grants/${fixtures.grants.healthAide.grant_id}`); }); }); context('getAndSendGrantForSavedSearch', () => {