Merge pull request #31922 from StateFarmIns/update-notif-ses-options

fix: Update email processor ses options to match v2 of ses sdk
This commit is contained in:
Fredrik Adelöw
2025-12-13 19:21:45 +01:00
committed by GitHub
5 changed files with 40 additions and 14 deletions
+21
View File
@@ -0,0 +1,21 @@
---
'@backstage/plugin-notifications-backend-module-email': patch
---
SES config for the notification email processor now supports utilizing an ARN for the SES identity when sending an email after the SES SDK V2 update.
The `sesConfig.fromArn` will set the `fromEmailAddressIdentityArn` option for the SES `SendEmailCommand`. The `sesConfig.sourceArn` field is removed since no equivalent option is available in the send email command options. Setting `sesConfig.sourceArn` will have no effect and log a warning. Example changes:
```diff
notifications:
processors:
email:
transportConfig:
transport: "ses"
region: "us-west-2"
sender: "sender@mycompany.com"
replyTo: "no-reply@mycompany.com"
sesConfig:
- sourceArn: "arn:aws:ses:us-west-2:123456789012:identity/example.com"
fromArn: "arn:aws:ses:us-west-2:123456789012:identity/example.com"
```
@@ -81,7 +81,6 @@ notifications:
receiver: 'users'
# Optional SES config
# sesConfig:
# sourceArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com'
# fromArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com'
# configurationSetName: 'custom-config'
# How many emails to send concurrently, defaults to 2
@@ -136,10 +136,6 @@ export interface Config {
* Optional SES config for mail options. Allows for delegated sender
*/
sesConfig?: {
/**
* ARN of the identity to use as the source of the email
*/
sourceArn?: string;
/**
* ARN of the identity to use for the "From"/sender address of the email
*/
@@ -497,9 +497,13 @@ describe('NotificationsEmailProcessor', () => {
text: 'https://example.org/notifications',
to: 'mock@backstage.io',
ses: {
SourceArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com',
FromArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com',
FromEmailAddressIdentityArn:
'arn:aws:ses:us-west-2:123456789012:identity/example.com',
},
});
expect(logger.warn).toHaveBeenCalledWith(
'sourceArn is not supported in SESv2 and will be ignored',
);
});
});
@@ -45,6 +45,7 @@ import { DefaultAwsCredentialsManager } from '@backstage/integration-aws-node';
import { NotificationTemplateRenderer } from '../extensions';
import Mail from 'nodemailer/lib/mailer';
import pThrottle from 'p-throttle';
import { SendEmailCommandInput } from '@aws-sdk/client-sesv2';
export class NotificationsEmailProcessor implements NotificationProcessor {
private transporter: any;
@@ -53,6 +54,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
private readonly sender: string;
private readonly replyTo?: string;
private readonly sesConfig?: Config;
private readonly sesOptions?: Partial<SendEmailCommandInput>;
private readonly cacheTtl: number;
private readonly concurrencyLimit: number;
private readonly throttleInterval: number;
@@ -91,6 +93,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
this.sender = emailProcessorConfig.getString('sender');
this.replyTo = emailProcessorConfig.getOptionalString('replyTo');
this.sesConfig = emailProcessorConfig.getOptionalConfig('sesConfig');
this.sesOptions = this.getSesOptions();
this.concurrencyLimit =
emailProcessorConfig.getOptionalNumber('concurrencyLimit') ?? 2;
this.throttleInterval = emailProcessorConfig.has('throttleInterval')
@@ -307,20 +310,23 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
return contentParts.join('\n\n');
}
private async getSesOptions() {
private getSesOptions(): Partial<SendEmailCommandInput> | undefined {
if (!this.sesConfig) {
return undefined;
}
const ses: Record<string, string> = {};
const sourceArn = this.sesConfig.getOptionalString('sourceArn');
const ses: Partial<SendEmailCommandInput> = {};
const fromArn = this.sesConfig.getOptionalString('fromArn');
const sourceArn = this.sesConfig.getOptionalString('sourceArn');
const configurationSetName = this.sesConfig.getOptionalString(
'configurationSetName',
);
if (sourceArn) ses.SourceArn = sourceArn;
if (fromArn) ses.FromArn = fromArn;
if (fromArn) ses.FromEmailAddressIdentityArn = fromArn;
if (configurationSetName) ses.ConfigurationSetName = configurationSetName;
if (sourceArn)
this.logger.warn(
'sourceArn is not supported in SESv2 and will be ignored',
);
return Object.keys(ses).length > 0 ? ses : undefined;
}
@@ -332,7 +338,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
html: this.getHtmlContent(notification),
text: this.getTextContent(notification),
replyTo: this.replyTo,
ses: await this.getSesOptions(),
ses: this.sesOptions,
};
await this.sendMails(mailOptions, emails);
@@ -350,7 +356,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
html: await this.templateRenderer?.getHtml?.(notification),
text: await this.templateRenderer?.getText?.(notification),
replyTo: this.replyTo,
ses: await this.getSesOptions(),
ses: this.sesOptions,
};
await this.sendMails(mailOptions, emails);