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

feat(connector): enable custom headers for SMTP connector #6256

Merged
merged 1 commit into from
Jul 17, 2024
Merged
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
5 changes: 5 additions & 0 deletions .changeset/slow-boxes-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@logto/connector-smtp": minor
---

enable static custom headers for SMTP connector
9 changes: 9 additions & 0 deletions packages/connectors/connector-smtp/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,5 +198,14 @@ export const defaultMetadata: ConnectorMetadata = {
type: ConnectorConfigFormItemType.Switch,
required: false,
},
{
key: 'customHeaders',
label: 'Custom Headers',
type: ConnectorConfigFormItemType.Json,
required: false,
defaultValue: {},
description:
'Custom headers to be added to original email headers when sending messages. Both keys and values should be string-typed.',
},
],
};
22 changes: 22 additions & 0 deletions packages/connectors/connector-smtp/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ describe('SMTP connector', () => {
to: 'baz',
});
});

it('should send mail with customer headers', async () => {
const connector = await createConnector({
getConfig: vi.fn().mockResolvedValue({
...mockedConfig,
customHeaders: { 'X-Test': 'test', 'X-Test-Another': ['test1', 'test2', 'test3'] },
}),
});
await connector.sendMessage({
to: 'baz',
type: TemplateType.OrganizationInvitation,
payload: { code: '345678', link: 'https://example.com' },
});

expect(sendMail).toHaveBeenCalledWith({
from: '<notice@test.smtp>',
subject: 'Organization invitation',
text: 'This is for organization invitation. Your link is https://example.com.',
to: 'baz',
headers: { 'X-Test': 'test', 'X-Test-Another': ['test1', 'test2', 'test3'] },
});
});
});

describe('Test config guard', () => {
Expand Down
11 changes: 9 additions & 2 deletions packages/connectors/connector-smtp/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert } from '@silverhand/essentials';
import { assert, conditional } from '@silverhand/essentials';

import type {
GetConnectorConfig,
Expand All @@ -14,6 +14,7 @@ import {
replaceSendMessageHandlebars,
} from '@logto/connector-kit';
import nodemailer from 'nodemailer';
import type Mail from 'nodemailer/lib/mailer';
import type SMTPTransport from 'nodemailer/lib/smtp-transport';

import { defaultMetadata } from './constant.js';
Expand Down Expand Up @@ -44,11 +45,17 @@ const sendMessage =
template.contentType
);

const mailOptions = {
const mailOptions: Mail.Options = {
to,
from: config.fromEmail,
replyTo: config.replyTo,
subject: replaceSendMessageHandlebars(template.subject, payload),
...conditional(
config.customHeaders &&
Object.entries(config.customHeaders).length > 0 && {
headers: config.customHeaders,
}
),
...contentsObject,
};

Expand Down
1 change: 1 addition & 0 deletions packages/connectors/connector-smtp/src/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const mockedConfig = {
usageType: 'OrganizationInvitation',
},
],
customHeaders: {},
};

export const mockedOauth2AuthWithToken = {
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/connector-smtp/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export const smtpConfigGuard = z.object({
servername: z.string().optional(),
ignoreTLS: z.boolean().optional(),
requireTLS: z.boolean().optional(),
customHeaders: z.record(z.string().or(z.string().array())).optional(),
});

export type SmtpConfig = z.infer<typeof smtpConfigGuard>;
Loading