Merge pull request #32576 from drodil/included_topics
feat(notifications): add support for included topics
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-backend-module-email': patch
|
||||
'@backstage/plugin-notifications-backend': patch
|
||||
'@backstage/plugin-notifications-common': patch
|
||||
---
|
||||
|
||||
Allow configuring included topics for email notifications.
|
||||
+6
-1
@@ -161,9 +161,14 @@ export interface Config {
|
||||
*/
|
||||
maxSeverity?: NotificationSeverity;
|
||||
/**
|
||||
* A notification who's topic is in this array will not be emailed
|
||||
* A notification with topic is in this array will not be emailed
|
||||
*/
|
||||
excludedTopics?: string[];
|
||||
/**
|
||||
* A notification with topic in this array will be emailed. If not defined, only
|
||||
* excludedTopics takes effect.
|
||||
*/
|
||||
includedTopics?: string[];
|
||||
};
|
||||
/**
|
||||
* White list of addresses to send email to
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
TestDatabases,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import {
|
||||
NotificationProcessor,
|
||||
NotificationRecipientResolver,
|
||||
NotificationSendOptions,
|
||||
} from '@backstage/plugin-notifications-node';
|
||||
@@ -827,6 +828,118 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /notifications with custom processor', () => {
|
||||
const httpAuth = mockServices.httpAuth({
|
||||
defaultCredentials: mockCredentials.service(),
|
||||
});
|
||||
|
||||
const customProcessor: NotificationProcessor = {
|
||||
getName: () => 'customProcessor',
|
||||
processOptions: jest.fn(),
|
||||
preProcess: jest.fn(),
|
||||
postProcess: jest.fn(),
|
||||
getNotificationFilters: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetAllMocks();
|
||||
const client = await database.getClient();
|
||||
await client('notification').del();
|
||||
await client('broadcast').del();
|
||||
await client('user_settings').del();
|
||||
|
||||
(customProcessor.processOptions as jest.Mock).mockImplementation(
|
||||
opts => opts,
|
||||
);
|
||||
(customProcessor.preProcess as jest.Mock).mockImplementation(
|
||||
(notification, _options) => notification,
|
||||
);
|
||||
|
||||
const router = await createRouter({
|
||||
logger: mockServices.logger.mock(),
|
||||
store,
|
||||
signals: signalService,
|
||||
userInfo,
|
||||
config,
|
||||
httpAuth,
|
||||
auth,
|
||||
catalog,
|
||||
processors: [customProcessor],
|
||||
});
|
||||
app = express().use(router).use(mockErrorHandler());
|
||||
});
|
||||
|
||||
const sendNotification = async (data: NotificationSendOptions) =>
|
||||
request(app)
|
||||
.post('/notifications')
|
||||
.send(data)
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('Accept', 'application/json');
|
||||
|
||||
it('should not call processor preProcess if topic is excluded', async () => {
|
||||
(customProcessor.getNotificationFilters as jest.Mock).mockReturnValue({
|
||||
excludedTopics: ['topic1'],
|
||||
});
|
||||
// Should be processed
|
||||
await sendNotification({
|
||||
recipients: {
|
||||
type: 'broadcast',
|
||||
},
|
||||
payload: {
|
||||
title: 'test notification',
|
||||
topic: 'topic2',
|
||||
},
|
||||
});
|
||||
// Excluded, should not be processed
|
||||
await sendNotification({
|
||||
recipients: {
|
||||
type: 'broadcast',
|
||||
},
|
||||
payload: {
|
||||
title: 'test notification',
|
||||
topic: 'topic1',
|
||||
},
|
||||
});
|
||||
expect(customProcessor.preProcess).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not call processor preProcess if topic is not included', async () => {
|
||||
(customProcessor.getNotificationFilters as jest.Mock).mockReturnValue({
|
||||
includedTopics: ['topic1'],
|
||||
});
|
||||
// Should not be processed, not included topic
|
||||
await sendNotification({
|
||||
recipients: {
|
||||
type: 'broadcast',
|
||||
},
|
||||
payload: {
|
||||
title: 'test notification',
|
||||
topic: 'topic2',
|
||||
},
|
||||
});
|
||||
// Should not be processed, no topic
|
||||
await sendNotification({
|
||||
recipients: {
|
||||
type: 'broadcast',
|
||||
},
|
||||
payload: {
|
||||
title: 'test notification',
|
||||
},
|
||||
});
|
||||
// Included, should be processed
|
||||
await sendNotification({
|
||||
recipients: {
|
||||
type: 'broadcast',
|
||||
},
|
||||
payload: {
|
||||
title: 'test notification',
|
||||
topic: 'topic1',
|
||||
},
|
||||
});
|
||||
expect(customProcessor.preProcess).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /', () => {
|
||||
const httpAuth = mockServices.httpAuth({
|
||||
defaultCredentials: mockCredentials.user(),
|
||||
|
||||
@@ -365,6 +365,15 @@ export async function createRouter(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.includedTopics) {
|
||||
if (
|
||||
!payload.topic ||
|
||||
!filters.includedTopics.includes(payload.topic)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push(processor);
|
||||
}
|
||||
@@ -418,14 +427,16 @@ export async function createRouter(
|
||||
) => {
|
||||
const filtered = await filterProcessors(notification);
|
||||
for (const processor of filtered) {
|
||||
if (processor.postProcess) {
|
||||
try {
|
||||
await processor.postProcess(notification, opts);
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error while post processing notification with ${processor.getName()}: ${e}`,
|
||||
);
|
||||
}
|
||||
if (!processor.postProcess) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await processor.postProcess(notification, opts);
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error while post processing notification with ${processor.getName()}: ${e}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -64,6 +64,7 @@ export type NotificationProcessorFilters = {
|
||||
minSeverity?: NotificationSeverity;
|
||||
maxSeverity?: NotificationSeverity;
|
||||
excludedTopics?: string[];
|
||||
includedTopics?: string[];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -43,5 +43,8 @@ export const getProcessorFiltersFromConfig = (config: Config) => {
|
||||
filter.excludedTopics = config.getOptionalStringArray(
|
||||
'filter.excludedTopics',
|
||||
);
|
||||
filter.includedTopics = config.getOptionalStringArray(
|
||||
'filter.includedTopics',
|
||||
);
|
||||
return filter;
|
||||
};
|
||||
|
||||
@@ -130,6 +130,7 @@ export type NotificationProcessorFilters = {
|
||||
minSeverity?: NotificationSeverity;
|
||||
maxSeverity?: NotificationSeverity;
|
||||
excludedTopics?: string[];
|
||||
includedTopics?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user