Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 3 additions & 4 deletions src/services/announcements/emails/emailBatchService.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class EmailBatchService {

// Normalize recipients to { email }
const normalizedRecipients = normalizeRecipientsToObjects(recipients);

if (normalizedRecipients.length === 0) {
const error = new Error('At least one recipient is required');
error.statusCode = 400;
Expand All @@ -47,17 +48,15 @@ class EmailBatchService {
const invalidRecipients = normalizedRecipients.filter(
(recipient) => !isValidEmailAddress(recipient.email),
);

if (invalidRecipients.length > 0) {
const error = new Error('One or more recipient emails are invalid');
error.statusCode = 400;
error.invalidRecipients = invalidRecipients.map((r) => r.email);
throw error;
}

// Filter to only valid recipients
const validRecipients = normalizedRecipients.filter((recipient) =>
isValidEmailAddress(recipient.email),
);
const validRecipients = normalizedRecipients;

// Check if we have any valid recipients AFTER filtering
if (validRecipients.length === 0) {
Expand Down
38 changes: 22 additions & 16 deletions src/utilities/emailValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ function isValidEmailAddress(email) {
*/
function normalizeRecipientsToArray(input) {
const arr = Array.isArray(input) ? input : [input];
const trimmed = arr
.map((e) => (typeof e === 'string' ? e.trim() : ''))
.filter((e) => e.length > 0);
// Dedupe case-insensitively

const splitEmails = arr.flatMap((e) => {
if (typeof e !== 'string') return [];

return e
.split(',') // 🔥 KEY FIX
.map((email) => email.trim())
.filter(Boolean);
});

// dedupe case-insensitively
const seen = new Set();
return trimmed.filter((e) => {
return splitEmails.filter((e) => {
const key = e.toLowerCase();
if (seen.has(key)) return false;
seen.add(key);
Expand All @@ -40,18 +47,17 @@ function normalizeRecipientsToArray(input) {
*/
function normalizeRecipientsToObjects(input) {
if (!Array.isArray(input)) return [];
const emails = input
.filter((item) => {
if (typeof item === 'string') {
return item.trim().length > 0;
}
return item && typeof item.email === 'string' && item.email.trim().length > 0;
})
.map((item) => ({
email: typeof item === 'string' ? item.trim() : item.email.trim(),
}));

// Dedupe case-insensitively
const emails = input.flatMap((item) => {
const value = typeof item === 'string' ? item : item?.email || '';

return value
.split(',')
.map((email) => email.trim())
.filter(Boolean)
.map((email) => ({ email }));
});

const seen = new Set();
return emails.filter((obj) => {
const key = obj.email.toLowerCase();
Expand Down
Loading