mirror of
https://github.com/unraid/api.git
synced 2026-01-03 23:19:54 -06:00
feat(NotificationsService): use existing notifier script to create notifications when possible
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
} from '@app/graphql/generated/api/types';
|
||||
import { NotificationSchema } from '@app/graphql/generated/api/operations';
|
||||
import { mkdir } from 'fs/promises';
|
||||
import { type NotificationIni } from '@app/core/types/states/notification';
|
||||
|
||||
// defined outside `describe` so it's defined inside the `beforeAll`
|
||||
// needed to mock the dynamix import
|
||||
@@ -183,8 +184,12 @@ describe.sequential('NotificationsService', () => {
|
||||
});
|
||||
|
||||
it('generates unique ids', async () => {
|
||||
const notifications = await Promise.all([...new Array(100)].map(() => createNotification()));
|
||||
const notificationIds = new Set(notifications.map((notification) => notification.id));
|
||||
const notifications = await Promise.all(
|
||||
// we break the "rules" here to speed up this test by ~450ms
|
||||
// @ts-expect-error makeNotificationId is private
|
||||
[...new Array(100)].map(() => service.makeNotificationId('test event'))
|
||||
);
|
||||
const notificationIds = new Set(notifications);
|
||||
expect(notificationIds.size).toEqual(notifications.length);
|
||||
});
|
||||
|
||||
@@ -363,3 +368,41 @@ describe.sequential('NotificationsService', () => {
|
||||
expect.soft(overview.archive.total).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe.concurrent('NotificationsService legacy script compatibility', () => {
|
||||
let service: NotificationsService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [NotificationsService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<NotificationsService>(NotificationsService);
|
||||
});
|
||||
|
||||
it.for([['normal'], ['warning'], ['alert']] as const)(
|
||||
'yields correct cli args for %ss',
|
||||
([importance], { expect }) => {
|
||||
const notification: NotificationIni = {
|
||||
event: 'Test Notification',
|
||||
subject: 'Test Subject',
|
||||
description: 'Test Description',
|
||||
importance,
|
||||
link: 'https://unraid.net',
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
const [, args] = service.getLegacyScriptArgs(notification);
|
||||
expect(args).toContain('-i');
|
||||
expect(args).toContain('-e');
|
||||
expect(args).toContain('-s');
|
||||
expect(args).toContain('-d');
|
||||
expect(args).toContain('-l');
|
||||
|
||||
expect(args).toContain(notification.event);
|
||||
expect(args).toContain(notification.subject);
|
||||
expect(args).toContain(notification.description);
|
||||
expect(args).toContain(importance);
|
||||
expect(args).toContain(notification.link);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ import { encode as encodeIni } from 'ini';
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
import { CHOKIDAR_USEPOLLING } from '@app/environment';
|
||||
import { emptyDir } from 'fs-extra';
|
||||
import { execa } from 'execa';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsService {
|
||||
@@ -178,19 +179,48 @@ export class NotificationsService {
|
||||
|
||||
public async createNotification(data: NotificationData): Promise<Notification> {
|
||||
const id: string = await this.makeNotificationId(data.title);
|
||||
const path = join(this.paths().UNREAD, id);
|
||||
|
||||
const fileData = this.makeNotificationFileData(data);
|
||||
this.logger.debug(`[createNotification] FileData: ${JSON.stringify(fileData, null, 4)}`);
|
||||
const ini = encodeIni(fileData);
|
||||
// this.logger.debug(`[createNotification] INI: ${ini}`);
|
||||
|
||||
await writeFile(path, ini);
|
||||
// await this.addToOverview(notification);
|
||||
// make sure both NOTIFICATION_ADDED and NOTIFICATION_OVERVIEW are fired
|
||||
try {
|
||||
const [command, args] = this.getLegacyScriptArgs(fileData);
|
||||
await execa(command, args);
|
||||
} catch (error) {
|
||||
// manually write the file if the script fails
|
||||
this.logger.debug(`[createNotification] legacy notifier failed: ${error}`);
|
||||
this.logger.verbose(`[createNotification] Writing: ${JSON.stringify(fileData, null, 4)}`);
|
||||
|
||||
const path = join(this.paths().UNREAD, id);
|
||||
const ini = encodeIni(fileData);
|
||||
// this.logger.debug(`[createNotification] INI: ${ini}`);
|
||||
await writeFile(path, ini);
|
||||
}
|
||||
|
||||
return this.notificationFileToGqlNotification({ id, type: NotificationType.UNREAD }, fileData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a NotificationIni, returns a tuple containing the command and arguments to be
|
||||
* passed to the legacy notifier script.
|
||||
*
|
||||
* The tuple represents a cli command to create an unraid notification.
|
||||
*
|
||||
* @param notification The notification to be converted to command line arguments.
|
||||
* @returns A 2-element tuple containing the legacy notifier command and arguments.
|
||||
*/
|
||||
public getLegacyScriptArgs(notification: NotificationIni): [string, string[]] {
|
||||
const { event, subject, description, link, importance } = notification;
|
||||
const args = [
|
||||
['-i', importance],
|
||||
['-e', event],
|
||||
['-s', subject],
|
||||
['-d', description],
|
||||
];
|
||||
if (link) {
|
||||
args.push(['-l', link]);
|
||||
}
|
||||
return ['/usr/local/emhttp/webGui/scripts/notify', args.flat()];
|
||||
}
|
||||
|
||||
private async makeNotificationId(eventTitle: string, replacement = '_'): Promise<string> {
|
||||
const { default: filenamify } = await import('filenamify');
|
||||
const allWhitespace = /\s+/g;
|
||||
|
||||
Reference in New Issue
Block a user