From 76eda0969cebf4f0e58893ec1fd0ca53e2b69873 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 9 Sep 2024 12:59:46 +0200 Subject: [PATCH] Only fetch scans/breaches with flag enabled In case they cause too much load, we can easily disable the flag again. --- .../(authenticated)/admin/emails/actions.tsx | 51 ++++---- .../breachAlert/BreachAlertEmail.stories.tsx | 13 ++- .../breachAlert/BreachAlertEmail.tsx | 30 +++-- src/scripts/cronjobs/emailBreachAlerts.tsx | 110 ++++++++++-------- 4 files changed, 123 insertions(+), 81 deletions(-) diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/admin/emails/actions.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/admin/emails/actions.tsx index fd275c33365..ea72176dde6 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/admin/emails/actions.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/admin/emails/actions.tsx @@ -27,7 +27,10 @@ import { createRandomHibpListing, createRandomScanResult, } from "../../../../../../apiMocks/mockData"; -import { BreachAlertEmail } from "../../../../../../emails/templates/breachAlert/BreachAlertEmail"; +import { + BreachAlertEmail, + RedesignedBreachAlertEmail, +} from "../../../../../../emails/templates/breachAlert/BreachAlertEmail"; import { SignupReportEmail } from "../../../../../../emails/templates/signupReport/SignupReportEmail"; import { getBreachesForEmail } from "../../../../../../utils/hibp"; import { getSha1 } from "../../../../../../utils/fxa"; @@ -186,26 +189,32 @@ export async function triggerBreachAlert( countryCode: assumedCountryCode, }); - await send( - emailAddress, - l10n.getString( - options.redesign === true - ? "email-breach-alert-all-subject" - : "breach-alert-subject", - ), - , - ); + options.redesign === true + ? await send( + emailAddress, + l10n.getString("email-breach-alert-all-subject"), + , + ) + : await send( + emailAddress, + l10n.getString("breach-alert-subject"), + , + ); } export async function triggerFirstDataBrokerRemovalFixed(emailAddress: string) { diff --git a/src/emails/templates/breachAlert/BreachAlertEmail.stories.tsx b/src/emails/templates/breachAlert/BreachAlertEmail.stories.tsx index 85a4f23689e..83e407a661d 100644 --- a/src/emails/templates/breachAlert/BreachAlertEmail.stories.tsx +++ b/src/emails/templates/breachAlert/BreachAlertEmail.stories.tsx @@ -5,7 +5,10 @@ import type { Meta, StoryObj } from "@storybook/react"; import { FC } from "react"; import type { SubscriberRow, OnerepScanRow } from "knex/types/tables"; -import { Props, BreachAlertEmail } from "./BreachAlertEmail"; +import { + RedesignedBreachAlertEmailProps, + RedesignedBreachAlertEmail, +} from "./BreachAlertEmail"; import { StorybookEmailRenderer } from "../../StorybookEmailRenderer"; import { getL10n } from "../../../app/functions/l10n/storybookAndJest"; import { @@ -14,11 +17,11 @@ import { createRandomScanResult, } from "../../../apiMocks/mockData"; -const meta: Meta> = { +const meta: Meta> = { title: "Emails/Breach alert", - component: (props: Props) => ( + component: (props: RedesignedBreachAlertEmailProps) => ( - + ), args: { @@ -39,7 +42,7 @@ const meta: Meta> = { }; export default meta; -type Story = StoryObj>; +type Story = StoryObj>; export const BreachAlertEmailStory: Story = { name: "Breach alert", diff --git a/src/emails/templates/breachAlert/BreachAlertEmail.tsx b/src/emails/templates/breachAlert/BreachAlertEmail.tsx index fd01c99dc59..176de7ac19e 100644 --- a/src/emails/templates/breachAlert/BreachAlertEmail.tsx +++ b/src/emails/templates/breachAlert/BreachAlertEmail.tsx @@ -27,16 +27,9 @@ export type Props = { breachedEmail: string; utmCampaignId: string; subscriber: SubscriberRow; - scanData: LatestOnerepScanData; - allSubscriberBreaches: SubscriberBreach[]; - enabledFeatureFlags: FeatureFlagName[]; }; export const BreachAlertEmail = (props: Props) => { - if (props.enabledFeatureFlags.includes("BreachEmailRedesign")) { - return ; - } - const l10n = props.l10n; return ( @@ -76,11 +69,28 @@ export const BreachAlertEmail = (props: Props) => { ); }; +export type RedesignedBreachAlertEmailProps = { + l10n: ExtendedReactLocalization; + breach: HibpLikeDbBreach; + breachedEmail: string; + utmCampaignId: string; + subscriber: SubscriberRow; + scanData: LatestOnerepScanData; + allSubscriberBreaches: SubscriberBreach[]; + enabledFeatureFlags: FeatureFlagName[]; +}; + // These components are fully covered by the BreachAlertEmail test, // but for some reason get marked as uncovered again once the // `src/scripts/cronjobs/emailBreachAlerts.test.ts` tests are run: /* c8 ignore start */ -const RedesignedBreachAlertEmail = (props: Props) => { +export const RedesignedBreachAlertEmail = ( + props: RedesignedBreachAlertEmailProps, +) => { + if (!props.enabledFeatureFlags.includes("BreachEmailRedesign")) { + return ; + } + const l10n = props.l10n; const locale = getLocale(props.l10n); const listFormatter = new Intl.ListFormat(locale); @@ -315,7 +325,9 @@ const Banner = (props: { ); }; -const DataPointCount = (props: Props & { utmContentSuffix: string }) => { +const DataPointCount = ( + props: RedesignedBreachAlertEmailProps & { utmContentSuffix: string }, +) => { if (props.scanData.scan === null) { return null; } diff --git a/src/scripts/cronjobs/emailBreachAlerts.tsx b/src/scripts/cronjobs/emailBreachAlerts.tsx index e0e12c50b1b..2f3bebe5fba 100644 --- a/src/scripts/cronjobs/emailBreachAlerts.tsx +++ b/src/scripts/cronjobs/emailBreachAlerts.tsx @@ -34,7 +34,10 @@ import { knexHibp, } from "../../utils/hibp"; import { renderEmail } from "../../emails/renderEmail"; -import { BreachAlertEmail } from "../../emails/templates/breachAlert/BreachAlertEmail"; +import { + BreachAlertEmail, + RedesignedBreachAlertEmail, +} from "../../emails/templates/breachAlert/BreachAlertEmail"; import { getCronjobL10n } from "../../app/functions/l10n/cronjobs"; import { sanitizeSubscriberRow } from "../../app/functions/server/sanitize"; import { getEnabledFeatureFlags } from "../../db/tables/featureFlags"; @@ -268,52 +271,67 @@ export async function poll( const enabledFeatureFlags = await getEnabledFeatureFlags({ email: recipient.primary_email, }); - const subject = enabledFeatureFlags.includes( - "BreachEmailRedesign", - ) - ? l10n.getString("email-breach-alert-all-subject") - : l10n.getString("breach-alert-subject"); - - /** - * Without an active user session, we don't know the user's country. This is - * our best guess based on their locale. At the time of writing, it's only - * used to determine whether to count SSN breaches (which we don't have - * recommendations for outside the US). - */ - const assumedCountryCode = getSignupLocaleCountry(recipient); - - // The unit tests are currently too complex for me to write - // a proper test for this, and I need to understand the code - // better to be able to refactor it to make it more amenable - // to simple tests. Hence, I don't have a test for this yet: - /* c8 ignore next 3 */ - if (typeof recipient.onerep_profile_id === "number") { - await refreshStoredScanResults(recipient.onerep_profile_id); + if (enabledFeatureFlags.includes("BreachEmailRedesign")) { + /** + * Without an active user session, we don't know the user's country. This is + * our best guess based on their locale. At the time of writing, it's only + * used to determine whether to count SSN breaches (which we don't have + * recommendations for outside the US). + */ + const assumedCountryCode = getSignupLocaleCountry(recipient); + + // The unit tests are currently too complex for me to write + // a proper test for this, and I need to understand the code + // better to be able to refactor it to make it more amenable + // to simple tests. Hence, I don't have a test for this yet: + /* c8 ignore next 3 */ + if (typeof recipient.onerep_profile_id === "number") { + await refreshStoredScanResults(recipient.onerep_profile_id); + } + const scanData = await getLatestOnerepScanResults( + recipient.onerep_profile_id, + ); + const allSubscriberBreaches = await getSubscriberBreaches({ + fxaUid: recipient.fxa_uid, + countryCode: assumedCountryCode, + }); + + const subject = l10n.getString( + "email-breach-alert-all-subject", + ); + + await sendEmail( + recipientEmail, + subject, + renderEmail( + , + ), + ); + } else { + const subject = l10n.getString("breach-alert-subject"); + await sendEmail( + recipientEmail, + subject, + renderEmail( + , + ), + ); } - const scanData = await getLatestOnerepScanResults( - recipient.onerep_profile_id, - ); - const allSubscriberBreaches = await getSubscriberBreaches({ - fxaUid: recipient.fxa_uid, - countryCode: assumedCountryCode, - }); - - await sendEmail( - recipientEmail, - subject, - renderEmail( - , - ), - ); } catch (e) { console.error("Failed to add email notification to table: ", e); setTimeout(process.exit, 1000);