Merge pull request #28796 from rr-wfm/feature/azure-communication-service
Add Azure Communication Service transport
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
Adds support for sending Backstage notifications as emails to users.
|
||||
|
||||
Supports sending emails using `SMTP`, `SES`, `sendmail`, or `stream` (for debugging purposes).
|
||||
Supports sending emails using `SMTP`, `SES`, `azure`, `sendmail`, or `stream` (for debugging purposes).
|
||||
|
||||
## Customizing email content
|
||||
|
||||
@@ -61,6 +61,12 @@ notifications:
|
||||
# accessKeyId: 'my-access-key
|
||||
# region: 'us-west-2'
|
||||
|
||||
# Azure Communication Service
|
||||
# transportConfig:
|
||||
# transport: 'azure'
|
||||
# endpoint: 'https://my-endpoint.communication.azure.com'
|
||||
# accessKey: 'my-access-key' Optional: if not provided, Managed Identity will be used
|
||||
|
||||
# sendmail
|
||||
# transportConfig:
|
||||
# transport: 'sendmail'
|
||||
|
||||
@@ -83,6 +83,18 @@ export interface Config {
|
||||
| {
|
||||
/** Only for debugging, disables the actual sending of emails */
|
||||
transport: 'stream';
|
||||
}
|
||||
| {
|
||||
transport: 'azure';
|
||||
/**
|
||||
* Azure Communication Services endpoint
|
||||
*/
|
||||
endpoint: string;
|
||||
/**
|
||||
* Optional Azure Communication Services access key
|
||||
* @visibility secret
|
||||
*/
|
||||
accessKey?: string;
|
||||
};
|
||||
/**
|
||||
* Sender email address
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-ses": "^3.550.0",
|
||||
"@aws-sdk/types": "^3.347.0",
|
||||
"@azure/communication-email": "^1.0.0",
|
||||
"@azure/identity": "^4.0.0",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-client": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
|
||||
+3
@@ -32,6 +32,7 @@ import {
|
||||
NotificationProcessorFilters,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
createAzureTransport,
|
||||
createSendmailTransport,
|
||||
createSesTransport,
|
||||
createSmtpTransport,
|
||||
@@ -117,6 +118,8 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
|
||||
this.transporter = createSendmailTransport(this.transportConfig);
|
||||
} else if (transport === 'stream') {
|
||||
this.transporter = createStreamTransport();
|
||||
} else if (transport === 'azure') {
|
||||
this.transporter = createAzureTransport(this.transportConfig);
|
||||
} else {
|
||||
throw new Error(`Unsupported transport: ${transport}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createTransport } from 'nodemailer';
|
||||
import {
|
||||
EmailClient,
|
||||
EmailMessage,
|
||||
EmailRecipients,
|
||||
} from '@azure/communication-email';
|
||||
import { DefaultAzureCredential } from '@azure/identity';
|
||||
import { Config } from '@backstage/config';
|
||||
import MailMessage from 'nodemailer/lib/mailer/mail-message';
|
||||
|
||||
export const createAzureTransport = async (config: Config) => {
|
||||
const accessKey = config.getOptionalString('accessKey');
|
||||
const credentials =
|
||||
accessKey === undefined ? new DefaultAzureCredential() : { key: accessKey };
|
||||
const emailClient = new EmailClient(
|
||||
config.getString('endpoint'),
|
||||
credentials,
|
||||
);
|
||||
const transport = {
|
||||
name: 'azure',
|
||||
version: '1.0.0',
|
||||
send: async (
|
||||
mail: MailMessage,
|
||||
callback: (err: Error | null, info: {} | null) => Promise<void>,
|
||||
) => {
|
||||
const envelope = mail.data.envelope || mail.message.getEnvelope();
|
||||
const from =
|
||||
typeof envelope.from === 'string'
|
||||
? envelope.from
|
||||
: config.getString('senderAddress');
|
||||
const to =
|
||||
typeof envelope.to === 'string' ? [envelope.to] : envelope.to ?? [];
|
||||
const recipients: EmailRecipients = {
|
||||
to: to.map(address => ({ address })),
|
||||
};
|
||||
|
||||
const content = {
|
||||
subject: mail.message.getHeader('Subject'),
|
||||
html:
|
||||
typeof mail.data.html === 'string'
|
||||
? mail.data.html
|
||||
: mail.data.html?.toString('utf-8'),
|
||||
plainText:
|
||||
typeof mail.data.text === 'string'
|
||||
? mail.data.text
|
||||
: mail.data.text?.toString('utf-8') ?? 'No content',
|
||||
};
|
||||
|
||||
const emailMessage: EmailMessage = {
|
||||
senderAddress: from,
|
||||
recipients: recipients,
|
||||
content: content,
|
||||
};
|
||||
|
||||
try {
|
||||
const poller = await emailClient.beginSend(emailMessage);
|
||||
const response = await poller.pollUntilDone();
|
||||
callback(null, response);
|
||||
} catch (error) {
|
||||
callback(error, null);
|
||||
}
|
||||
},
|
||||
};
|
||||
return createTransport(transport);
|
||||
};
|
||||
@@ -17,3 +17,4 @@ export { createSmtpTransport } from './smtp';
|
||||
export { createSesTransport } from './ses';
|
||||
export { createSendmailTransport } from './sendmail';
|
||||
export { createStreamTransport } from './stream';
|
||||
export { createAzureTransport } from './azure';
|
||||
|
||||
Reference in New Issue
Block a user