From 004e227ed3e969785787b936c01fe4c599d5f10c Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Mon, 6 May 2024 12:26:21 +1000 Subject: [PATCH 1/5] chore: bump `polykey` to 1.4.0 chore: update deps hash --- npmDepsHash | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/npmDepsHash b/npmDepsHash index 629b0383..771b49fb 100644 --- a/npmDepsHash +++ b/npmDepsHash @@ -1 +1 @@ -sha256-pckkN6xZ+tj+srTB5iHCwuHb3EFv0KiTPvFIGTTy1nQ= +sha256-z97XzEPI3Dwl+Ld26FdKVzkstLbTWRgLiKVT8Dh1uZY= diff --git a/package-lock.json b/package-lock.json index 30980998..1c3bafd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "nexpect": "^0.6.0", "node-gyp-build": "^4.4.0", "nodemon": "^3.0.1", - "polykey": "^1.2.3", + "polykey": "^1.4.0", "prettier": "^3.0.0", "shelljs": "^0.8.5", "shx": "^0.3.4", @@ -7569,9 +7569,9 @@ } }, "node_modules/polykey": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/polykey/-/polykey-1.2.3.tgz", - "integrity": "sha512-PwsXsLMVZvv+yR+ry5+9Bzu10yXSvTczFM58GM4zpR1BxoLD7bwUj4gwJGnToAcufdUismpwHhb7CjT6QYYK/A==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/polykey/-/polykey-1.4.0.tgz", + "integrity": "sha512-xurnvH8aJENtjvVbYszDCr0f2KfucQgPfLhpmXHho35gz1KvVDEHoOmaA+qIK0pSksaPVcOxEPt5oQ+Apsde8Q==", "dev": true, "dependencies": { "@matrixai/async-cancellable": "^1.1.1", diff --git a/package.json b/package.json index 677c6fe9..c43fe0d9 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "nexpect": "^0.6.0", "node-gyp-build": "^4.4.0", "nodemon": "^3.0.1", - "polykey": "^1.2.3", + "polykey": "^1.4.0", "prettier": "^3.0.0", "shelljs": "^0.8.5", "shx": "^0.3.4", From c104b0532c4a9b190c95d1abe8a64cdc4f2a4181 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 26 Apr 2024 14:08:30 +1000 Subject: [PATCH 2/5] feat: `notifications inbox` and `notifications outbox` commands --- src/notifications/CommandNotifications.ts | 8 +- src/notifications/CommandSend.ts | 6 + src/notifications/{ => inbox}/CommandClear.ts | 12 +- src/notifications/inbox/CommandInbox.ts | 15 +++ src/notifications/{ => inbox}/CommandRead.ts | 24 ++-- src/notifications/inbox/index.ts | 1 + src/notifications/outbox/CommandClear.ts | 60 +++++++++ src/notifications/outbox/CommandOutbox.ts | 15 +++ src/notifications/outbox/CommandRead.ts | 115 ++++++++++++++++++ src/notifications/outbox/index.ts | 1 + tests/nodes/claim.test.ts | 14 ++- tests/nodes/connections.test.ts | 7 +- tests/notifications/sendReadClear.test.ts | 66 +++++++++- tests/vaults/share.test.ts | 5 +- 14 files changed, 313 insertions(+), 36 deletions(-) rename src/notifications/{ => inbox}/CommandClear.ts (83%) create mode 100644 src/notifications/inbox/CommandInbox.ts rename src/notifications/{ => inbox}/CommandRead.ts (86%) create mode 100644 src/notifications/inbox/index.ts create mode 100644 src/notifications/outbox/CommandClear.ts create mode 100644 src/notifications/outbox/CommandOutbox.ts create mode 100644 src/notifications/outbox/CommandRead.ts create mode 100644 src/notifications/outbox/index.ts diff --git a/src/notifications/CommandNotifications.ts b/src/notifications/CommandNotifications.ts index aff48da1..2a88fe6e 100644 --- a/src/notifications/CommandNotifications.ts +++ b/src/notifications/CommandNotifications.ts @@ -1,5 +1,5 @@ -import CommandClear from './CommandClear'; -import CommandRead from './CommandRead'; +import CommandInbox from './inbox'; +import CommandOutbox from './outbox'; import CommandSend from './CommandSend'; import CommandPolykey from '../CommandPolykey'; @@ -8,8 +8,8 @@ class CommandNotifications extends CommandPolykey { super(...args); this.name('notifications'); this.description('Notifications Operations'); - this.addCommand(new CommandClear(...args)); - this.addCommand(new CommandRead(...args)); + this.addCommand(new CommandInbox(...args)); + this.addCommand(new CommandOutbox(...args)); this.addCommand(new CommandSend(...args)); } } diff --git a/src/notifications/CommandSend.ts b/src/notifications/CommandSend.ts index 0a85d635..ec001af2 100644 --- a/src/notifications/CommandSend.ts +++ b/src/notifications/CommandSend.ts @@ -17,6 +17,10 @@ class CommandSend extends CommandPolykey { binParsers.parseNodeId, ); this.argument('', 'Message to send'); + this.option( + '-r, --retries [number]', + '(optional) Number of retries that should be attempted before giving up', + ); this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); @@ -52,12 +56,14 @@ class CommandSend extends CommandPolykey { }, logger: this.logger.getChild(PolykeyClient.name), }); + const retries = parseInt(options.retries); await binUtils.retryAuthentication( (auth) => pkClient.rpcClient.methods.notificationsSend({ metadata: auth, nodeIdEncoded: nodesUtils.encodeNodeId(nodeId), message: message, + retries: Number.isNaN(retries) ? undefined : retries, }), auth, ); diff --git a/src/notifications/CommandClear.ts b/src/notifications/inbox/CommandClear.ts similarity index 83% rename from src/notifications/CommandClear.ts rename to src/notifications/inbox/CommandClear.ts index 16077b0c..1f8fc153 100644 --- a/src/notifications/CommandClear.ts +++ b/src/notifications/inbox/CommandClear.ts @@ -1,14 +1,14 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; -import CommandPolykey from '../CommandPolykey'; -import * as binUtils from '../utils'; -import * as binOptions from '../utils/options'; -import * as binProcessors from '../utils/processors'; +import CommandPolykey from '../../CommandPolykey'; +import * as binUtils from '../../utils'; +import * as binOptions from '../../utils/options'; +import * as binProcessors from '../../utils/processors'; class CommandClear extends CommandPolykey { constructor(...args: ConstructorParameters) { super(...args); this.name('clear'); - this.description('Clear all Notifications'); + this.description('Clear Inbox Notifications'); this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); @@ -45,7 +45,7 @@ class CommandClear extends CommandPolykey { }); await binUtils.retryAuthentication( (auth) => - pkClient.rpcClient.methods.notificationsClear({ + pkClient.rpcClient.methods.notificationsInboxClear({ metadata: auth, }), auth, diff --git a/src/notifications/inbox/CommandInbox.ts b/src/notifications/inbox/CommandInbox.ts new file mode 100644 index 00000000..cfa9a2f5 --- /dev/null +++ b/src/notifications/inbox/CommandInbox.ts @@ -0,0 +1,15 @@ +import CommandClear from './CommandClear'; +import CommandRead from './CommandRead'; +import CommandPolykey from '../../CommandPolykey'; + +class CommandInbox extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('inbox'); + this.description('Notifications Inbox Operations'); + this.addCommand(new CommandClear(...args)); + this.addCommand(new CommandRead(...args)); + } +} + +export default CommandInbox; diff --git a/src/notifications/CommandRead.ts b/src/notifications/inbox/CommandRead.ts similarity index 86% rename from src/notifications/CommandRead.ts rename to src/notifications/inbox/CommandRead.ts index 4b2811e9..c603eb92 100644 --- a/src/notifications/CommandRead.ts +++ b/src/notifications/inbox/CommandRead.ts @@ -1,23 +1,22 @@ import type { Notification } from 'polykey/dist/notifications/types'; import type PolykeyClient from 'polykey/dist/PolykeyClient'; -import CommandPolykey from '../CommandPolykey'; -import * as binUtils from '../utils'; -import * as binOptions from '../utils/options'; -import * as binProcessors from '../utils/processors'; +import CommandPolykey from '../../CommandPolykey'; +import * as binUtils from '../../utils'; +import * as binOptions from '../../utils/options'; +import * as binProcessors from '../../utils/processors'; class CommandRead extends CommandPolykey { constructor(...args: ConstructorParameters) { super(...args); this.name('read'); - this.description('Display Notifications'); + this.description('Display Inbox Notifications'); this.option( '-u, --unread', '(optional) Flag to only display unread notifications', ); this.option( - '-n, --number [number]', + '-l, --limit [number]', '(optional) Number of notifications to read', - 'all', ); this.option( '-o, --order [order]', @@ -63,14 +62,13 @@ class CommandRead extends CommandPolykey { }); const notificationReadMessages = await binUtils.retryAuthentication( async (auth) => { - const response = await pkClient.rpcClient.methods.notificationsRead( - { + const response = + await pkClient.rpcClient.methods.notificationsInboxRead({ metadata: auth, unread: options.unread, - number: options.number, - order: options.order, - }, - ); + limit: parseInt(options.limit), + order: options.order === 'newest' ? 'desc' : 'asc', + }); const notificationReadMessages: Array<{ notification: Notification; }> = []; diff --git a/src/notifications/inbox/index.ts b/src/notifications/inbox/index.ts new file mode 100644 index 00000000..629e1d46 --- /dev/null +++ b/src/notifications/inbox/index.ts @@ -0,0 +1 @@ +export { default } from './CommandInbox'; diff --git a/src/notifications/outbox/CommandClear.ts b/src/notifications/outbox/CommandClear.ts new file mode 100644 index 00000000..28d5ac37 --- /dev/null +++ b/src/notifications/outbox/CommandClear.ts @@ -0,0 +1,60 @@ +import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import CommandPolykey from '../../CommandPolykey'; +import * as binUtils from '../../utils'; +import * as binOptions from '../../utils/options'; +import * as binProcessors from '../../utils/processors'; + +class CommandClear extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('clear'); + this.description('Clear Outbox Notifications'); + this.addOption(binOptions.nodeId); + this.addOption(binOptions.clientHost); + this.addOption(binOptions.clientPort); + this.action(async (options) => { + const { default: PolykeyClient } = await import( + 'polykey/dist/PolykeyClient' + ); + const clientOptions = await binProcessors.processClientOptions( + options.nodePath, + options.nodeId, + options.clientHost, + options.clientPort, + this.fs, + this.logger.getChild(binProcessors.processClientOptions.name), + ); + const auth = await binProcessors.processAuthentication( + options.passwordFile, + this.fs, + ); + + let pkClient: PolykeyClient; + this.exitHandlers.handlers.push(async () => { + if (pkClient != null) await pkClient.stop(); + }); + try { + pkClient = await PolykeyClient.createPolykeyClient({ + nodeId: clientOptions.nodeId, + host: clientOptions.clientHost, + port: clientOptions.clientPort, + options: { + nodePath: options.nodePath, + }, + logger: this.logger.getChild(PolykeyClient.name), + }); + await binUtils.retryAuthentication( + (auth) => + pkClient.rpcClient.methods.notificationsOutboxClear({ + metadata: auth, + }), + auth, + ); + } finally { + if (pkClient! != null) await pkClient.stop(); + } + }); + } +} + +export default CommandClear; diff --git a/src/notifications/outbox/CommandOutbox.ts b/src/notifications/outbox/CommandOutbox.ts new file mode 100644 index 00000000..40198d71 --- /dev/null +++ b/src/notifications/outbox/CommandOutbox.ts @@ -0,0 +1,15 @@ +import CommandClear from './CommandClear'; +import CommandRead from './CommandRead'; +import CommandPolykey from '../../CommandPolykey'; + +class CommandOutbox extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('outbox'); + this.description('Notifications Outbox Operations'); + this.addCommand(new CommandClear(...args)); + this.addCommand(new CommandRead(...args)); + } +} + +export default CommandOutbox; diff --git a/src/notifications/outbox/CommandRead.ts b/src/notifications/outbox/CommandRead.ts new file mode 100644 index 00000000..035acf12 --- /dev/null +++ b/src/notifications/outbox/CommandRead.ts @@ -0,0 +1,115 @@ +import type { Notification } from 'polykey/dist/notifications/types'; +import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import type { NotificationOutboxMessage } from 'polykey/dist/client/types'; +import CommandPolykey from '../../CommandPolykey'; +import * as binUtils from '../../utils'; +import * as binOptions from '../../utils/options'; +import * as binProcessors from '../../utils/processors'; + +class CommandRead extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('read'); + this.description('Display Outbox Notifications'); + this.option( + '-l, --limit [number]', + '(optional) Number of notifications to read', + ); + this.option( + '-o, --order [order]', + '(optional) Order to read notifications', + 'newest', + ); + this.addOption(binOptions.nodeId); + this.addOption(binOptions.clientHost); + this.addOption(binOptions.clientPort); + this.action(async (options) => { + const { default: PolykeyClient } = await import( + 'polykey/dist/PolykeyClient' + ); + const notificationsUtils = await import( + 'polykey/dist/notifications/utils' + ); + const clientOptions = await binProcessors.processClientOptions( + options.nodePath, + options.nodeId, + options.clientHost, + options.clientPort, + this.fs, + this.logger.getChild(binProcessors.processClientOptions.name), + ); + const meta = await binProcessors.processAuthentication( + options.passwordFile, + this.fs, + ); + + let pkClient: PolykeyClient; + this.exitHandlers.handlers.push(async () => { + if (pkClient != null) await pkClient.stop(); + }); + try { + pkClient = await PolykeyClient.createPolykeyClient({ + nodeId: clientOptions.nodeId, + host: clientOptions.clientHost, + port: clientOptions.clientPort, + options: { + nodePath: options.nodePath, + }, + logger: this.logger.getChild(PolykeyClient.name), + }); + const notificationReadMessages = await binUtils.retryAuthentication( + async (auth) => { + const response = + await pkClient.rpcClient.methods.notificationsOutboxRead({ + metadata: auth, + limit: parseInt(options.limit), + order: options.order === 'newest' ? 'desc' : 'asc', + }); + const notificationReadMessages: Array<{ + notification: Notification; + taskMetadata: NotificationOutboxMessage['taskMetadata']; + }> = []; + for await (const notificationMessage of response) { + const notification = notificationsUtils.parseNotification( + notificationMessage.notification, + ); + notificationReadMessages.push({ + notification, + taskMetadata: notificationMessage.taskMetadata, + }); + } + return notificationReadMessages; + }, + meta, + ); + if (notificationReadMessages.length === 0) { + process.stderr.write('No notifications pending\n'); + } + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: notificationReadMessages, + }), + ); + } else { + for (const notificationReadMessage of notificationReadMessages) { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: { + notificiation: notificationReadMessage.notification, + taskMetadata: notificationReadMessage.taskMetadata, + }, + }), + ); + } + } + } finally { + if (pkClient! != null) await pkClient.stop(); + } + }); + } +} + +export default CommandRead; diff --git a/src/notifications/outbox/index.ts b/src/notifications/outbox/index.ts new file mode 100644 index 00000000..ff65853b --- /dev/null +++ b/src/notifications/outbox/index.ts @@ -0,0 +1 @@ +export { default } from './CommandOutbox'; diff --git a/tests/nodes/claim.test.ts b/tests/nodes/claim.test.ts index b33a0c12..f0fe71a3 100644 --- a/tests/nodes/claim.test.ts +++ b/tests/nodes/claim.test.ts @@ -96,8 +96,11 @@ describe('claim', () => { expect(stderr).toContain(remoteIdEncoded); }); test('sends a gestalt invite (force invite)', async () => { - await remoteNode.notificationsManager.sendNotification(localId, { - type: 'GestaltInvite', + await remoteNode.notificationsManager.sendNotification({ + nodeId: localId, + data: { + type: 'GestaltInvite', + }, }); const { exitCode, stdout, stderr } = await testUtils.pkStdio( ['nodes', 'claim', remoteIdEncoded, '--force-invite', '--format', 'json'], @@ -114,8 +117,11 @@ describe('claim', () => { expect(stderr).toContain(nodesUtils.encodeNodeId(remoteId)); }); test('claims a node', async () => { - await remoteNode.notificationsManager.sendNotification(localId, { - type: 'GestaltInvite', + await remoteNode.notificationsManager.sendNotification({ + nodeId: localId, + data: { + type: 'GestaltInvite', + }, }); const { exitCode, stdout, stderr } = await testUtils.pkStdio( ['nodes', 'claim', remoteIdEncoded, '--format', 'json'], diff --git a/tests/nodes/connections.test.ts b/tests/nodes/connections.test.ts index fe41375d..52ab1154 100644 --- a/tests/nodes/connections.test.ts +++ b/tests/nodes/connections.test.ts @@ -87,8 +87,11 @@ describe('connections', () => { }); }); test('Correctly list connection information, and not list auth data', async () => { - await remoteNode.notificationsManager.sendNotification(localId, { - type: 'GestaltInvite', + await remoteNode.notificationsManager.sendNotification({ + nodeId: localId, + data: { + type: 'GestaltInvite', + }, }); const { exitCode } = await testUtils.pkStdio( ['nodes', 'claim', remoteIdEncoded, '--force-invite'], diff --git a/tests/notifications/sendReadClear.test.ts b/tests/notifications/sendReadClear.test.ts index 17ecc18f..1623f5a9 100644 --- a/tests/notifications/sendReadClear.test.ts +++ b/tests/notifications/sendReadClear.test.ts @@ -163,7 +163,7 @@ describe('send/read/claim', () => { expect(exitCode).toBe(0); // Read notifications ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'read', '--format', 'json'], + ['notifications', 'inbox', 'read', '--format', 'json'], { env: { PK_NODE_PATH: receiverAgentDir, @@ -204,7 +204,7 @@ describe('send/read/claim', () => { }); // Read only unread (none) ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'read', '--unread', '--format', 'json'], + ['notifications', 'inbox', 'read', '--unread', '--format', 'json'], { env: { PK_NODE_PATH: receiverAgentDir, @@ -218,7 +218,14 @@ describe('send/read/claim', () => { expect(readNotificationMessages).toHaveLength(0); // Read notifications on reverse order ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'read', '--order=oldest', '--format', 'json'], + [ + 'notifications', + 'inbox', + 'read', + '--order=oldest', + '--format', + 'json', + ], { env: { PK_NODE_PATH: receiverAgentDir, @@ -259,7 +266,7 @@ describe('send/read/claim', () => { }); // Read only one notification ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'read', '--number=1', '--format', 'json'], + ['notifications', 'inbox', 'read', '--limit=1', '--format', 'json'], { env: { PK_NODE_PATH: receiverAgentDir, @@ -281,7 +288,7 @@ describe('send/read/claim', () => { isRead: true, }); // Clear notifications - await testUtils.pkExec(['notifications', 'clear'], { + await testUtils.pkExec(['notifications', 'inbox', 'clear'], { env: { PK_NODE_PATH: receiverAgentDir, PK_PASSWORD: receiverAgentPassword, @@ -290,7 +297,7 @@ describe('send/read/claim', () => { }); // Check there are no more notifications ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'read', '--format', 'json'], + ['notifications', 'inbox', 'read', '--format', 'json'], { env: { PK_NODE_PATH: receiverAgentDir, @@ -302,6 +309,53 @@ describe('send/read/claim', () => { expect(exitCode).toBe(0); readNotificationMessages = JSON.parse(stdout); expect(readNotificationMessages).toHaveLength(0); + // Sending notifications to invalid nodeIds for outbox + const invalidNodeId = 'v53jhuvmfkc4tnngvor2op7c12jmohqa704pg3pcvrdui9j5kh900'; + ({ exitCode } = await testUtils.pkExec( + [ + 'notifications', + 'send', + invalidNodeId, + 'test message 1', + '--retries', + '0' + ], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + // Read outbox + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'outbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: receiverAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + let sentNotificationMessages = JSON.parse(stdout); + expect(sentNotificationMessages).toHaveLength(1); + expect(sentNotificationMessages[0]).toMatchObject({ + notification: { + data: { + type: 'General', + message: 'test message 1', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: invalidNodeId, + }, + taskMetadata: { + remainingRetries: 0 + } + }); }, globalThis.defaultTimeout * 3, ); diff --git a/tests/vaults/share.test.ts b/tests/vaults/share.test.ts index c7e9f04f..a4905593 100644 --- a/tests/vaults/share.test.ts +++ b/tests/vaults/share.test.ts @@ -90,7 +90,10 @@ describe('commandShare', () => { ); try { // We don't want to actually send a notification - mockedSendNotification.mockImplementation(async (_) => {}); + mockedSendNotification.mockResolvedValue({ + notificationId: ids.generateNotificationIdFromTimestamp(Date.now()), + sendP: Promise.resolve(), + }); const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); const vaultIdEncoded = vaultsUtils.encodeVaultId(vaultId); const targetNodeId = nodeIdGenerator(); From c5cee9ef8dd5b67cf057786c1ba0c88c5d3696bd Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 3 May 2024 14:06:32 +1000 Subject: [PATCH 3/5] fix: split tests into inbox and outbox for `notifications` commands --- .../{ => inbox}/sendReadClear.test.ts | 65 +----- .../outbox/sendReadClear.test.ts | 210 ++++++++++++++++++ 2 files changed, 219 insertions(+), 56 deletions(-) rename tests/notifications/{ => inbox}/sendReadClear.test.ts (84%) create mode 100644 tests/notifications/outbox/sendReadClear.test.ts diff --git a/tests/notifications/sendReadClear.test.ts b/tests/notifications/inbox/sendReadClear.test.ts similarity index 84% rename from tests/notifications/sendReadClear.test.ts rename to tests/notifications/inbox/sendReadClear.test.ts index 1623f5a9..08a03cf2 100644 --- a/tests/notifications/sendReadClear.test.ts +++ b/tests/notifications/inbox/sendReadClear.test.ts @@ -5,10 +5,10 @@ import path from 'path'; import fs from 'fs'; import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; import * as nodesUtils from 'polykey/dist/nodes/utils'; -import * as testUtils from '../utils'; +import * as testUtils from '../../utils'; describe('send/read/claim', () => { - const logger = new Logger('send/read/clear test', LogLevel.WARN, [ + const logger = new Logger('inbox send/read/clear test', LogLevel.WARN, [ new StreamHandler(), ]); let dataDir: string; @@ -161,7 +161,7 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - // Read notifications + // Read inbox notifications ({ exitCode, stdout } = await testUtils.pkExec( ['notifications', 'inbox', 'read', '--format', 'json'], { @@ -202,7 +202,7 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - // Read only unread (none) + // Read inbox only unread (none) ({ exitCode, stdout } = await testUtils.pkExec( ['notifications', 'inbox', 'read', '--unread', '--format', 'json'], { @@ -216,7 +216,7 @@ describe('send/read/claim', () => { expect(exitCode).toBe(0); readNotificationMessages = JSON.parse(stdout); expect(readNotificationMessages).toHaveLength(0); - // Read notifications on reverse order + // Read inbox notifications on reverse order ({ exitCode, stdout } = await testUtils.pkExec( [ 'notifications', @@ -264,9 +264,9 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - // Read only one notification + // Read only one inbox notification ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'inbox', 'read', '--limit=1', '--format', 'json'], + ['notifications', 'inbox', 'read', '--limit', '1', '--format', 'json'], { env: { PK_NODE_PATH: receiverAgentDir, @@ -287,7 +287,7 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - // Clear notifications + // Clear inbox notifications await testUtils.pkExec(['notifications', 'inbox', 'clear'], { env: { PK_NODE_PATH: receiverAgentDir, @@ -295,7 +295,7 @@ describe('send/read/claim', () => { }, cwd: receiverAgentDir, }); - // Check there are no more notifications + // Check there are no more inbox notifications ({ exitCode, stdout } = await testUtils.pkExec( ['notifications', 'inbox', 'read', '--format', 'json'], { @@ -309,53 +309,6 @@ describe('send/read/claim', () => { expect(exitCode).toBe(0); readNotificationMessages = JSON.parse(stdout); expect(readNotificationMessages).toHaveLength(0); - // Sending notifications to invalid nodeIds for outbox - const invalidNodeId = 'v53jhuvmfkc4tnngvor2op7c12jmohqa704pg3pcvrdui9j5kh900'; - ({ exitCode } = await testUtils.pkExec( - [ - 'notifications', - 'send', - invalidNodeId, - 'test message 1', - '--retries', - '0' - ], - { - env: { - PK_NODE_PATH: senderAgentDir, - PK_PASSWORD: senderAgentPassword, - }, - cwd: senderAgentDir, - }, - )); - expect(exitCode).toBe(0); - // Read outbox - ({ exitCode, stdout } = await testUtils.pkExec( - ['notifications', 'outbox', 'read', '--format', 'json'], - { - env: { - PK_NODE_PATH: senderAgentDir, - PK_PASSWORD: receiverAgentPassword, - }, - cwd: senderAgentDir, - }, - )); - expect(exitCode).toBe(0); - let sentNotificationMessages = JSON.parse(stdout); - expect(sentNotificationMessages).toHaveLength(1); - expect(sentNotificationMessages[0]).toMatchObject({ - notification: { - data: { - type: 'General', - message: 'test message 1', - }, - iss: nodesUtils.encodeNodeId(senderId), - sub: invalidNodeId, - }, - taskMetadata: { - remainingRetries: 0 - } - }); }, globalThis.defaultTimeout * 3, ); diff --git a/tests/notifications/outbox/sendReadClear.test.ts b/tests/notifications/outbox/sendReadClear.test.ts new file mode 100644 index 00000000..c5012f56 --- /dev/null +++ b/tests/notifications/outbox/sendReadClear.test.ts @@ -0,0 +1,210 @@ +import type { NodeId } from 'polykey/dist/ids/types'; +import type { Notification } from 'polykey/dist/notifications/types'; +import type { StatusLive } from 'polykey/dist/status/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as testUtils from '../../utils'; + +describe('send/read/claim', () => { + const logger = new Logger('outbox send/read/clear test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + let senderId: NodeId; + let senderAgentStatus: StatusLive; + let senderAgentClose: () => Promise; + let senderAgentDir: string; + let senderAgentPassword: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + ({ + agentStatus: senderAgentStatus, + agentClose: senderAgentClose, + agentDir: senderAgentDir, + agentPassword: senderAgentPassword, + } = await testUtils.setupTestAgent(logger)); + senderId = senderAgentStatus.data.nodeId; + }); + afterEach(async () => { + await senderAgentClose(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test( + 'sends, receives, and clears notifications', + async () => { + const receiverId = + 'v0000000000000000000000000000000000000000000000000000'; + let exitCode: number, stdout: string; + let readNotificationMessages: Array<{ notification: Notification }>; + // Send some notifications + ({ exitCode } = await testUtils.pkExec( + ['notifications', 'send', receiverId, 'test message 1'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + ({ exitCode } = await testUtils.pkExec( + ['notifications', 'send', receiverId, 'test message 2'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + ({ exitCode } = await testUtils.pkExec( + ['notifications', 'send', receiverId, 'test message 3'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + // Read outbox notifications + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'outbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(3); + expect(readNotificationMessages[0].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 3', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + expect(readNotificationMessages[1].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 2', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + expect(readNotificationMessages[2].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 1', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + // Read outbox notifications on reverse order + ({ exitCode, stdout } = await testUtils.pkExec( + [ + 'notifications', + 'outbox', + 'read', + '--order=oldest', + '--format', + 'json', + ], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(3); + expect(readNotificationMessages[0].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 1', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + expect(readNotificationMessages[1].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 2', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + expect(readNotificationMessages[2].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 3', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + // Read only one outbox notification + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'outbox', 'read', '--limit', '1', '--format', 'json'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(1); + expect(readNotificationMessages[0].notification).toMatchObject({ + data: { + type: 'General', + message: 'test message 3', + }, + iss: nodesUtils.encodeNodeId(senderId), + sub: receiverId, + }); + // Clear outbox notifications + await testUtils.pkExec(['notifications', 'outbox', 'clear'], { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }); + // Check there are no more outbox notifications + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'outbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(0); + }, + globalThis.defaultTimeout * 3, + ); +}); From 7dccfb1c6c8799c90a0eb4555b36c0e60b6a54f7 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 3 May 2024 14:07:53 +1000 Subject: [PATCH 4/5] feat: `notifications inbox remove` and `notifications outbox remove` commands --- src/notifications/inbox/CommandInbox.ts | 2 + src/notifications/inbox/CommandRemove.ts | 69 +++++++++++++++++++ src/notifications/outbox/CommandOutbox.ts | 2 + src/notifications/outbox/CommandRemove.ts | 69 +++++++++++++++++++ src/utils/parsers.ts | 4 ++ ...ar.test.ts => sendReadRemoveClear.test.ts} | 58 +++++++++++++++- ...ar.test.ts => sendReadRemoveClear.test.ts} | 58 +++++++++++++++- 7 files changed, 256 insertions(+), 6 deletions(-) create mode 100644 src/notifications/inbox/CommandRemove.ts create mode 100644 src/notifications/outbox/CommandRemove.ts rename tests/notifications/inbox/{sendReadClear.test.ts => sendReadRemoveClear.test.ts} (84%) rename tests/notifications/outbox/{sendReadClear.test.ts => sendReadRemoveClear.test.ts} (78%) diff --git a/src/notifications/inbox/CommandInbox.ts b/src/notifications/inbox/CommandInbox.ts index cfa9a2f5..3783ebf4 100644 --- a/src/notifications/inbox/CommandInbox.ts +++ b/src/notifications/inbox/CommandInbox.ts @@ -1,5 +1,6 @@ import CommandClear from './CommandClear'; import CommandRead from './CommandRead'; +import CommandRemove from './CommandRemove'; import CommandPolykey from '../../CommandPolykey'; class CommandInbox extends CommandPolykey { @@ -9,6 +10,7 @@ class CommandInbox extends CommandPolykey { this.description('Notifications Inbox Operations'); this.addCommand(new CommandClear(...args)); this.addCommand(new CommandRead(...args)); + this.addCommand(new CommandRemove(...args)); } } diff --git a/src/notifications/inbox/CommandRemove.ts b/src/notifications/inbox/CommandRemove.ts new file mode 100644 index 00000000..c189088e --- /dev/null +++ b/src/notifications/inbox/CommandRemove.ts @@ -0,0 +1,69 @@ +import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import * as notificationsUtils from 'polykey/dist/notifications/utils'; +import CommandPolykey from '../../CommandPolykey'; +import * as binUtils from '../../utils'; +import * as binOptions from '../../utils/options'; +import * as binProcessors from '../../utils/processors'; +import * as binParsers from '../../utils/parsers'; + +class CommandRemove extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('remove'); + this.description('Remove a Notification in the Inbox'); + this.argument( + '', + 'Id of the notification to remove', + binParsers.parseNotificationId, + ); + this.addOption(binOptions.nodeId); + this.addOption(binOptions.clientHost); + this.addOption(binOptions.clientPort); + this.action(async (notificationId, options) => { + const { default: PolykeyClient } = await import( + 'polykey/dist/PolykeyClient' + ); + const clientOptions = await binProcessors.processClientOptions( + options.nodePath, + options.nodeId, + options.clientHost, + options.clientPort, + this.fs, + this.logger.getChild(binProcessors.processClientOptions.name), + ); + const auth = await binProcessors.processAuthentication( + options.passwordFile, + this.fs, + ); + + let pkClient: PolykeyClient; + this.exitHandlers.handlers.push(async () => { + if (pkClient != null) await pkClient.stop(); + }); + try { + pkClient = await PolykeyClient.createPolykeyClient({ + nodeId: clientOptions.nodeId, + host: clientOptions.clientHost, + port: clientOptions.clientPort, + options: { + nodePath: options.nodePath, + }, + logger: this.logger.getChild(PolykeyClient.name), + }); + await binUtils.retryAuthentication( + (auth) => + pkClient.rpcClient.methods.notificationsInboxRemove({ + notificationIdEncoded: + notificationsUtils.encodeNotificationId(notificationId), + metadata: auth, + }), + auth, + ); + } finally { + if (pkClient! != null) await pkClient.stop(); + } + }); + } +} + +export default CommandRemove; diff --git a/src/notifications/outbox/CommandOutbox.ts b/src/notifications/outbox/CommandOutbox.ts index 40198d71..5c3fbe96 100644 --- a/src/notifications/outbox/CommandOutbox.ts +++ b/src/notifications/outbox/CommandOutbox.ts @@ -1,5 +1,6 @@ import CommandClear from './CommandClear'; import CommandRead from './CommandRead'; +import CommandRemove from './CommandRemove'; import CommandPolykey from '../../CommandPolykey'; class CommandOutbox extends CommandPolykey { @@ -9,6 +10,7 @@ class CommandOutbox extends CommandPolykey { this.description('Notifications Outbox Operations'); this.addCommand(new CommandClear(...args)); this.addCommand(new CommandRead(...args)); + this.addCommand(new CommandRemove(...args)); } } diff --git a/src/notifications/outbox/CommandRemove.ts b/src/notifications/outbox/CommandRemove.ts new file mode 100644 index 00000000..8861c748 --- /dev/null +++ b/src/notifications/outbox/CommandRemove.ts @@ -0,0 +1,69 @@ +import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import * as notificationsUtils from 'polykey/dist/notifications/utils'; +import CommandPolykey from '../../CommandPolykey'; +import * as binUtils from '../../utils'; +import * as binOptions from '../../utils/options'; +import * as binProcessors from '../../utils/processors'; +import * as binParsers from '../../utils/parsers'; + +class CommandRemove extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('remove'); + this.description('Remove a Pending Notification to be Sent in the Outbox'); + this.argument( + '', + 'Id of the notification to remove', + binParsers.parseNotificationId, + ); + this.addOption(binOptions.nodeId); + this.addOption(binOptions.clientHost); + this.addOption(binOptions.clientPort); + this.action(async (notificationId, options) => { + const { default: PolykeyClient } = await import( + 'polykey/dist/PolykeyClient' + ); + const clientOptions = await binProcessors.processClientOptions( + options.nodePath, + options.nodeId, + options.clientHost, + options.clientPort, + this.fs, + this.logger.getChild(binProcessors.processClientOptions.name), + ); + const auth = await binProcessors.processAuthentication( + options.passwordFile, + this.fs, + ); + + let pkClient: PolykeyClient; + this.exitHandlers.handlers.push(async () => { + if (pkClient != null) await pkClient.stop(); + }); + try { + pkClient = await PolykeyClient.createPolykeyClient({ + nodeId: clientOptions.nodeId, + host: clientOptions.clientHost, + port: clientOptions.clientPort, + options: { + nodePath: options.nodePath, + }, + logger: this.logger.getChild(PolykeyClient.name), + }); + await binUtils.retryAuthentication( + (auth) => + pkClient.rpcClient.methods.notificationsOutboxRemove({ + notificationIdEncoded: + notificationsUtils.encodeNotificationId(notificationId), + metadata: auth, + }), + auth, + ); + } finally { + if (pkClient! != null) await pkClient.stop(); + } + }); + } +} + +export default CommandRemove; diff --git a/src/utils/parsers.ts b/src/utils/parsers.ts index 307d0c45..1dc5b513 100644 --- a/src/utils/parsers.ts +++ b/src/utils/parsers.ts @@ -105,6 +105,9 @@ const parseNodeId: (data: string) => ids.NodeId = validateParserToArgParser( ids.parseNodeId, ); +const parseNotificationId: (data: string) => ids.NotificationId = + validateParserToArgParser(ids.parseNotificationId); + const parseGestaltId: (data: string) => ids.GestaltId = validateParserToArgParser(ids.parseGestaltId); @@ -153,6 +156,7 @@ export { parseInteger, parseNumber, parseNodeId, + parseNotificationId, parseGestaltId, parseGestaltIdentityId, parseGestaltAction, diff --git a/tests/notifications/inbox/sendReadClear.test.ts b/tests/notifications/inbox/sendReadRemoveClear.test.ts similarity index 84% rename from tests/notifications/inbox/sendReadClear.test.ts rename to tests/notifications/inbox/sendReadRemoveClear.test.ts index 08a03cf2..36b4e593 100644 --- a/tests/notifications/inbox/sendReadClear.test.ts +++ b/tests/notifications/inbox/sendReadRemoveClear.test.ts @@ -8,9 +8,11 @@ import * as nodesUtils from 'polykey/dist/nodes/utils'; import * as testUtils from '../../utils'; describe('send/read/claim', () => { - const logger = new Logger('inbox send/read/clear test', LogLevel.WARN, [ - new StreamHandler(), - ]); + const logger = new Logger( + 'inbox send/read/remove/clear test', + LogLevel.WARN, + [new StreamHandler()], + ); let dataDir: string; let senderId: NodeId; let senderHost: string; @@ -287,6 +289,56 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); + // Get a notificationId to remove + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'inbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: receiverAgentDir, + PK_PASSWORD: receiverAgentPassword, + }, + cwd: receiverAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(3); + const deletedNotificationIdEncoded = + readNotificationMessages[0].notification.notificationIdEncoded; + // Remove inbox notificataions + await testUtils.pkExec( + ['notifications', 'inbox', 'remove', deletedNotificationIdEncoded], + { + env: { + PK_NODE_PATH: receiverAgentDir, + PK_PASSWORD: receiverAgentPassword, + }, + cwd: receiverAgentDir, + }, + ); + // Check that the notification no longer exists + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'inbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: receiverAgentDir, + PK_PASSWORD: receiverAgentPassword, + }, + cwd: receiverAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(2); + expect(readNotificationMessages).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + notification: { + notificationIdEncoded: deletedNotificationIdEncoded, + }, + }), + ]), + ); // Clear inbox notifications await testUtils.pkExec(['notifications', 'inbox', 'clear'], { env: { diff --git a/tests/notifications/outbox/sendReadClear.test.ts b/tests/notifications/outbox/sendReadRemoveClear.test.ts similarity index 78% rename from tests/notifications/outbox/sendReadClear.test.ts rename to tests/notifications/outbox/sendReadRemoveClear.test.ts index c5012f56..ef206a32 100644 --- a/tests/notifications/outbox/sendReadClear.test.ts +++ b/tests/notifications/outbox/sendReadRemoveClear.test.ts @@ -8,9 +8,11 @@ import * as nodesUtils from 'polykey/dist/nodes/utils'; import * as testUtils from '../../utils'; describe('send/read/claim', () => { - const logger = new Logger('outbox send/read/clear test', LogLevel.WARN, [ - new StreamHandler(), - ]); + const logger = new Logger( + 'outbox send/read/remove/clear test', + LogLevel.WARN, + [new StreamHandler()], + ); let dataDir: string; let senderId: NodeId; let senderAgentStatus: StatusLive; @@ -182,6 +184,56 @@ describe('send/read/claim', () => { iss: nodesUtils.encodeNodeId(senderId), sub: receiverId, }); + // Get a notificationId to remove + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'outbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(3); + const deletedNotificationIdEncoded = + readNotificationMessages[0].notification.notificationIdEncoded; + // Remove outbox notificataions + await testUtils.pkExec( + ['notifications', 'outbox', 'remove', deletedNotificationIdEncoded], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + ); + // Check that the notification no longer exists + ({ exitCode, stdout } = await testUtils.pkExec( + ['notifications', 'outbox', 'read', '--format', 'json'], + { + env: { + PK_NODE_PATH: senderAgentDir, + PK_PASSWORD: senderAgentPassword, + }, + cwd: senderAgentDir, + }, + )); + expect(exitCode).toBe(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(2); + expect(readNotificationMessages).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + notification: { + notificationIdEncoded: deletedNotificationIdEncoded, + }, + }), + ]), + ); // Clear outbox notifications await testUtils.pkExec(['notifications', 'outbox', 'clear'], { env: { From 8335af80ff6012f64bb44de0292406838719802a Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Mon, 6 May 2024 12:22:59 +1000 Subject: [PATCH 5/5] fix: moved `CommandInbox` and `CommandOutbox` to `index.ts`s --- src/notifications/inbox/CommandInbox.ts | 17 ----------------- src/notifications/inbox/index.ts | 18 +++++++++++++++++- src/notifications/outbox/CommandOutbox.ts | 17 ----------------- src/notifications/outbox/index.ts | 18 +++++++++++++++++- 4 files changed, 34 insertions(+), 36 deletions(-) delete mode 100644 src/notifications/inbox/CommandInbox.ts delete mode 100644 src/notifications/outbox/CommandOutbox.ts diff --git a/src/notifications/inbox/CommandInbox.ts b/src/notifications/inbox/CommandInbox.ts deleted file mode 100644 index 3783ebf4..00000000 --- a/src/notifications/inbox/CommandInbox.ts +++ /dev/null @@ -1,17 +0,0 @@ -import CommandClear from './CommandClear'; -import CommandRead from './CommandRead'; -import CommandRemove from './CommandRemove'; -import CommandPolykey from '../../CommandPolykey'; - -class CommandInbox extends CommandPolykey { - constructor(...args: ConstructorParameters) { - super(...args); - this.name('inbox'); - this.description('Notifications Inbox Operations'); - this.addCommand(new CommandClear(...args)); - this.addCommand(new CommandRead(...args)); - this.addCommand(new CommandRemove(...args)); - } -} - -export default CommandInbox; diff --git a/src/notifications/inbox/index.ts b/src/notifications/inbox/index.ts index 629e1d46..3783ebf4 100644 --- a/src/notifications/inbox/index.ts +++ b/src/notifications/inbox/index.ts @@ -1 +1,17 @@ -export { default } from './CommandInbox'; +import CommandClear from './CommandClear'; +import CommandRead from './CommandRead'; +import CommandRemove from './CommandRemove'; +import CommandPolykey from '../../CommandPolykey'; + +class CommandInbox extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('inbox'); + this.description('Notifications Inbox Operations'); + this.addCommand(new CommandClear(...args)); + this.addCommand(new CommandRead(...args)); + this.addCommand(new CommandRemove(...args)); + } +} + +export default CommandInbox; diff --git a/src/notifications/outbox/CommandOutbox.ts b/src/notifications/outbox/CommandOutbox.ts deleted file mode 100644 index 5c3fbe96..00000000 --- a/src/notifications/outbox/CommandOutbox.ts +++ /dev/null @@ -1,17 +0,0 @@ -import CommandClear from './CommandClear'; -import CommandRead from './CommandRead'; -import CommandRemove from './CommandRemove'; -import CommandPolykey from '../../CommandPolykey'; - -class CommandOutbox extends CommandPolykey { - constructor(...args: ConstructorParameters) { - super(...args); - this.name('outbox'); - this.description('Notifications Outbox Operations'); - this.addCommand(new CommandClear(...args)); - this.addCommand(new CommandRead(...args)); - this.addCommand(new CommandRemove(...args)); - } -} - -export default CommandOutbox; diff --git a/src/notifications/outbox/index.ts b/src/notifications/outbox/index.ts index ff65853b..5c3fbe96 100644 --- a/src/notifications/outbox/index.ts +++ b/src/notifications/outbox/index.ts @@ -1 +1,17 @@ -export { default } from './CommandOutbox'; +import CommandClear from './CommandClear'; +import CommandRead from './CommandRead'; +import CommandRemove from './CommandRemove'; +import CommandPolykey from '../../CommandPolykey'; + +class CommandOutbox extends CommandPolykey { + constructor(...args: ConstructorParameters) { + super(...args); + this.name('outbox'); + this.description('Notifications Outbox Operations'); + this.addCommand(new CommandClear(...args)); + this.addCommand(new CommandRead(...args)); + this.addCommand(new CommandRemove(...args)); + } +} + +export default CommandOutbox;