feat: notification processor improvements
Notification processor functions are now renamed to `preProcess` and `postProcess`. Additionally, processor name is now required to be returned by `getName`. A new processor functionality `processOptions` was added to process options before sending the notification. Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
@@ -31,7 +31,10 @@ import {
|
||||
RELATION_PARENT_OF,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { NotificationProcessor } from '@backstage/plugin-notifications-node';
|
||||
import {
|
||||
NotificationProcessor,
|
||||
NotificationSendOptions,
|
||||
} from '@backstage/plugin-notifications-node';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import {
|
||||
AuthService,
|
||||
@@ -45,6 +48,7 @@ import {
|
||||
NewNotificationSignal,
|
||||
Notification,
|
||||
NotificationReadSignal,
|
||||
NotificationStatus,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';
|
||||
|
||||
@@ -73,7 +77,7 @@ export async function createRouter(
|
||||
userInfo,
|
||||
discovery,
|
||||
catalog,
|
||||
processors,
|
||||
processors = [],
|
||||
signals,
|
||||
} = options;
|
||||
|
||||
@@ -173,18 +177,54 @@ export async function createRouter(
|
||||
return users;
|
||||
};
|
||||
|
||||
const decorateNotification = async (notification: Notification) => {
|
||||
let ret = notification;
|
||||
for (const processor of processors ?? []) {
|
||||
ret = processor.decorate ? await processor.decorate(ret) : ret;
|
||||
const processOptions = async (opts: NotificationSendOptions) => {
|
||||
let ret = opts;
|
||||
for (const processor of processors) {
|
||||
try {
|
||||
ret = processor.processOptions
|
||||
? await processor.processOptions(ret)
|
||||
: ret;
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error while processing notification options with ${processor.getName()}: ${e}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const processorSendNotification = async (notification: Notification) => {
|
||||
for (const processor of processors ?? []) {
|
||||
if (processor.send) {
|
||||
processor.send(notification);
|
||||
const preProcessNotification = async (
|
||||
notification: Notification,
|
||||
opts: NotificationSendOptions,
|
||||
) => {
|
||||
let ret = notification;
|
||||
for (const processor of processors) {
|
||||
try {
|
||||
ret = processor.preProcess
|
||||
? await processor.preProcess(ret, opts)
|
||||
: ret;
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error while pre processing notification with ${processor.getName()}: ${e}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const postProcessNotification = async (
|
||||
notification: Notification,
|
||||
opts: NotificationSendOptions,
|
||||
) => {
|
||||
for (const processor of processors) {
|
||||
if (processor.postProcess) {
|
||||
try {
|
||||
await processor.postProcess(notification, opts);
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error while post processing notification with ${processor.getName()}: ${e}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -250,7 +290,7 @@ export async function createRouter(
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/status', async (req, res) => {
|
||||
router.get('/status', async (req: Request<any, NotificationStatus>, res) => {
|
||||
const user = await getUser(req);
|
||||
const status = await store.getStatus({ user });
|
||||
res.send(status);
|
||||
@@ -313,17 +353,19 @@ export async function createRouter(
|
||||
|
||||
const sendBroadcastNotification = async (
|
||||
baseNotification: Omit<Notification, 'user' | 'id'>,
|
||||
opts: { scope?: string; origin: string },
|
||||
opts: NotificationSendOptions,
|
||||
origin: string,
|
||||
) => {
|
||||
const { scope, origin } = opts;
|
||||
const { scope } = opts.payload;
|
||||
const broadcastNotification = {
|
||||
...baseNotification,
|
||||
user: null,
|
||||
id: uuid(),
|
||||
};
|
||||
const notification = await decorateNotification({
|
||||
...broadcastNotification,
|
||||
user: '',
|
||||
});
|
||||
const notification = await preProcessNotification(
|
||||
broadcastNotification,
|
||||
opts,
|
||||
);
|
||||
let existingNotification;
|
||||
if (scope) {
|
||||
existingNotification = await store.getExistingScopeBroadcast({
|
||||
@@ -342,7 +384,6 @@ export async function createRouter(
|
||||
} else {
|
||||
await store.saveBroadcast(notification);
|
||||
}
|
||||
processorSendNotification(ret);
|
||||
|
||||
if (signals) {
|
||||
await signals.publish<NewNotificationSignal>({
|
||||
@@ -353,6 +394,7 @@ export async function createRouter(
|
||||
},
|
||||
channel: 'notifications',
|
||||
});
|
||||
postProcessNotification(ret, opts);
|
||||
}
|
||||
return notification;
|
||||
};
|
||||
@@ -360,10 +402,11 @@ export async function createRouter(
|
||||
const sendUserNotifications = async (
|
||||
baseNotification: Omit<Notification, 'user' | 'id'>,
|
||||
users: string[],
|
||||
opts: { scope?: string; origin: string },
|
||||
opts: NotificationSendOptions,
|
||||
origin: string,
|
||||
) => {
|
||||
const notifications = [];
|
||||
const { scope, origin } = opts;
|
||||
const { scope } = opts.payload;
|
||||
const uniqueUsers = [...new Set(users)];
|
||||
for (const user of uniqueUsers) {
|
||||
const userNotification = {
|
||||
@@ -371,7 +414,7 @@ export async function createRouter(
|
||||
id: uuid(),
|
||||
user,
|
||||
};
|
||||
const notification = await decorateNotification(userNotification);
|
||||
const notification = await preProcessNotification(userNotification, opts);
|
||||
|
||||
let existingNotification;
|
||||
if (scope) {
|
||||
@@ -393,7 +436,6 @@ export async function createRouter(
|
||||
await store.saveNotification(notification);
|
||||
}
|
||||
|
||||
processorSendNotification(ret);
|
||||
notifications.push(ret);
|
||||
|
||||
if (signals) {
|
||||
@@ -406,59 +448,68 @@ export async function createRouter(
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
postProcessNotification(ret, opts);
|
||||
}
|
||||
return notifications;
|
||||
};
|
||||
|
||||
// Add new notification
|
||||
router.post('/', async (req, res) => {
|
||||
const { recipients, payload } = req.body;
|
||||
const notifications = [];
|
||||
let users = [];
|
||||
router.post(
|
||||
'/',
|
||||
async (req: Request<any, Notification[], NotificationSendOptions>, res) => {
|
||||
const opts = await processOptions(req.body);
|
||||
const { recipients, payload } = opts;
|
||||
const notifications: Notification[] = [];
|
||||
let users = [];
|
||||
|
||||
const credentials = await httpAuth.credentials(req, { allow: ['service'] });
|
||||
|
||||
const { title, scope } = payload;
|
||||
|
||||
if (!recipients || !title) {
|
||||
logger.error(`Invalid notification request received`);
|
||||
throw new InputError();
|
||||
}
|
||||
|
||||
const origin = credentials.principal.subject;
|
||||
const baseNotification = {
|
||||
payload: {
|
||||
...payload,
|
||||
severity: payload.severity ?? 'normal',
|
||||
},
|
||||
origin,
|
||||
created: new Date(),
|
||||
};
|
||||
|
||||
if (recipients.type === 'broadcast') {
|
||||
const broadcast = await sendBroadcastNotification(baseNotification, {
|
||||
scope,
|
||||
origin,
|
||||
const credentials = await httpAuth.credentials(req, {
|
||||
allow: ['service'],
|
||||
});
|
||||
notifications.push(broadcast);
|
||||
} else {
|
||||
const entityRef = recipients.entityRef;
|
||||
|
||||
try {
|
||||
users = await getUsersForEntityRef(entityRef);
|
||||
} catch (e) {
|
||||
const { title } = payload;
|
||||
|
||||
if (!recipients || !title) {
|
||||
logger.error(`Invalid notification request received`);
|
||||
throw new InputError();
|
||||
}
|
||||
const userNotifications = await sendUserNotifications(
|
||||
baseNotification,
|
||||
users,
|
||||
{ scope, origin },
|
||||
);
|
||||
notifications.push(...userNotifications);
|
||||
}
|
||||
|
||||
res.json(notifications);
|
||||
});
|
||||
const origin = credentials.principal.subject;
|
||||
const baseNotification = {
|
||||
payload: {
|
||||
...payload,
|
||||
severity: payload.severity ?? 'normal',
|
||||
},
|
||||
origin,
|
||||
created: new Date(),
|
||||
};
|
||||
|
||||
if (recipients.type === 'broadcast') {
|
||||
const broadcast = await sendBroadcastNotification(
|
||||
baseNotification,
|
||||
opts,
|
||||
origin,
|
||||
);
|
||||
notifications.push(broadcast);
|
||||
} else {
|
||||
const entityRef = recipients.entityRef;
|
||||
|
||||
try {
|
||||
users = await getUsersForEntityRef(entityRef);
|
||||
} catch (e) {
|
||||
throw new InputError();
|
||||
}
|
||||
const userNotifications = await sendUserNotifications(
|
||||
baseNotification,
|
||||
users,
|
||||
opts,
|
||||
origin,
|
||||
);
|
||||
notifications.push(...userNotifications);
|
||||
}
|
||||
|
||||
res.json(notifications);
|
||||
},
|
||||
);
|
||||
|
||||
router.use(errorHandler());
|
||||
return router;
|
||||
|
||||
Reference in New Issue
Block a user