feat(notifications): implement severity of notifications
Signed-off-by: Marek Libra <marek.libra@gmail.com>
This commit is contained in:
@@ -14,9 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils';
|
||||
import { DatabaseNotificationsStore } from './DatabaseNotificationsStore';
|
||||
import {
|
||||
DatabaseNotificationsStore,
|
||||
getNumericSeverity,
|
||||
} from './DatabaseNotificationsStore';
|
||||
import { Knex } from 'knex';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
Notification,
|
||||
NotificationSeverity,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
@@ -48,6 +54,7 @@ const id4 = '04e0871e-e60a-4f68-8110-5ae3513f992e';
|
||||
const id5 = '05e0871e-e60a-4f68-8110-5ae3513f992e';
|
||||
const id6 = '06e0871e-e60a-4f68-8110-5ae3513f992e';
|
||||
const id7 = '07e0871e-e60a-4f68-8110-5ae3513f992e';
|
||||
const ids = [id1, id2, id3, id4, id5, id6, id7];
|
||||
|
||||
const now = Date.now();
|
||||
const timeDelay = 5 * 1000; /* 5 secs */
|
||||
@@ -266,6 +273,64 @@ describe.each(databases.eachSupportedId())(
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNotifications filters on severity', () => {
|
||||
beforeEach(async () => {
|
||||
const severities: (NotificationSeverity | undefined)[] = [
|
||||
'normal',
|
||||
undefined,
|
||||
'critical',
|
||||
'high',
|
||||
'low',
|
||||
];
|
||||
await Promise.all(
|
||||
severities.map((severity, idx) =>
|
||||
storage.saveNotification({
|
||||
id: ids[idx],
|
||||
user,
|
||||
origin: 'test-origin',
|
||||
created: new Date(now - idx * timeDelay),
|
||||
payload: {
|
||||
title: severity || 'default',
|
||||
severity,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('normal', async () => {
|
||||
const normal = await storage.getNotifications({
|
||||
user,
|
||||
minimalSeverity: getNumericSeverity('normal'),
|
||||
});
|
||||
expect(normal.map(idOnly)).toEqual([id1, id2, id3, id4]);
|
||||
});
|
||||
|
||||
it('critical', async () => {
|
||||
const critical = await storage.getNotifications({
|
||||
user,
|
||||
minimalSeverity: getNumericSeverity('critical'),
|
||||
});
|
||||
expect(critical.length).toBe(1);
|
||||
expect(critical.at(0)?.id).toEqual(id3);
|
||||
});
|
||||
|
||||
it('high', async () => {
|
||||
const high = await storage.getNotifications({
|
||||
user,
|
||||
minimalSeverity: getNumericSeverity('high'),
|
||||
});
|
||||
expect(high.map(idOnly)).toEqual([id3, id4]);
|
||||
});
|
||||
|
||||
it('low', async () => {
|
||||
const low = await storage.getNotifications({
|
||||
user,
|
||||
minimalSeverity: getNumericSeverity('low'),
|
||||
});
|
||||
expect(low.map(idOnly)).toEqual([id1, id2, id3, id4, id5]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNotifications pagination', () => {
|
||||
beforeEach(async () => {
|
||||
await storage.saveNotification(testNotification1);
|
||||
@@ -439,13 +504,14 @@ describe.each(databases.eachSupportedId())(
|
||||
payload: {
|
||||
title: 'New notification',
|
||||
link: '/scaffolder/task/1234',
|
||||
severity: 'normal',
|
||||
severity: 'low',
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
expect(existing).not.toBeNull();
|
||||
expect(existing?.id).toEqual(id2);
|
||||
expect(existing?.payload.title).toEqual('New notification');
|
||||
expect(existing?.payload.severity).toEqual('low');
|
||||
expect(existing?.read).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,10 @@ import {
|
||||
NotificationModifyOptions,
|
||||
NotificationsStore,
|
||||
} from './NotificationsStore';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
Notification,
|
||||
NotificationSeverity,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
const migrationsDir = resolvePackagePath(
|
||||
@@ -30,6 +33,17 @@ const migrationsDir = resolvePackagePath(
|
||||
'migrations',
|
||||
);
|
||||
|
||||
const severities: NotificationSeverity[] = [
|
||||
'critical',
|
||||
'high',
|
||||
'normal',
|
||||
'low',
|
||||
];
|
||||
export const getNumericSeverity = (severity: string): Number => {
|
||||
const idx = severities.indexOf(severity as NotificationSeverity);
|
||||
return idx >= 0 ? idx : 2 /* normal */;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
private constructor(private readonly db: Knex) {}
|
||||
@@ -70,7 +84,7 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
description: row.description,
|
||||
link: row.link,
|
||||
topic: row.topic,
|
||||
severity: row.severity,
|
||||
severity: severities[row.severity],
|
||||
scope: row.scope,
|
||||
icon: row.icon,
|
||||
},
|
||||
@@ -87,7 +101,7 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
link: notification.payload?.link,
|
||||
title: notification.payload?.title,
|
||||
description: notification.payload?.description,
|
||||
severity: notification.payload?.severity,
|
||||
severity: getNumericSeverity(notification.payload?.severity ?? 'normal'),
|
||||
scope: notification.payload?.scope,
|
||||
saved: notification.saved,
|
||||
read: notification.read,
|
||||
@@ -155,6 +169,10 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
query.whereNull('notification.saved');
|
||||
} // or match both if undefined
|
||||
|
||||
if (options.minimalSeverity !== undefined) {
|
||||
query.where('notification.severity', '<=', options.minimalSeverity);
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
@@ -225,25 +243,28 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
return rows[0] as Notification;
|
||||
}
|
||||
|
||||
async restoreExistingNotification(options: {
|
||||
async restoreExistingNotification({
|
||||
id,
|
||||
notification,
|
||||
}: {
|
||||
id: string;
|
||||
notification: Notification;
|
||||
}) {
|
||||
const query = this.db('notification')
|
||||
.where('id', options.id)
|
||||
.where('user', options.notification.user);
|
||||
.where('id', id)
|
||||
.where('user', notification.user);
|
||||
|
||||
await query.update({
|
||||
title: options.notification.payload.title,
|
||||
description: options.notification.payload.description,
|
||||
link: options.notification.payload.link,
|
||||
topic: options.notification.payload.topic,
|
||||
title: notification.payload.title,
|
||||
description: notification.payload.description,
|
||||
link: notification.payload.link,
|
||||
topic: notification.payload.topic,
|
||||
updated: new Date(),
|
||||
severity: options.notification.payload.severity,
|
||||
severity: getNumericSeverity(notification.payload?.severity ?? 'normal'),
|
||||
read: null,
|
||||
});
|
||||
|
||||
return await this.getNotification(options);
|
||||
return await this.getNotification({ id });
|
||||
}
|
||||
|
||||
async getNotification(options: { id: string }): Promise<Notification | null> {
|
||||
|
||||
@@ -32,6 +32,7 @@ export type NotificationGetOptions = {
|
||||
read?: boolean;
|
||||
saved?: boolean;
|
||||
createdAfter?: Date;
|
||||
minimalSeverity?: Number;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -18,6 +18,7 @@ import express, { Request } from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import {
|
||||
DatabaseNotificationsStore,
|
||||
getNumericSeverity,
|
||||
NotificationGetOptions,
|
||||
} from '../database';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@@ -237,6 +238,11 @@ export async function createRouter(
|
||||
}
|
||||
opts.createdAfter = new Date(sinceEpoch);
|
||||
}
|
||||
if (req.query.minimal_severity) {
|
||||
opts.minimalSeverity = getNumericSeverity(
|
||||
req.query.minimal_severity.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
const [notifications, totalCount] = await Promise.all([
|
||||
store.getNotifications(opts),
|
||||
|
||||
Reference in New Issue
Block a user