Skip to content

Commit

Permalink
Only fetch scans/breaches with flag enabled
Browse files Browse the repository at this point in the history
In case they cause too much load, we can easily disable the flag
again.
  • Loading branch information
Vinnl committed Sep 17, 2024
1 parent 45eb43a commit 76eda09
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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",
),
<BreachAlertEmail
subscriber={subscriber}
breach={createRandomHibpListing()}
breachedEmail={emailAddress}
allSubscriberBreaches={allSubscriberBreaches}
scanData={scanData}
enabledFeatureFlags={
options.redesign === true ? ["BreachEmailRedesign"] : []
}
utmCampaignId="breach-alert"
l10n={l10n}
/>,
);
options.redesign === true
? await send(
emailAddress,
l10n.getString("email-breach-alert-all-subject"),
<RedesignedBreachAlertEmail
subscriber={subscriber}
breach={createRandomHibpListing()}
breachedEmail={emailAddress}
allSubscriberBreaches={allSubscriberBreaches}
scanData={scanData}
enabledFeatureFlags={["BreachEmailRedesign"]}
utmCampaignId="breach-alert"
l10n={l10n}
/>,
)
: await send(
emailAddress,
l10n.getString("breach-alert-subject"),
<BreachAlertEmail
subscriber={subscriber}
breach={createRandomHibpListing()}
breachedEmail={emailAddress}
utmCampaignId="breach-alert"
l10n={l10n}
/>,
);
}

export async function triggerFirstDataBrokerRemovalFixed(emailAddress: string) {
Expand Down
13 changes: 8 additions & 5 deletions src/emails/templates/breachAlert/BreachAlertEmail.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -14,11 +17,11 @@ import {
createRandomScanResult,
} from "../../../apiMocks/mockData";

const meta: Meta<FC<Props>> = {
const meta: Meta<FC<RedesignedBreachAlertEmailProps>> = {
title: "Emails/Breach alert",
component: (props: Props) => (
component: (props: RedesignedBreachAlertEmailProps) => (
<StorybookEmailRenderer>
<BreachAlertEmail {...props} />
<RedesignedBreachAlertEmail {...props} />
</StorybookEmailRenderer>
),
args: {
Expand All @@ -39,7 +42,7 @@ const meta: Meta<FC<Props>> = {
};

export default meta;
type Story = StoryObj<FC<Props>>;
type Story = StoryObj<FC<RedesignedBreachAlertEmailProps>>;

export const BreachAlertEmailStory: Story = {
name: "Breach alert",
Expand Down
30 changes: 21 additions & 9 deletions src/emails/templates/breachAlert/BreachAlertEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <RedesignedBreachAlertEmail {...props} />;
}

const l10n = props.l10n;

return (
Expand Down Expand Up @@ -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 <BreachAlertEmail {...props} />;
}

const l10n = props.l10n;
const locale = getLocale(props.l10n);
const listFormatter = new Intl.ListFormat(locale);
Expand Down Expand Up @@ -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;
}
Expand Down
110 changes: 64 additions & 46 deletions src/scripts/cronjobs/emailBreachAlerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(
<RedesignedBreachAlertEmail
l10n={l10n}
breach={breachAlert}
breachedEmail={breachedEmail}
allSubscriberBreaches={allSubscriberBreaches}
utmCampaignId={utmCampaignId}
enabledFeatureFlags={enabledFeatureFlags}
subscriber={recipient}
scanData={scanData}
/>,
),
);
} else {
const subject = l10n.getString("breach-alert-subject");
await sendEmail(
recipientEmail,
subject,
renderEmail(
<BreachAlertEmail
l10n={l10n}
breach={breachAlert}
breachedEmail={breachedEmail}
utmCampaignId={utmCampaignId}
subscriber={recipient}
/>,
),
);
}
const scanData = await getLatestOnerepScanResults(
recipient.onerep_profile_id,
);
const allSubscriberBreaches = await getSubscriberBreaches({
fxaUid: recipient.fxa_uid,
countryCode: assumedCountryCode,
});

await sendEmail(
recipientEmail,
subject,
renderEmail(
<BreachAlertEmail
l10n={l10n}
breach={breachAlert}
breachedEmail={breachedEmail}
allSubscriberBreaches={allSubscriberBreaches}
utmCampaignId={utmCampaignId}
enabledFeatureFlags={enabledFeatureFlags}
subscriber={recipient}
scanData={scanData}
/>,
),
);
} catch (e) {
console.error("Failed to add email notification to table: ", e);
setTimeout(process.exit, 1000);
Expand Down

0 comments on commit 76eda09

Please sign in to comment.