feat: make notification id logic

This commit is contained in:
Pujit Mehrotra
2024-09-25 13:10:41 -04:00
parent 0e784b8ad6
commit 90f02aa1c5
4 changed files with 115 additions and 19 deletions

39
api/package-lock.json generated
View File

@@ -46,6 +46,7 @@
"dockerode": "^3.3.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"filenamify": "^6.0.0",
"find-process": "^1.4.7",
"global-agent": "^3.0.0",
"graphql": "^16.8.1",
@@ -10186,6 +10187,31 @@
"license": "MIT",
"optional": true
},
"node_modules/filename-reserved-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz",
"integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/filenamify": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz",
"integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==",
"dependencies": {
"filename-reserved-regex": "^3.0.0"
},
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"license": "MIT",
@@ -26222,6 +26248,19 @@
"version": "1.0.0",
"optional": true
},
"filename-reserved-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz",
"integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw=="
},
"filenamify": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz",
"integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==",
"requires": {
"filename-reserved-regex": "^3.0.0"
}
},
"fill-range": {
"version": "7.0.1",
"requires": {

View File

@@ -97,6 +97,7 @@
"dockerode": "^3.3.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"filenamify": "^6.0.0",
"find-process": "^1.4.7",
"global-agent": "^3.0.0",
"graphql": "^16.8.1",

View File

@@ -4,32 +4,32 @@ import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-seria
import { Serializer } from 'multi-ini';
test('MultiIni breaks when serializing an object with a boolean inside', async () => {
const objectToSerialize = {
root: {
anonMode: false,
},
};
const serializer = new Serializer({ keep_quotes: false });
expect(serializer.serialize(objectToSerialize)).toMatchInlineSnapshot(`
const objectToSerialize = {
root: {
anonMode: false,
},
};
const serializer = new Serializer({ keep_quotes: false });
expect(serializer.serialize(objectToSerialize)).toMatchInlineSnapshot(`
"[root]
anonMode=false
"
`)
`);
});
test('MultiIni can safely serialize an object with a boolean inside', async () => {
const objectToSerialize = {
root: {
anonMode: false,
},
};
expect(safelySerializeObjectToIni(objectToSerialize)).toMatchInlineSnapshot(`
const objectToSerialize = {
root: {
anonMode: false,
},
};
expect(safelySerializeObjectToIni(objectToSerialize)).toMatchInlineSnapshot(`
"[root]
anonMode="false"
"
`);
const result = safelySerializeObjectToIni(objectToSerialize);
expect(parse(result)).toMatchInlineSnapshot(`
const result = safelySerializeObjectToIni(objectToSerialize);
expect(parse(result)).toMatchInlineSnapshot(`
{
"root": {
"anonMode": false,
@@ -37,3 +37,33 @@ test('MultiIni can safely serialize an object with a boolean inside', async () =
}
`);
});
test('Can serialize top-level fields', async () => {
const objectToSerialize = {
id: 'an-id',
message: 'hello-world',
number: 1,
float: 1.1,
flag: true,
flag2: false,
item: undefined,
missing: null,
empty: {},
};
const expected = `
"id=an-id
message=hello-world
number=1
float=1.1
flag="true"
flag2="false"
[empty]
"
`
.split('\n')
.map((line) => line.trim())
.join('\n');
expect(safelySerializeObjectToIni({ objectToSerialize })).toMatchInlineSnapshot(expected);
});

View File

@@ -19,7 +19,9 @@ import { FSWatcher, watch } from 'chokidar';
import { FileLoadStatus } from '@app/store/types';
import { pubsub, PUBSUB_CHANNEL } from '@app/core/pubsub';
import { fileExists } from '@app/core/utils/files/file-exists';
import { safelySerializeObjectToIni as encodeIni } from '@app/core/utils/files/safe-ini-serializer';
// import { safelySerializeObjectToIni as encodeIni } from '@app/core/utils/files/safe-ini-serializer';
import { encode as encodeIni } from 'ini';
import { v7 as uuidv7 } from 'uuid';
@Injectable()
export class NotificationsService {
@@ -131,12 +133,13 @@ export class NotificationsService {
*------------------------------------------------------------------------**/
public async createNotification(data: NotificationData): Promise<Notification> {
// const id: string = this.makeNotificationId();
const id: string = '_DEV_CUSTOM_NOTIFICATION_1234.notify'; // placeholder
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);
@@ -144,6 +147,29 @@ export class NotificationsService {
return { ...data, id, type: NotificationType.UNREAD, timestamp: fileData.timestamp };
}
private async makeNotificationId(eventTitle: string, replacement = '_'): Promise<string> {
const { default: filenamify } = await import('filenamify');
const allWhitespace = /\s+/g;
// replace symbols & whitespace with underscores
const prefix = filenamify(eventTitle, { replacement }).replace(allWhitespace, replacement);
/**-----------------------
* Why UUIDv7?
*
* So we can sort notifications chronologically
* without having to read the contents of the files.
*
* This makes it more annoying to manually distinguish id's because
* the start of the uuid encodes the timestamp, and the random bits
* are at the end, so the first few chars of each uuid might be relatively common.
*
* See https://uuid7.com/ for an overview of UUIDv7
* See https://park.is/blog_posts/20240803_extracting_timestamp_from_uuid_v7/ for how
* timestamps are encoded
*------------------------**/
return `${prefix}_${uuidv7()}.notify`;
}
private makeNotificationFileData(notification: NotificationData): NotificationIni {
const { title, subject, description, link, importance } = notification;
const secondsSinceUnixEpoch = Math.floor(Date.now() / 1_000);