feat: add notifications filtering by processors
Signed-off-by: Yaron Dayagi <ydayagi@redhat.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-node': major
|
||||
---
|
||||
|
||||
add notifications filtering by processors
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-backend-module-email': minor
|
||||
---
|
||||
|
||||
add notification filters
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-backend': major
|
||||
---
|
||||
|
||||
adding filtering of notifications by processors
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { HumanDuration } from '@backstage/types';
|
||||
import { NotificationSeverity } from '@backstage/plugin-notifications-common';
|
||||
|
||||
export interface Config {
|
||||
/**
|
||||
@@ -117,6 +118,20 @@ export interface Config {
|
||||
*/
|
||||
ttl?: HumanDuration;
|
||||
};
|
||||
filter?: {
|
||||
/**
|
||||
* Minimum severity. A notification with lower severity will not be emailed
|
||||
*/
|
||||
minSeverity?: NotificationSeverity;
|
||||
/**
|
||||
* Maximum severity. A notification with higher severity will not be emailed
|
||||
*/
|
||||
maxSeverity?: NotificationSeverity;
|
||||
/**
|
||||
* A notification who's topic is in this array will not be emailed
|
||||
*/
|
||||
excludedTopics?: string[];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+35
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
import {
|
||||
NotificationProcessor,
|
||||
NotificationProcessorFilters,
|
||||
NotificationSendOptions,
|
||||
} from '@backstage/plugin-notifications-node';
|
||||
import {
|
||||
@@ -28,7 +29,11 @@ import {
|
||||
CATALOG_FILTER_EXISTS,
|
||||
CatalogClient,
|
||||
} from '@backstage/catalog-client';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
Notification,
|
||||
notificationSeverities,
|
||||
NotificationSeverity,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
createSendmailTransport,
|
||||
createSesTransport,
|
||||
@@ -51,6 +56,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
|
||||
private readonly concurrencyLimit: number;
|
||||
private readonly throttleInterval: number;
|
||||
private readonly frontendBaseUrl: string;
|
||||
private readonly filter: NotificationProcessorFilters;
|
||||
|
||||
constructor(
|
||||
private readonly logger: LoggerService,
|
||||
@@ -80,6 +86,30 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
|
||||
? durationToMilliseconds(readDurationFromConfig(cacheConfig))
|
||||
: 3_600_000;
|
||||
this.frontendBaseUrl = config.getString('app.baseUrl');
|
||||
this.filter = {};
|
||||
const minSeverity = emailProcessorConfig.getOptionalString(
|
||||
'filter.minSeverity',
|
||||
) as NotificationSeverity;
|
||||
if (minSeverity) {
|
||||
if (notificationSeverities.includes(minSeverity)) {
|
||||
this.filter.minSeverity = minSeverity;
|
||||
} else {
|
||||
throw new Error(`Invalid minSeverity: ${minSeverity}`);
|
||||
}
|
||||
}
|
||||
const maxSeverity = emailProcessorConfig.getOptionalString(
|
||||
'filter.maxSeverity',
|
||||
) as NotificationSeverity;
|
||||
if (maxSeverity) {
|
||||
if (notificationSeverities.includes(maxSeverity)) {
|
||||
this.filter.maxSeverity = maxSeverity;
|
||||
} else {
|
||||
throw new Error(`Invalid maxSeverity: ${maxSeverity}`);
|
||||
}
|
||||
}
|
||||
this.filter.excludedTopics = emailProcessorConfig.getOptionalStringArray(
|
||||
'filter.excludedTopics',
|
||||
);
|
||||
}
|
||||
|
||||
private async getTransporter() {
|
||||
@@ -312,4 +342,8 @@ export class NotificationsEmailProcessor implements NotificationProcessor {
|
||||
|
||||
await this.sendTemplateEmail(notification, emails);
|
||||
}
|
||||
|
||||
getNotificationFilters(): NotificationProcessorFilters {
|
||||
return this.filter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ import { SignalsService } from '@backstage/plugin-signals-node';
|
||||
import {
|
||||
NewNotificationSignal,
|
||||
Notification,
|
||||
NotificationPayload,
|
||||
NotificationReadSignal,
|
||||
notificationSeverities,
|
||||
NotificationStatus,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';
|
||||
@@ -177,9 +179,46 @@ export async function createRouter(
|
||||
return users;
|
||||
};
|
||||
|
||||
const processOptions = async (opts: NotificationSendOptions) => {
|
||||
let ret = opts;
|
||||
const filterProcessors = (payload: NotificationPayload) => {
|
||||
const result: NotificationProcessor[] = [];
|
||||
|
||||
for (const processor of processors) {
|
||||
if (processor.getNotificationFilters) {
|
||||
const filters = processor.getNotificationFilters();
|
||||
if (filters.minSeverity) {
|
||||
if (
|
||||
notificationSeverities.indexOf(payload.severity ?? 'normal') >
|
||||
notificationSeverities.indexOf(filters.minSeverity)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.maxSeverity) {
|
||||
if (
|
||||
notificationSeverities.indexOf(payload.severity ?? 'normal') <
|
||||
notificationSeverities.indexOf(filters.maxSeverity)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.excludedTopics && payload.topic) {
|
||||
if (filters.excludedTopics.includes(payload.topic)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push(processor);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const processOptions = async (opts: NotificationSendOptions) => {
|
||||
const filtered = filterProcessors(opts.payload);
|
||||
let ret = opts;
|
||||
for (const processor of filtered) {
|
||||
try {
|
||||
ret = processor.processOptions
|
||||
? await processor.processOptions(ret)
|
||||
@@ -197,8 +236,9 @@ export async function createRouter(
|
||||
notification: Notification,
|
||||
opts: NotificationSendOptions,
|
||||
) => {
|
||||
const filtered = filterProcessors(notification.payload);
|
||||
let ret = notification;
|
||||
for (const processor of processors) {
|
||||
for (const processor of filtered) {
|
||||
try {
|
||||
ret = processor.preProcess
|
||||
? await processor.preProcess(ret, opts)
|
||||
@@ -216,7 +256,8 @@ export async function createRouter(
|
||||
notification: Notification,
|
||||
opts: NotificationSendOptions,
|
||||
) => {
|
||||
for (const processor of processors) {
|
||||
const filtered = filterProcessors(notification.payload);
|
||||
for (const processor of filtered) {
|
||||
if (processor.postProcess) {
|
||||
try {
|
||||
await processor.postProcess(notification, opts);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { Notification as Notification_2 } from '@backstage/plugin-notifications-common';
|
||||
import { NotificationPayload } from '@backstage/plugin-notifications-common';
|
||||
import { NotificationSeverity } from '@backstage/plugin-notifications-common';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -23,6 +24,7 @@ export class DefaultNotificationService implements NotificationService {
|
||||
// @public
|
||||
export interface NotificationProcessor {
|
||||
getName(): string;
|
||||
getNotificationFilters?(): NotificationProcessorFilters;
|
||||
postProcess?(
|
||||
notification: Notification_2,
|
||||
options: NotificationSendOptions,
|
||||
@@ -36,6 +38,13 @@ export interface NotificationProcessor {
|
||||
): Promise<NotificationSendOptions>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationProcessorFilters = {
|
||||
minSeverity?: NotificationSeverity;
|
||||
maxSeverity?: NotificationSeverity;
|
||||
excludedTopics?: string[];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationRecipients =
|
||||
| {
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
Notification,
|
||||
NotificationSeverity,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import { NotificationSendOptions } from './service';
|
||||
|
||||
/**
|
||||
@@ -90,6 +93,11 @@ export interface NotificationProcessor {
|
||||
notification: Notification,
|
||||
options: NotificationSendOptions,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* notification filters are used to call the processor only in certain conditions
|
||||
*/
|
||||
getNotificationFilters?(): NotificationProcessorFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,3 +116,12 @@ export const notificationsProcessingExtensionPoint =
|
||||
createExtensionPoint<NotificationsProcessingExtensionPoint>({
|
||||
id: 'notifications.processing',
|
||||
});
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type NotificationProcessorFilters = {
|
||||
minSeverity?: NotificationSeverity;
|
||||
maxSeverity?: NotificationSeverity;
|
||||
excludedTopics?: string[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user