Skip to content

Commit

Permalink
Merge pull request #60 from marmelab/feat/mail-multiple-to
Browse files Browse the repository at this point in the history
Feat(mailing): Add support for multiple recipients and fix some typos in mails
  • Loading branch information
slax57 authored Aug 12, 2024
2 parents 0b1ee0b + 49fe6ee commit 67a022c
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 96 deletions.
2 changes: 1 addition & 1 deletion public/auth-callback.html
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
function interceptAuthCallback() {
const hash = window.location.hash;
const {access_token, refresh_token, type} = getUrlParams();
window.location.href = `../#/auth-callback?access_token=${access_token}&refresh_token=${refresh_token}&type=${type}`;
window.location.href = `./#/auth-callback?access_token=${access_token}&refresh_token=${refresh_token}&type=${type}`;
}

// Call the function to intercept the auth callback
Expand Down
136 changes: 80 additions & 56 deletions src/tests/supabase/functions/postmark.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getExpectedAuthorization } from '../../../../supabase/functions/postmark/getExpectedAuthorization.js';
import { extractMailContactData } from '../../../../supabase/functions/postmark/extractMailContactData.js';
import { getExpectedAuthorization } from '../../../../supabase/functions/postmark/getExpectedAuthorization.js';

describe('getExpectedAuthorization', () => {
it('should return the expected Authorization header from provided user and password', () => {
Expand All @@ -16,15 +16,17 @@ describe('extractMailContactData', () => {
Name: 'Firstname Lastname',
},
]);
expect(result).toEqual({
firstName: 'Firstname',
lastName: 'Lastname',
email: 'firstname.lastname@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: 'Firstname',
lastName: 'Lastname',
email: 'firstname.lastname@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should ignore extra recipients', () => {
it('should support extra recipients', () => {
const result = extractMailContactData([
{
Email: 'firstname.lastname@marmelab.com',
Expand All @@ -35,12 +37,20 @@ describe('extractMailContactData', () => {
Name: 'John Doe',
},
]);
expect(result).toEqual({
firstName: 'Firstname',
lastName: 'Lastname',
email: 'firstname.lastname@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: 'Firstname',
lastName: 'Lastname',
email: 'firstname.lastname@marmelab.com',
domain: 'marmelab.com',
},
{
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should use a single word name as last name', () => {
Expand All @@ -50,12 +60,14 @@ describe('extractMailContactData', () => {
Name: 'Name',
},
]);
expect(result).toEqual({
firstName: '',
lastName: 'Name',
email: 'name@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: '',
lastName: 'Name',
email: 'name@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should support multi word last name', () => {
Expand All @@ -65,12 +77,14 @@ describe('extractMailContactData', () => {
Name: 'Multi Word Name',
},
]);
expect(result).toEqual({
firstName: 'Multi',
lastName: 'Word Name',
email: 'multi.word.name@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: 'Multi',
lastName: 'Word Name',
email: 'multi.word.name@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should support multiple @ in email', () => {
Expand All @@ -81,12 +95,14 @@ describe('extractMailContactData', () => {
Name: 'John Doe',
},
]);
expect(result).toEqual({
firstName: 'John',
lastName: 'Doe',
email: '"john@doe"@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: 'John',
lastName: 'Doe',
email: '"john@doe"@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should use first part of email when Name is empty', () => {
Expand All @@ -96,12 +112,14 @@ describe('extractMailContactData', () => {
Name: '',
},
]);
expect(result).toEqual({
firstName: 'john',
lastName: 'doe',
email: 'john.doe@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: 'john',
lastName: 'doe',
email: 'john.doe@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should use first part of email when Name is empty and support single word', () => {
Expand All @@ -111,12 +129,14 @@ describe('extractMailContactData', () => {
Name: '',
},
]);
expect(result).toEqual({
firstName: '',
lastName: 'john',
email: 'john@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: '',
lastName: 'john',
email: 'john@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should use first part of email when Name is empty and support multiple words', () => {
Expand All @@ -126,12 +146,14 @@ describe('extractMailContactData', () => {
Name: '',
},
]);
expect(result).toEqual({
firstName: 'john',
lastName: 'doe multi',
email: 'john.doe.multi@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: 'john',
lastName: 'doe multi',
email: 'john.doe.multi@marmelab.com',
domain: 'marmelab.com',
},
]);
});

it('should support empty Name and multiple @ in email', () => {
Expand All @@ -142,11 +164,13 @@ describe('extractMailContactData', () => {
Name: '',
},
]);
expect(result).toEqual({
firstName: '"john',
lastName: 'doe"',
email: '"john@doe"@marmelab.com',
domain: 'marmelab.com',
});
expect(result).toEqual([
{
firstName: '"john',
lastName: 'doe"',
email: '"john@doe"@marmelab.com',
domain: 'marmelab.com',
},
]);
});
});
5 changes: 4 additions & 1 deletion supabase/functions/postmark/addNoteToContact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,8 @@ export const addNoteToContact = async ({
{ status: 500 }
);

return new Response('OK');
await supabaseAdmin
.from('contacts')
.update({ last_seen: new Date() })
.eq('id', contact.id);
};
33 changes: 18 additions & 15 deletions supabase/functions/postmark/extractMailContactData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,22 @@ export const extractMailContactData = (
Name: string;
}[]
) => {
// We only support one recipient for now
const contact = ToFull[0];

const domain = contact.Email.split('@').at(-1);
const fullName =
contact.Name ||
contact.Email.split('@').slice(0, -1).join(' ').split('.').join(' ');
let firstName = '';
let lastName = fullName;
if (fullName && fullName.includes(' ')) {
const parts = fullName.split(' ');
firstName = parts[0];
lastName = parts.slice(1).join(' ');
}
return { firstName, lastName, email: contact.Email, domain };
return ToFull.map(contact => {
const domain = contact.Email.split('@').at(-1)!;
const fullName =
contact.Name ||
contact.Email.split('@')
.slice(0, -1)
.join(' ')
.split('.')
.join(' ');
let firstName = '';
let lastName = fullName;
if (fullName && fullName.includes(' ')) {
const parts = fullName.split(' ');
firstName = parts[0];
lastName = parts.slice(1).join(' ');
}
return { firstName, lastName, email: contact.Email, domain };
});
};
41 changes: 24 additions & 17 deletions supabase/functions/postmark/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

// Setup type definitions for built-in Supabase Runtime APIs
import 'jsr:@supabase/functions-js/edge-runtime.d.ts';
import { getExpectedAuthorization } from './getExpectedAuthorization.ts';
import { extractMailContactData } from './extractMailContactData.ts';
import { addNoteToContact } from './addNoteToContact.ts';
import { extractMailContactData } from './extractMailContactData.ts';
import { getExpectedAuthorization } from './getExpectedAuthorization.ts';
import { getNoteContent } from './getNoteContent.ts';

const webhookUser = Deno.env.get('POSTMARK_WEBHOOK_USER');
Expand Down Expand Up @@ -46,24 +46,31 @@ Deno.serve(async req => {
);
}

const { firstName, lastName, email, domain } =
extractMailContactData(ToFull);
if (!email) {
// Return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
return new Response(`Could not extract email from ToFull: ${ToFull}`, {
status: 403,
const contacts = extractMailContactData(ToFull);

for (const { firstName, lastName, email, domain } of contacts) {
if (!email) {
// Return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
return new Response(
`Could not extract email from ToFull: ${ToFull}`,
{
status: 403,
}
);
}

await addNoteToContact({
salesEmail,
email,
domain,
firstName,
lastName,
noteContent,
});
}

return await addNoteToContact({
salesEmail,
email,
domain,
firstName,
lastName,
noteContent,
});
return new Response('OK');
});

const checkRequestTypeAndHeaders = (req: Request) => {
Expand Down
5 changes: 0 additions & 5 deletions supabase/templates/invite.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ <h2>Confirm your account</h2>
class="button"
>Confirm my account</a
>
<p>
<strong>Warning:</strong> If the password reset request did
not originate from you, please contact us immediately as
this may be a fraudulent attempt.
</p>
<p>See you soon!</p>
<p>Atomic CRM team</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion supabase/templates/recovery.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<body>
<div class="container">
<div class="header">
<h2>Reset your password</h2>
<h2>Reset Your Atomic CRM Password</h2>
</div>
<div class="content">
<p>Hello,</p>
Expand Down

0 comments on commit 67a022c

Please sign in to comment.