From 3d6bc2b6bd24ce03b3207955ee48e5a028238cfc Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Thu, 9 Jan 2025 14:17:32 -0800 Subject: [PATCH 1/2] feat: getter and setter for churn email state --- src/db/tables/subscribers.ts | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/db/tables/subscribers.ts b/src/db/tables/subscribers.ts index a20a061ad96..55b63012d6a 100644 --- a/src/db/tables/subscribers.ts +++ b/src/db/tables/subscribers.ts @@ -551,6 +551,31 @@ async function markFirstDataBrokerRemovalFixedEmailAsJustSent( } /* c8 ignore stop */ + +// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy +/* c8 ignore start */ +async function markChurnPreventionEmailAsJustSent( + subscriberId: SubscriberRow["id"], +) { + const affectedSubscribers = await knex("subscribers") + .update({ + churn_prevention_email_sent: true, + // @ts-ignore knex.fn.now() results in it being set to a date, + // even if it's not typed as a JS date object: + updated_at: knex.fn.now(), + }) + .where("id", subscriberId) + .returning("*"); + + if (affectedSubscribers.length !== 1) { + throw new Error( + `Attempted to mark 1 user as having just been sent the churn prevention email, but instead found [${affectedSubscribers.length}] matching its ID.`, + ); + } +} + +/* c8 ignore stop */ + // Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy /* c8 ignore start */ async function markMonthlyActivityPlusEmailAsJustSent( @@ -669,6 +694,16 @@ async function isSubscriberPlus(subscriberId: SubscriberRow["id"]) { } /* c8 ignore stop */ +// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy +/* c8 ignore start */ +async function getChurnPreventionEmailSent(subscriberId: SubscriberRow["id"]) { + const res = await knex("subscribers") + .select("churn_prevention_email_sent") + .where("id", subscriberId); + return res?.[0]?.["churn_prevention_email_sent"] ?? null; +} +/* c8 ignore stop */ + export { getOnerepProfileId, getSubscribersByHashes, @@ -685,6 +720,7 @@ export { getPotentialSubscribersWaitingForFirstDataBrokerRemovalFixedEmail, getFreeSubscribersWaitingForMonthlyEmail, getPlusSubscribersWaitingForMonthlyEmail, + markChurnPreventionEmailAsJustSent, markFirstDataBrokerRemovalFixedEmailAsJustSent, markMonthlyActivityPlusEmailAsJustSent, deleteUnverifiedSubscribers, @@ -693,6 +729,7 @@ export { deleteOnerepProfileId, incrementSignInCountForEligibleFreeUser, getSignInCount, + getChurnPreventionEmailSent, unresolveAllBreaches, isSubscriberPlus, knex as knexSubscribers, From 16b7f5608bdbdb02a52d389925c8d0ce3ad7fe65 Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 13 Jan 2025 13:25:58 -0800 Subject: [PATCH 2/2] feat: change boolean to date --- src/db/tables/subscribers.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/db/tables/subscribers.ts b/src/db/tables/subscribers.ts index 55b63012d6a..33ea552e0f3 100644 --- a/src/db/tables/subscribers.ts +++ b/src/db/tables/subscribers.ts @@ -559,7 +559,9 @@ async function markChurnPreventionEmailAsJustSent( ) { const affectedSubscribers = await knex("subscribers") .update({ - churn_prevention_email_sent: true, + // @ts-ignore knex.fn.now() results in it being set to a date, + // even if it's not typed as a JS date object: + churn_prevention_email_sent_at: knex.fn.now(), // @ts-ignore knex.fn.now() results in it being set to a date, // even if it's not typed as a JS date object: updated_at: knex.fn.now(), @@ -696,11 +698,13 @@ async function isSubscriberPlus(subscriberId: SubscriberRow["id"]) { // Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy /* c8 ignore start */ -async function getChurnPreventionEmailSent(subscriberId: SubscriberRow["id"]) { +async function getChurnPreventionEmailSentAt( + subscriberId: SubscriberRow["id"], +) { const res = await knex("subscribers") - .select("churn_prevention_email_sent") + .select("churn_prevention_email_sent_at") .where("id", subscriberId); - return res?.[0]?.["churn_prevention_email_sent"] ?? null; + return res?.[0]?.["churn_prevention_email_sent_at"] ?? null; } /* c8 ignore stop */ @@ -729,7 +733,7 @@ export { deleteOnerepProfileId, incrementSignInCountForEligibleFreeUser, getSignInCount, - getChurnPreventionEmailSent, + getChurnPreventionEmailSentAt, unresolveAllBreaches, isSubscriberPlus, knex as knexSubscribers,