-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
265 additions
and
255 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,19 +2,20 @@ import { AlertRecord, EmailConfig } from "../index.ts"; | |
import { Recipient } from "../template.ts"; | ||
import { commonTemplate } from "../template.ts"; | ||
|
||
interface DelinquentTenantArguments { | ||
interface FreeTrialStalledArguments { | ||
tenant: string; | ||
// This feels like it should apply to all alert types, and doesn't belong here.. | ||
recipients: Recipient[]; | ||
trial_start: string; | ||
trial_end: string; | ||
tenant: string; | ||
} | ||
|
||
type DelinquentTenantRecord = AlertRecord<"delinquent_tenant", DelinquentTenantArguments>; | ||
type FreeTrialStalledRecord = AlertRecord<"free_trial_stalled", FreeTrialStalledArguments>; | ||
|
||
const delinquentTenant = (req: DelinquentTenantRecord, started: boolean): EmailConfig[] => { | ||
// This alert only fires if they don't have a CC entered and they're >=5 days after the end of their trial | ||
// So this alert resolving implicitly means they entered a CC. | ||
const freeTrialStalled = (req: FreeTrialStalledRecord, started: boolean): EmailConfig[] => { | ||
return req.arguments.recipients.map((recipient) => ({ | ||
// emails: [recipient.email], | ||
// TODO(jshearer): Remove [email protected] after testing | ||
emails: ["[email protected]", "[email protected]"], | ||
subject: `Free Tier Grace Period for ${req.arguments.tenant}: ${started ? "No CC 💳❌" : "CC Entered 💳✅"}`, | ||
|
@@ -31,6 +32,6 @@ const delinquentTenant = (req: DelinquentTenantRecord, started: boolean): EmailC | |
})); | ||
}; | ||
|
||
export const delinquentTenantEmail = (request: DelinquentTenantRecord): EmailConfig[] => { | ||
return delinquentTenant(request, request.resolved_at !== null); | ||
export const freeTrialStalledEmail = (request: FreeTrialStalledRecord): EmailConfig[] => { | ||
return freeTrialStalled(request, request.resolved_at !== null); | ||
}; |
73 changes: 73 additions & 0 deletions
73
supabase/functions/alerts/alert_types/missing_payment_method.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { AlertRecord, EmailConfig } from "../index.ts"; | ||
import { commonTemplate, Recipient } from "../template.ts"; | ||
|
||
interface MissingPaymentMethodArguments { | ||
// This feels like it should apply to all alert types, and doesn't belong here.. | ||
recipients: Recipient[]; | ||
trial_start: string; | ||
trial_end: string; | ||
tenant: string; | ||
plan_state: "free_tier" | "free_trial" | "paid"; | ||
} | ||
|
||
const faq = [ | ||
{ | ||
question: "Where is my data stored?", | ||
answer: "By default, all collection data is stored in an Estuary-owned cloud storage bucket" + | ||
"with a 30 day retention plicy. Now that you have a paid account, you can update this " + | ||
"to store data in your own cloud storage bucket. We support GCS, S3, and Azure Blob storage.", | ||
}, | ||
{ | ||
question: "How can I access Estuary Support?", | ||
answer: "Reach out to [email protected] or join our slack.", | ||
}, | ||
{ | ||
question: "Is it possible to schedule data flows?", | ||
answer: "Estuary moves most data in real-time by default, without the need for scheduling, " + | ||
"but you can add “update delays” to data warehouses to enable more downtime on your " + | ||
"warehouse for cost savings. This can be enabled under “advanced settings” and default " + | ||
"settings are 30 minutes for a warehouse.", | ||
}, | ||
]; | ||
|
||
type MissingPaymentRecord = AlertRecord<"missing_payment_method", MissingPaymentMethodArguments>; | ||
|
||
const paymentMethodProvided = (req: MissingPaymentRecord): EmailConfig[] => { | ||
return req.arguments.recipients.map((recipient) => ({ | ||
emails: [recipient.email], | ||
subject: `Estuary: Thanks for Adding a Payment Method🎉`, | ||
content: commonTemplate( | ||
` | ||
<mj-text font-size="17px">We hope you are enjoying Estuary Flow. We have received your payment method for your account <a class="identifier">${req.arguments.tenant}</a>. ${ | ||
req.arguments.plan_state === "free_trial" | ||
? `After your free trial ends on <strong>${req.arguments.trial_end}</strong>, you will automatically be switched the paid tier.` | ||
: `you are now on the paid tier.` | ||
}</mj-text> | ||
<mj-button href="https://dashboard.estuary.dev/admin/billing">📈 See your bill</mj-button> | ||
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" padding-top="40px" padding-bottom="10px" /> | ||
<mj-text align="center" font-weight="bold" font-size="22px">Frequently Asked Questions</mj-text> | ||
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" padding-bottom="20px" /> | ||
${ | ||
faq.map(({ question, answer }) => ` | ||
<mj-text font-weight="bold" font-size="19px">${question}</mj-text> | ||
<mj-text font-size="17px">${answer}</mj-text> | ||
`).join("\n") | ||
} | ||
`, | ||
recipient, | ||
), | ||
})); | ||
}; | ||
|
||
export const missingPaymentMethodEmail = (request: MissingPaymentRecord): EmailConfig[] => { | ||
// We should only send an email on the trailing edge of this alert, i.e | ||
// "payment method is no longer missing" | ||
if (request.resolved_at) { | ||
return paymentMethodProvided(request); | ||
} else { | ||
// Maaaaaybe we want to send an email here on tenant creation that says something like | ||
// "Welcome to Estuary! You're on the free tier, but you'll need to add a payment method to continue using the platform after your trial ends." | ||
return []; | ||
} | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
import { serve } from "https://deno.land/[email protected]/http/server.ts"; | ||
|
||
import { corsHeaders } from "../_shared/cors.ts"; | ||
import { dataNotProcessedInIntervalEmail } from "./alert_types/data_not_processed_in_interval.ts"; | ||
import { delinquentTenantEmail } from "./alert_types/delinquent_tenant.ts"; | ||
import { dataMovementStalledEmail } from "./alert_types/data_movement_stalled.ts"; | ||
import { freeTrialStalledEmail } from "./alert_types/free_trial_stalled.ts"; | ||
import { freeTrialEndingEmail } from "./alert_types/free_trial_ending.ts"; | ||
import { paidTenantEmail } from "./alert_types/paid_tenant.ts"; | ||
import { missingPaymentMethodEmail } from "./alert_types/missing_payment_method.ts"; | ||
import { freeTrialEmail } from "./alert_types/free_trial.ts"; | ||
|
||
export interface AlertRecord<T extends keyof typeof emailTemplates, A> { | ||
|
@@ -22,11 +22,11 @@ export interface EmailConfig { | |
} | ||
|
||
const emailTemplates = { | ||
"delinquent_tenant": delinquentTenantEmail, | ||
"free_trial_ending": freeTrialEndingEmail, | ||
"free_trial": freeTrialEmail, | ||
"paid_tenant": paidTenantEmail, | ||
"data_not_processed_in_interval_v2": dataNotProcessedInIntervalEmail, | ||
"free_trial_ending": freeTrialEndingEmail, | ||
"free_trial_stalled": freeTrialStalledEmail, | ||
"missing_payment_method": missingPaymentMethodEmail, | ||
"data_movement_stalled": dataMovementStalledEmail, | ||
}; | ||
|
||
// This is a temporary type guard for the POST request that provides shallow validation | ||
|
@@ -144,19 +144,19 @@ serve(async (rawRequest: Request): Promise<Response> => { | |
// templates have different arguments, that looks like (never)=>EmailConfig[]. | ||
// [1]: https://github.com/microsoft/TypeScript/issues/30581 | ||
switch (request.alert_type) { | ||
case "data_not_processed_in_interval_v2": | ||
case "free_trial": | ||
pendingEmails = emailTemplates[request.alert_type](request); | ||
break; | ||
case "free_trial": | ||
case "free_trial_ending": | ||
pendingEmails = emailTemplates[request.alert_type](request); | ||
break; | ||
case "paid_tenant": | ||
case "free_trial_stalled": | ||
pendingEmails = emailTemplates[request.alert_type](request); | ||
break; | ||
case "free_trial_ending": | ||
case "missing_payment_method": | ||
pendingEmails = emailTemplates[request.alert_type](request); | ||
break; | ||
case "delinquent_tenant": | ||
case "data_movement_stalled": | ||
pendingEmails = emailTemplates[request.alert_type](request); | ||
break; | ||
default: { | ||
|
Oops, something went wrong.