Skip to content

Commit

Permalink
feat: disable tracking for transactional emails
Browse files Browse the repository at this point in the history
  • Loading branch information
KishenKumarrrrr committed Jan 25, 2024
1 parent 46a7625 commit fe78f1c
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 12 deletions.
6 changes: 6 additions & 0 deletions backend/src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ interface ConfigSchema {
}
mailFrom: string
mailConfigurationSet: string
noTrackingMailConfigurationSet: string
mailVia: string
mailDefaultRate: number
transactionalEmail: {
Expand Down Expand Up @@ -468,6 +469,11 @@ const config: Config<ConfigSchema> = convict({
default: 'postman-email-open',
env: 'BACKEND_SES_CONFIGURATION_SET',
},
noTrackingMailConfigurationSet: {
doc: 'AWS SES Configuration set that does not include open and read tracking',
default: 'postman-email-no-tracking',
env: 'BACKEND_SES_NO_TRACKING_CONFIGURATION_SET',
},
mailVia: {
doc: 'Text to appended to custom sender name',
default: 'via Postman',
Expand Down
3 changes: 2 additions & 1 deletion backend/src/core/services/mail.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const mailClient = new MailClient(
config.get('mailOptions'),
config.get('emailCallback.hashSecret'),
config.get('emailFallback.activate') ? config.get('mailFrom') : undefined,
config.get('mailConfigurationSet')
config.get('mailConfigurationSet'),
config.get('noTrackingMailConfigurationSet')
)

export const MailService = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const InitEmailTransactionalMiddleware = (
tag?: string
cc?: string[]
bcc?: string[]
disable_tracking?: boolean
}
type ReqBodyWithId = ReqBody & { emailMessageTransactionalId: string }

Expand Down Expand Up @@ -210,6 +211,7 @@ export const InitEmailTransactionalMiddleware = (
cc,
bcc,
emailMessageTransactionalId, // added by saveMessage middleware
disable_tracking: disableTracking,
} = req.body

try {
Expand Down Expand Up @@ -275,6 +277,7 @@ export const InitEmailTransactionalMiddleware = (
? bcc.filter((c) => !blacklistedRecipients.includes(c))
: undefined,
emailMessageTransactionalId,
disableTracking,
})
emailMessageTransactional.set(
'status',
Expand Down
1 change: 1 addition & 0 deletions backend/src/email/routes/email-transactional.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const InitEmailTransactionalRoute = (
.items(
Joi.string().trim().email().options({ convert: true }).lowercase()
),
disable_tracking: Joi.boolean().optional(),
}),
}
const getByIdValidator = {
Expand Down
3 changes: 3 additions & 0 deletions backend/src/email/services/email-transactional.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ async function sendMessage({
cc,
bcc,
emailMessageTransactionalId,
disableTracking,
}: {
subject: string
body: string
Expand All @@ -51,6 +52,7 @@ async function sendMessage({
cc?: string[]
bcc?: string[]
emailMessageTransactionalId: string
disableTracking?: boolean
}): Promise<void> {
// TODO: flagging this coupling for future refactoring:
// currently, we are using EmailTemplateService to sanitize both tx emails and campaign emails
Expand Down Expand Up @@ -99,6 +101,7 @@ async function sendMessage({
// receive from SES, but not saving to DB
const isEmailSent = await EmailService.sendEmail(mailToSend, {
extraSmtpHeaders: { isTransactional: true },
disableTracking,
})
if (!isEmailSent) {
throw new Error('Failed to send transactional email')
Expand Down
43 changes: 32 additions & 11 deletions shared/src/clients/mail-client.class/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@ export * from './interfaces'

export type SendEmailOpts = {
extraSmtpHeaders: Record<string, any>
disableTracking?: boolean
}

export default class MailClient {
private mailer: nodemailer.Transporter
private email: string
private hashSecret: string
private configSet: string | undefined
private defaultConfigSet: string | undefined
private noTrackingConfigSet: string | undefined

constructor(
credentials: MailCredentials,
hashSecret: string,
email?: string,
configSet?: string
defaultConfigSet?: string,
noTrackingConfigSet?: string
) {
const { host, port, auth } = credentials
this.hashSecret = hashSecret
Expand All @@ -35,7 +38,8 @@ export default class MailClient {
pass: auth.pass,
},
})
this.configSet = configSet
this.defaultConfigSet = defaultConfigSet
this.noTrackingConfigSet = noTrackingConfigSet
}

public sendMail(
Expand All @@ -61,14 +65,7 @@ export default class MailClient {
let headers: any = {
[REFERENCE_ID_HEADER]: JSON.stringify(xSmtpHeader),
}
if (this.configSet) {
headers = {
...headers,
// Specify this to configure callback endpoint for notifications other
// than delivery and bounce through SES configuration set
[CONFIGURATION_SET_HEADER]: this.configSet,
}
}
headers = this.setSesConfigurationHeader(headers, option?.disableTracking)
if (input.unsubLink) {
headers = {
...headers,
Expand Down Expand Up @@ -96,4 +93,28 @@ export default class MailClient {
})
})
}

private setSesConfigurationHeader(
headers: object,
disableTracking: boolean | undefined
): object {
// 1. If there is no default config set, we will not set any configuration header
if (!this.defaultConfigSet) {
return headers
}
// 2. If the user wants to disable tracking and there is a no tracking configuration, we set it
if (disableTracking && this.noTrackingConfigSet) {
return {
...headers,
// Configuration header does not include open and read notification
[CONFIGURATION_SET_HEADER]: this.noTrackingConfigSet,
}
}
// 3. Otherwise, we will use the default tracking SES configuration set
return {
...headers,
// Configuration header includes open and read notification
[CONFIGURATION_SET_HEADER]: this.defaultConfigSet,
}
}
}

0 comments on commit fe78f1c

Please sign in to comment.