Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use single email Mailersend endpoint for auth emails #3346

Merged
Merged
Show file tree
Hide file tree
Changes from 112 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
bc882b0
Add testing and frontend instructions
cesarvarela Nov 6, 2024
1b9b14f
Update README to clarify GraphQL API access and endpoints
cesarvarela Nov 6, 2024
97ec722
Add database migration instructions to README files
cesarvarela Nov 6, 2024
1251405
Move additional configuration section
cesarvarela Nov 6, 2024
14b1848
Move style guid
cesarvarela Nov 6, 2024
84459b1
Move prismic instructions
cesarvarela Nov 6, 2024
ed151be
Delete outdated basic setup instructions
cesarvarela Nov 6, 2024
8827c11
Delete outdated netlify instructions
cesarvarela Nov 6, 2024
6b63ad8
Delete error loging from main readme
cesarvarela Nov 6, 2024
457fcdc
Delete old social networks instructions
cesarvarela Nov 6, 2024
12dc477
Cleanup sample env file
cesarvarela Nov 6, 2024
8db169c
Move github workflow instructions
cesarvarela Nov 6, 2024
8f7be31
Add mailersend and cloudflare instructions
cesarvarela Nov 6, 2024
9537f20
Reorganize env vars
cesarvarela Nov 6, 2024
5646c2d
Update graphql endpoint instructions
cesarvarela Nov 6, 2024
7470add
Move netlify instructions
cesarvarela Nov 6, 2024
8040bd6
Fix prismic section headings
cesarvarela Nov 6, 2024
7330ac3
Set-up next auth server side
cesarvarela Nov 14, 2024
6a9d6ef
Update components
cesarvarela Nov 14, 2024
1b27150
Convert user context to typescript and remove Atlas calls
cesarvarela Nov 14, 2024
b6c5921
Add custom pages
cesarvarela Nov 15, 2024
1e5856c
Update typescript (jest)
cesarvarela Nov 15, 2024
ea18305
Update tests
cesarvarela Nov 15, 2024
06932eb
Allow mocking user sessions
cesarvarela Nov 15, 2024
fa727db
update command
cesarvarela Nov 15, 2024
fb90749
Update test files
cesarvarela Nov 15, 2024
6acf23d
Merge staging
cesarvarela Nov 19, 2024
0e8df00
update package lock
cesarvarela Nov 19, 2024
351f6a9
Remove unused function
cesarvarela Nov 19, 2024
2ada3ec
Fix wrong seesion fetch function
cesarvarela Nov 20, 2024
16e29c5
Update tests
cesarvarela Nov 20, 2024
0f89fdf
tmp add new variables
cesarvarela Nov 20, 2024
0e7f5c7
Add missing configs
cesarvarela Nov 20, 2024
430f7d9
Add missing variables
cesarvarela Nov 20, 2024
41b5531
Add missing env var
cesarvarela Nov 20, 2024
401e791
only debug on localhost
cesarvarela Nov 20, 2024
1e03b54
Update tests
cesarvarela Nov 20, 2024
2ec4780
Update tests
cesarvarela Nov 20, 2024
7b95d22
Update session with custom data
cesarvarela Nov 20, 2024
656e1b7
Update tests
cesarvarela Nov 20, 2024
10eb9cb
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Nov 20, 2024
66dff1a
Update tests
cesarvarela Nov 20, 2024
49970fc
Updates
cesarvarela Nov 20, 2024
c8e99a8
Fix test
cesarvarela Nov 20, 2024
82767cc
Update tests
cesarvarela Nov 20, 2024
4006f42
update test
cesarvarela Nov 21, 2024
cecf1f7
Update test
cesarvarela Nov 21, 2024
ba863bb
Update incidents table
cesarvarela Nov 21, 2024
90c0980
Update components
cesarvarela Nov 21, 2024
14dcb27
Update components
cesarvarela Nov 21, 2024
6b5ea6b
Delete unused pages
cesarvarela Nov 23, 2024
ede00f5
Add command to create magic links
cesarvarela Nov 25, 2024
820f74b
Update lockfile
cesarvarela Nov 25, 2024
56d6e85
Use email template for magic links
cesarvarela Nov 25, 2024
135476a
Update signup and login pages
cesarvarela Nov 26, 2024
1f7653d
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Nov 27, 2024
8a66771
Reduce timeouts
cesarvarela Nov 27, 2024
360a09d
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Nov 27, 2024
49fe877
Update tests
cesarvarela Nov 27, 2024
01bc41b
Update tests
cesarvarela Nov 27, 2024
f943244
Update test and add algolia mock
cesarvarela Nov 28, 2024
b4b6133
Update tests
cesarvarela Nov 28, 2024
b4fa1d0
update subscriptions tests
cesarvarela Nov 29, 2024
4c5ce1e
Update seeds
cesarvarela Nov 29, 2024
2cefdc3
Update tests
cesarvarela Nov 29, 2024
d617048
Update tests
cesarvarela Nov 29, 2024
94bb599
set default status invalid
cesarvarela Nov 29, 2024
d7c49db
Update test
cesarvarela Dec 2, 2024
a535b20
Update login
cesarvarela Dec 2, 2024
5d46951
Update workflows
cesarvarela Dec 2, 2024
5056d26
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 2, 2024
4f71a7a
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 3, 2024
99bf234
Add netlify redirect
cesarvarela Dec 3, 2024
f3a4b90
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 3, 2024
4c01ad7
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 3, 2024
7801e15
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 3, 2024
ca46f0c
Add missing wildcard
cesarvarela Dec 3, 2024
25417e9
Remove hardcoded values
cesarvarela Dec 4, 2024
7057e58
debug
cesarvarela Dec 4, 2024
a43a48c
Remove hardcoded url
cesarvarela Dec 4, 2024
45305db
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 5, 2024
5cee07b
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 11, 2024
c4f9a43
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Dec 12, 2024
1a9618a
Do not allow users to login without signing up first
cesarvarela Dec 12, 2024
18b9f24
Use different templates for sign ups and sign ins
cesarvarela Dec 12, 2024
19ef9df
Use local copy of package to fix issues with Jest not supporting ESM …
cesarvarela Dec 13, 2024
740aa01
Move functions to utils file
cesarvarela Dec 13, 2024
65dc408
Remove unused function
cesarvarela Dec 13, 2024
2e1bb7b
Add tests to signin auth methods
cesarvarela Dec 13, 2024
b7bd66d
Fix small issues
cesarvarela Dec 13, 2024
f1da9ce
Remove unused packages
cesarvarela Dec 13, 2024
476bf88
Add next auth sign up tests
cesarvarela Dec 16, 2024
cb909b9
Use encoded value
cesarvarela Dec 16, 2024
4b4f518
Add migration to fetch users from atlas and insert them in the new au…
cesarvarela Dec 16, 2024
5e16d93
Add tests to user creation.
cesarvarela Dec 17, 2024
3060987
Update test to handle new param
cesarvarela Dec 17, 2024
27f8369
Update tests
cesarvarela Dec 17, 2024
a76fe7f
Undo readme changes
cesarvarela Dec 19, 2024
4dde199
Cleanup tsconfigs
cesarvarela Dec 24, 2024
2908019
Add test to session callback
cesarvarela Dec 24, 2024
a15093e
Different copy for login and signup
cesarvarela Dec 24, 2024
d8f5773
Update test
cesarvarela Dec 24, 2024
a65ecf5
Merge remote-tracking branch 'upstream/staging' into feature-magic-links
cesarvarela Jan 27, 2025
e14ec88
Fix tests
cesarvarela Jan 27, 2025
54788db
Use single email send api for auth emails
cesarvarela Jan 28, 2025
d77be0a
Update test script
cesarvarela Jan 28, 2025
f0061bd
Merge remote-tracking branch 'upstream/staging' into feature-email-api
cesarvarela Feb 1, 2025
85b3ccb
update lockfile
cesarvarela Feb 1, 2025
d75ec65
Update call
cesarvarela Feb 1, 2025
b9eac1d
Update notification tests
cesarvarela Feb 3, 2025
f93aec3
Update auth tests
cesarvarela Feb 3, 2025
44e014e
Merge remote-tracking branch 'upstream/staging' into feature-email-api
cesarvarela Feb 3, 2025
7198a07
Add tests to single email function
cesarvarela Feb 4, 2025
0fa142d
Add tests to check for proper send email parameters
cesarvarela Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions site/gatsby-site/nextauth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const getAuthConfig = async (req: any): Promise<NextAuthOptions> => {
if (user) {

await sendEmail({
recipients: [{ email }],
recipient: { email },
subject: 'Secure link to log in to AIID',
templateId: 'Login',
dynamicData: { magicLink: url },
Expand All @@ -44,7 +44,7 @@ export const getAuthConfig = async (req: any): Promise<NextAuthOptions> => {
else {

await sendEmail({
recipients: [{ email }],
recipient: { email },
subject: 'Secure link to create your AIID account',
templateId: 'Signup',
dynamicData: { magicLink: url },
Expand Down
67 changes: 65 additions & 2 deletions site/gatsby-site/server/emails/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import assert from "assert";
import { RateLimiter } from "limiter";

interface SendEmailParams {
export interface SendBulkEmailParams {
recipients: {
email: string;
userId?: string;
Expand Down Expand Up @@ -87,7 +87,7 @@
await mailersend.email.sendBulk(emails);
}

export const sendEmail = async ({ recipients, subject, dynamicData, templateId }: SendEmailParams) => {
export const sendBulkEmails = async ({ recipients, subject, dynamicData, templateId }: SendBulkEmailParams) => {

const emailTemplateBody = templates[templateId];

Expand Down Expand Up @@ -135,3 +135,66 @@
throw error;
}
}

export const mailersendSingleSend = async (email: EmailParams) => {

const mailersend = new MailerSend({
apiKey: config.MAILERSEND_API_KEY,
});

assert(email.to.length == 1, 'Email must have exactly one recipient');
assert(!email.cc, 'Should not use the "cc" field');

await mailersend.email.send(email);
}

Check warning on line 149 in site/gatsby-site/server/emails/index.ts

View check run for this annotation

Codecov / codecov/patch

site/gatsby-site/server/emails/index.ts#L140-L149

Added lines #L140 - L149 were not covered by tests

export interface SendEmailParams {
kepae marked this conversation as resolved.
Show resolved Hide resolved
recipient: {
email: string;
userId?: string;
};
subject: string;
dynamicData?: {
magicLink?: string;
};
templateId: string;
}

export const sendEmail = async ({ recipient, subject, dynamicData, templateId }: SendEmailParams) => {

const emailTemplateBody = templates[templateId];

if (!emailTemplateBody) {
throw new Error(`Template not found: ${templateId}`);
}

const personalization = {
email: recipient.email,
data: {
...dynamicData,
email: recipient.email,
userId: recipient.userId,
siteUrl: config.SITE_URL,
}
}

const emailParams = new EmailParams()
.setFrom({ email: config.NOTIFICATIONS_SENDER, name: config.NOTIFICATIONS_SENDER_NAME })
.setTo([new Recipient(recipient.email)])
.setPersonalization([personalization])
.setSubject(subject)
.setHtml(emailTemplateBody);
//TODO: add a text version of the email
// .setText("Greetings from the team, you got this message through MailerSend.");

try {

await mailersendSingleSend(emailParams);

} catch (error: any) {
error.message = `[Send Email]: ${error.message}`;
reporter.error(error);

throw error;
}
}

Check warning on line 200 in site/gatsby-site/server/emails/index.ts

View check run for this annotation

Codecov / codecov/patch

site/gatsby-site/server/emails/index.ts#L164-L200

Added lines #L164 - L200 were not covered by tests
40 changes: 15 additions & 25 deletions site/gatsby-site/server/tests/next-auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,9 @@ describe('Auth', () => {
dynamicData: {
magicLink: expect.stringMatching(/^http:\/\/localhost:8000\/api\/auth\/callback\/http-email\?callbackUrl=http%3A%2F%2Flocalhost%3A8000%2F&token=.+&email=test.user%40incidentdatabase.ai$/),
},
recipients: [
{
email: "[email protected]",
},
],
recipient: {
email: "[email protected]",
},
subject: "Secure link to log in to AIID",
templateId: "Login",

Expand Down Expand Up @@ -189,11 +187,9 @@ describe('Auth', () => {
dynamicData: {
magicLink: expect.stringMatching(/^http:\/\/localhost:8000\/api\/auth\/callback\/http-email\?callbackUrl=http%3A%2F%2Flocalhost%3A8000%2Fsome-path%2Fsome-page&token=.+&email=test.user%40incidentdatabase.ai$/),
},
recipients: [
{
email: "[email protected]",
},
],
recipient: {
email: "[email protected]",
},
subject: "Secure link to log in to AIID",
templateId: "Login",

Expand Down Expand Up @@ -223,11 +219,9 @@ describe('Auth', () => {
dynamicData: {
magicLink: expect.stringMatching(/^http:\/\/localhost:8000\/api\/auth\/callback\/http-email\?callbackUrl=http%3A%2F%2Flocalhost%3A8000%2F&token=.+&email=test.user%40incidentdatabase.ai$/),
},
recipients: [
{
email: "[email protected]",
},
],
recipient: {
email: "[email protected]",
},
subject: "Secure link to create your AIID account",
templateId: "Signup",
});
Expand Down Expand Up @@ -270,11 +264,9 @@ describe('Auth', () => {
dynamicData: {
magicLink: expect.stringMatching(/^http:\/\/localhost:8000\/api\/auth\/callback\/http-email\?callbackUrl=http%3A%2F%2Flocalhost%3A8000%2F&token=.+&email=test.user%40incidentdatabase.ai$/),
},
recipients: [
{
email: "[email protected]",
},
],
recipient: {
email: "[email protected]",
},
subject: "Secure link to log in to AIID",
templateId: "Login",
});
Expand Down Expand Up @@ -313,11 +305,9 @@ describe('Auth', () => {
dynamicData: {
magicLink: expect.stringMatching(/^http:\/\/localhost:8000\/api\/auth\/callback\/http-email\?callbackUrl=http%3A%2F%2Flocalhost%3A8000%2Fsome-path%2Fsome-page&token=.+&email=test.user%40incidentdatabase.ai$/),
},
recipients: [
{
email: "[email protected]",
},
],
recipient: {
email: "[email protected]",
},
subject: "Secure link to create your AIID account",
templateId: "Signup",
});
Expand Down
18 changes: 9 additions & 9 deletions site/gatsby-site/server/tests/notifications.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe(`Notifications`, () => {


mockSession('123');
const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockResolvedValue();
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockResolvedValue();

await processNotifications();

Expand Down Expand Up @@ -149,7 +149,7 @@ describe(`Notifications`, () => {
mockSession('123');
jest.spyOn(common, 'getUserAdminData').mockResolvedValue({ userId: '123', email: '[email protected]' });

const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockResolvedValue();
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockResolvedValue();


const result = await processNotifications();
Expand Down Expand Up @@ -277,7 +277,7 @@ describe(`Notifications`, () => {

mockSession('123');
jest.spyOn(common, 'getUserAdminData').mockResolvedValue({ userId: '123', email: '[email protected]' });
const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockResolvedValue();
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockResolvedValue();

const result = await processNotifications();

Expand Down Expand Up @@ -403,7 +403,7 @@ describe(`Notifications`, () => {

mockSession('123');
jest.spyOn(common, 'getUserAdminData').mockResolvedValue({ userId: '123', email: '[email protected]' });
const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockResolvedValue();
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockResolvedValue();

const result = await processNotifications();

Expand Down Expand Up @@ -523,7 +523,7 @@ describe(`Notifications`, () => {

mockSession('123');
jest.spyOn(common, 'getUserAdminData').mockResolvedValue({ userId: '123', email: '[email protected]' });
const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockResolvedValue();
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockResolvedValue();

const result = await processNotifications();

Expand Down Expand Up @@ -644,7 +644,7 @@ describe(`Notifications`, () => {

mockSession('123');
jest.spyOn(common, 'getUserAdminData').mockResolvedValue({ userId: '123', email: '[email protected]' });
const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockResolvedValue();
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockResolvedValue();

const result = await processNotifications();

Expand Down Expand Up @@ -1294,7 +1294,7 @@ describe(`Notifications`, () => {
}
});

jest.spyOn(emails, 'sendEmail').mockRestore();
jest.spyOn(emails, 'sendBulkEmails').mockRestore();
jest.spyOn(common, 'getUserAdminData').mockResolvedValueOnce({ userId: 'user1', email: '[email protected]' })
jest.spyOn(common, 'getUserAdminData').mockResolvedValueOnce({ userId: 'user2', email: '[email protected]' });

Expand Down Expand Up @@ -1503,7 +1503,7 @@ describe(`Notifications`, () => {
mockSession('123');
jest.spyOn(common, 'getUserAdminData').mockResolvedValue({ userId: '123', email: '[email protected]' });

const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockImplementation(() => {
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockImplementation(() => {
throw new Error('Failed to send email');
});

Expand Down Expand Up @@ -1634,7 +1634,7 @@ describe(`Notifications`, () => {
// No recipients
jest.spyOn(common, 'getUserAdminData').mockResolvedValue(null);

const sendEmailMock = jest.spyOn(emails, 'sendEmail').mockImplementation(() => {
const sendEmailMock = jest.spyOn(emails, 'sendBulkEmails').mockImplementation(() => {
throw new Error('Failed to send email');
});

Expand Down
21 changes: 10 additions & 11 deletions site/gatsby-site/src/scripts/process-notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import config from "../../server/config";
import { Context, DBEntity, DBIncident, DBNotification, DBReport, DBSubscription } from "../../server/interfaces";
import * as reporter from '../../server/reporter';
import { MongoClient } from "mongodb";
import { sendEmail } from "../../server/emails";
import { SendBulkEmailParams, sendBulkEmails } from "../../server/emails";

const usersCache: UserAdminData[] = [];

Expand Down Expand Up @@ -104,8 +104,8 @@ async function notificationsToNewIncidents(context: Context) {
const incident = await incidentsCollection.findOne({ incident_id: pendingNotification.incident_id });

if (incident && recipients.length > 0) {
//Send email notification
const sendEmailParams = {

const sendEmailParams: SendBulkEmailParams = {
recipients,
subject: 'New Incident {{incidentId}} was created',
dynamicData: {
Expand All @@ -122,7 +122,7 @@ async function notificationsToNewIncidents(context: Context) {
templateId: 'NewIncident' // Template value from function name sufix from "site/realm/functions/config.json"
};

await sendEmail(sendEmailParams);
await sendBulkEmails(sendEmailParams);
}


Expand Down Expand Up @@ -203,7 +203,7 @@ async function notificationsToIncidentUpdates(context: Context) {

if (incident && recipients.length > 0) {

const sendEmailParams = {
const sendEmailParams: SendBulkEmailParams = {
recipients,
subject: 'Incident {{incidentId}} was updated',
dynamicData: {
Expand All @@ -218,7 +218,7 @@ async function notificationsToIncidentUpdates(context: Context) {
templateId: newReportNumber ? 'NewReportAddedToAnIncident' : 'IncidentUpdate',
};

await sendEmail(sendEmailParams);
await sendBulkEmails(sendEmailParams);

console.log(`Incident ${incident.incident_id} updates: Pending notification was processed.`);
}
Expand Down Expand Up @@ -294,7 +294,7 @@ async function notificationsToNewEntityIncidents(context: Context) {

if (incident && entity && recipients.length > 0) {

const sendEmailParams = {
const sendEmailParams: SendBulkEmailParams = {
recipients,
subject: isIncidentUpdate ? 'Update Incident for {{entityName}}' : 'New Incident for {{entityName}}',
dynamicData: {
Expand All @@ -314,7 +314,7 @@ async function notificationsToNewEntityIncidents(context: Context) {
templateId: isIncidentUpdate ? 'EntityIncidentUpdated' : 'NewEntityIncident'
};

await sendEmail(sendEmailParams);
await sendBulkEmails(sendEmailParams);

console.log(`New "${entity.name}" Entity Incidents: pending notification was processed.`);
}
Expand Down Expand Up @@ -373,8 +373,7 @@ async function notificationsToNewPromotions(context: Context) {

if (incident && recipients.length > 0) {

//Send email notification
const sendEmailParams = {
const sendEmailParams: SendBulkEmailParams = {
recipients,
subject: 'Your submission has been approved!',
dynamicData: {
Expand All @@ -387,7 +386,7 @@ async function notificationsToNewPromotions(context: Context) {
templateId: 'SubmissionApproved' // Template value from function name sufix from "site/realm/functions/config.json"
};

await sendEmail(sendEmailParams);
await sendBulkEmails(sendEmailParams);
}

} catch (error: any) {
Expand Down
4 changes: 2 additions & 2 deletions site/gatsby-site/src/scripts/sendEmailTest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { sendEmail } from '../../server/emails';
import { sendBulkEmails } from '../../server/emails';

// from site/gatsby-site, run with
// TEST_EMAIL_TO_ADDRESS=<address> dotenv run <path to>/npx ts-node src/scripts/sendEmailTest.js
Expand Down Expand Up @@ -33,5 +33,5 @@ if (!email) {

console.log(JSON.stringify(sendEmailArguments, null, 2));

sendEmail(sendEmailArguments);
sendBulkEmails(sendEmailArguments);
}