From bfac7ab8b77c7234d0394727fc4c7db42d64dcac Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 23 Mar 2024 00:38:12 +0800 Subject: [PATCH 1/5] feat: handle message hooks --- .../src/interfaces/handle-result.interface.ts | 40 +---- packages/common/src/interfaces/index.ts | 1 + .../common/src/interfaces/plugin.interface.ts | 51 +++--- packages/core/__test__/nostr-relay.spec.ts | 12 +- .../__test__/services/event.service.spec.ts | 29 ++-- .../services/plugin-manager.service.spec.ts | 164 +++++++----------- packages/core/src/interfaces/index.ts | 2 +- packages/core/src/interfaces/options.ts | 38 ++++ packages/core/src/nostr-relay.ts | 44 ++--- packages/core/src/services/event.service.ts | 9 +- .../src/services/plugin-manager.service.ts | 145 ++++++++++------ 11 files changed, 270 insertions(+), 265 deletions(-) rename packages/{core => common}/src/interfaces/handle-result.interface.ts (52%) create mode 100644 packages/core/src/interfaces/options.ts diff --git a/packages/core/src/interfaces/handle-result.interface.ts b/packages/common/src/interfaces/handle-result.interface.ts similarity index 52% rename from packages/core/src/interfaces/handle-result.interface.ts rename to packages/common/src/interfaces/handle-result.interface.ts index 76974529..2c2dddee 100644 --- a/packages/core/src/interfaces/handle-result.interface.ts +++ b/packages/common/src/interfaces/handle-result.interface.ts @@ -1,41 +1,5 @@ -import { Event, LogLevel, Logger, MessageType } from '@nostr-relay/common'; - -/** - * Options for NostrRelay - */ -export type NostrRelayOptions = { - /** - * Domain name of the Nostr Relay server. If not set, NIP-42 is not enabled. - * More info: https://github.com/nostr-protocol/nips/blob/master/42.md - */ - domain?: string; - /** - * Logger to use. `Default: ConsoleLoggerService` - */ - logger?: Logger; - /** - * The minimum log level to log. `Default: LogLevel.INFO` - */ - logLevel?: LogLevel; - createdAtUpperLimit?: number; - createdAtLowerLimit?: number; - /** - * Allowed minimum PoW difficulty for events.` Default: 0` - */ - minPowDifficulty?: number; - /** - * Maximum number of subscriptions per client. `Default: 20` - */ - maxSubscriptionsPerClient?: number; - /** - * TTL for filter result cache in milliseconds. `Default: 1000` - */ - filterResultCacheTtl?: number; - /** - * TTL for event handling result cache in milliseconds. `Default: 600000` - */ - eventHandlingResultCacheTtl?: number; -}; +import { MessageType } from '../constants'; +import { Event } from './event.interface'; /** * Result of handling REQ message diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts index bef5ba94..f6d15e6e 100644 --- a/packages/common/src/interfaces/index.ts +++ b/packages/common/src/interfaces/index.ts @@ -3,6 +3,7 @@ export * from './common.interface'; export * from './event-repository.interface'; export * from './event.interface'; export * from './filter.interface'; +export * from './handle-result.interface'; export * from './logger.interface'; export * from './message.interface'; export * from './plugin.interface'; diff --git a/packages/common/src/interfaces/plugin.interface.ts b/packages/common/src/interfaces/plugin.interface.ts index d74842e1..7813d41f 100644 --- a/packages/common/src/interfaces/plugin.interface.ts +++ b/packages/common/src/interfaces/plugin.interface.ts @@ -1,42 +1,53 @@ import { ClientContext } from '../client-context'; import { Event, EventHandleResult } from './event.interface'; +import { HandleMessageResult } from './handle-result.interface'; +import { IncomingMessage } from './message.interface'; export type NostrRelayPlugin = - | BeforeEventHandle - | AfterEventHandle - | BeforeEventBroadcast - | AfterEventBroadcast; + | PreHandleMessage + | PostHandleMessage + | PreHandleEvent + | PostHandleEvent + | PreBroadcast + | PostBroadcast; -export type BeforeHookResult = - | { canContinue: true } - | ({ canContinue: false } & T); +export interface PreHandleMessage { + preHandleMessage( + ctx: ClientContext, + message: IncomingMessage, + ): Promise | IncomingMessage | null; +} -export type BeforeEventHandleResult = BeforeHookResult<{ - result: EventHandleResult; -}>; +export interface PostHandleMessage { + postHandleMessage( + ctx: ClientContext, + message: IncomingMessage, + handleResult: HandleMessageResult, + ): Promise | void; +} -export interface BeforeEventHandle { - beforeEventHandle( +export interface PreHandleEvent { + preHandleEvent( ctx: ClientContext, event: Event, - ): Promise | BeforeEventHandleResult; + ): Promise | Event | null; } -export interface AfterEventHandle { - afterEventHandle( +export interface PostHandleEvent { + postHandleEvent( ctx: ClientContext, event: Event, handleResult: EventHandleResult, ): Promise | void; } -export interface BeforeEventBroadcast { - beforeEventBroadcast( +export interface PreBroadcast { + preBroadcast( ctx: ClientContext, event: Event, - ): Promise | BeforeHookResult; + ): Promise | Event | null; } -export interface AfterEventBroadcast { - afterEventBroadcast(ctx: ClientContext, event: Event): Promise | void; +export interface PostBroadcast { + postBroadcast(ctx: ClientContext, event: Event): Promise | void; } diff --git a/packages/core/__test__/nostr-relay.spec.ts b/packages/core/__test__/nostr-relay.spec.ts index 3f0ad17c..40826ca3 100644 --- a/packages/core/__test__/nostr-relay.spec.ts +++ b/packages/core/__test__/nostr-relay.spec.ts @@ -82,8 +82,8 @@ describe('NostrRelay', () => { ]; const mockPlugin = { - beforeEventHandle: jest.fn().mockReturnValue({ canContinue: true }), - afterEventHandle: jest.fn().mockReturnValue(handleResult), + preHandleEvent: jest.fn().mockReturnValue(event), + postHandleEvent: jest.fn().mockReturnValue(handleResult), }; nostrRelay.register(mockPlugin); const mockHandleEvent = jest @@ -93,8 +93,8 @@ describe('NostrRelay', () => { await nostrRelay.handleEventMessage(client, event); - expect(mockPlugin.beforeEventHandle).toHaveBeenCalledWith(ctx, event); - expect(mockPlugin.afterEventHandle).toHaveBeenCalledWith( + expect(mockPlugin.preHandleEvent).toHaveBeenCalledWith(ctx, event); + expect(mockPlugin.postHandleEvent).toHaveBeenCalledWith( ctx, event, handleResult, @@ -159,8 +159,8 @@ describe('NostrRelay', () => { it('should not handle event due to plugin prevention', async () => { jest - .spyOn(nostrRelay['pluginManagerService'], 'callBeforeEventHandleHooks') - .mockResolvedValue({ canContinue: false, result: {} as any }); + .spyOn(nostrRelay['pluginManagerService'], 'preHandleEvent') + .mockResolvedValue(null); const mockHandleEvent = jest .spyOn(nostrRelay['eventService'], 'handleEvent') .mockResolvedValue({ success: true }); diff --git a/packages/core/__test__/services/event.service.spec.ts b/packages/core/__test__/services/event.service.spec.ts index 17fafb8e..33701a68 100644 --- a/packages/core/__test__/services/event.service.spec.ts +++ b/packages/core/__test__/services/event.service.spec.ts @@ -131,37 +131,28 @@ describe('eventService', () => { }); it('should handle ephemeral event successfully', async () => { - const mockBeforeEventBroadcast = jest - .spyOn( - eventService['pluginManagerService'], - 'callBeforeEventBroadcastHooks', - ) - .mockResolvedValue({ canContinue: true }); - const mockAfterEventBroadcast = jest - .spyOn( - eventService['pluginManagerService'], - 'callAfterEventBroadcastHooks', - ) + const event = { id: 'a', kind: EventKind.EPHEMERAL_FIRST } as Event; + const mockPreBroadcast = jest + .spyOn(eventService['pluginManagerService'], 'preBroadcast') + .mockResolvedValue(event); + const mockPostBroadcast = jest + .spyOn(eventService['pluginManagerService'], 'postBroadcast') .mockImplementation(); jest.spyOn(EventUtils, 'validate').mockReturnValue(undefined); - const event = { id: 'a', kind: EventKind.EPHEMERAL_FIRST } as Event; expect(await eventService.handleEvent(ctx, event)).toEqual({ noReplyNeeded: true, success: true, }); - expect(mockBeforeEventBroadcast).toHaveBeenCalledWith(ctx, event); - expect(mockAfterEventBroadcast).toHaveBeenCalledWith(ctx, event); + expect(mockPreBroadcast).toHaveBeenCalledWith(ctx, event); + expect(mockPostBroadcast).toHaveBeenCalledWith(ctx, event); expect(subscriptionService.broadcast).toHaveBeenCalledWith(event); }); it('should not broadcast due to plugin prevention', async () => { jest - .spyOn( - eventService['pluginManagerService'], - 'callBeforeEventBroadcastHooks', - ) - .mockResolvedValue({ canContinue: false }); + .spyOn(eventService['pluginManagerService'], 'preBroadcast') + .mockResolvedValue(null); jest.spyOn(EventUtils, 'validate').mockReturnValue(undefined); const event = { id: 'a', kind: EventKind.EPHEMERAL_FIRST } as Event; diff --git a/packages/core/__test__/services/plugin-manager.service.spec.ts b/packages/core/__test__/services/plugin-manager.service.spec.ts index bb1fc898..d2bfc0e0 100644 --- a/packages/core/__test__/services/plugin-manager.service.spec.ts +++ b/packages/core/__test__/services/plugin-manager.service.spec.ts @@ -16,243 +16,211 @@ describe('PluginManagerService', () => { describe('register', () => { it('should register before event handler plugin', () => { const plugin = { - beforeEventHandle: jest.fn(), + preHandleEvent: jest.fn(), }; pluginManagerService.register(plugin); - expect(pluginManagerService['beforeEventHandlePlugins']).toEqual([ - plugin, - ]); + expect(pluginManagerService['preHandleEventPlugins']).toEqual([plugin]); }); it('should register after event handler plugin', () => { const plugin = { - afterEventHandle: jest.fn(), + postHandleEvent: jest.fn(), }; pluginManagerService.register(plugin); - expect(pluginManagerService['afterEventHandlePlugins']).toEqual([plugin]); + expect(pluginManagerService['postHandleEventPlugins']).toEqual([plugin]); }); it('should register before event broadcast plugin', () => { const plugin = { - beforeEventBroadcast: jest.fn(), + preBroadcast: jest.fn(), }; pluginManagerService.register(plugin); - expect(pluginManagerService['beforeEventBroadcastPlugins']).toEqual([ - plugin, - ]); + expect(pluginManagerService['preBroadcastPlugins']).toEqual([plugin]); }); it('should register after event broadcast plugin', () => { const plugin = { - afterEventBroadcast: jest.fn(), + postBroadcast: jest.fn(), }; pluginManagerService.register(plugin); - expect(pluginManagerService['afterEventBroadcastPlugins']).toEqual([ - plugin, - ]); + expect(pluginManagerService['postBroadcastPlugins']).toEqual([plugin]); }); it('should register multiple plugins', () => { const pluginA = { - beforeEventHandle: jest.fn(), - beforeEventBroadcast: jest.fn(), + preHandleEvent: jest.fn(), + preBroadcast: jest.fn(), }; const pluginB = { - afterEventHandle: jest.fn(), - afterEventBroadcast: jest.fn(), + postHandleEvent: jest.fn(), + postBroadcast: jest.fn(), }; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - expect(pluginManagerService['beforeEventHandlePlugins']).toEqual([ - pluginA, - ]); - expect(pluginManagerService['afterEventHandlePlugins']).toEqual([ - pluginB, - ]); - expect(pluginManagerService['beforeEventBroadcastPlugins']).toEqual([ - pluginA, - ]); - expect(pluginManagerService['afterEventBroadcastPlugins']).toEqual([ - pluginB, - ]); + expect(pluginManagerService['preHandleEventPlugins']).toEqual([pluginA]); + expect(pluginManagerService['postHandleEventPlugins']).toEqual([pluginB]); + expect(pluginManagerService['preBroadcastPlugins']).toEqual([pluginA]); + expect(pluginManagerService['postBroadcastPlugins']).toEqual([pluginB]); }); }); - describe('callBeforeEventHandleHooks', () => { + describe('preHandleEvent', () => { it('should run before event handle plugins', async () => { + const event = {} as Event; const pluginA = { - beforeEventHandle: jest.fn().mockReturnValue({ canContinue: true }), + preHandleEvent: jest.fn().mockReturnValue(event), }; const pluginB = { - beforeEventHandle: jest.fn().mockReturnValue({ canContinue: true }), + preHandleEvent: jest.fn().mockReturnValue(event), }; - const event = {} as Event; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - const result = await pluginManagerService.callBeforeEventHandleHooks( - ctx, - event, - ); + const result = await pluginManagerService.preHandleEvent(ctx, event); - expect(result).toEqual({ canContinue: true }); - expect(pluginA.beforeEventHandle).toHaveBeenCalledWith(ctx, event); - expect(pluginB.beforeEventHandle).toHaveBeenCalledWith(ctx, event); + expect(result).toEqual(event); + expect(pluginA.preHandleEvent).toHaveBeenCalledWith(ctx, event); + expect(pluginB.preHandleEvent).toHaveBeenCalledWith(ctx, event); const pluginACallOrder = - pluginA.beforeEventHandle.mock.invocationCallOrder[0]; + pluginA.preHandleEvent.mock.invocationCallOrder[0]; const pluginBCallOrder = - pluginB.beforeEventHandle.mock.invocationCallOrder[0]; + pluginB.preHandleEvent.mock.invocationCallOrder[0]; expect(pluginACallOrder).toBeLessThan(pluginBCallOrder); }); it('should return false if any plugin returns false', async () => { + const event = {} as Event; const pluginA = { - beforeEventHandle: jest.fn().mockReturnValue(false), + preHandleEvent: jest.fn().mockReturnValue(null), }; const pluginB = { - beforeEventHandle: jest.fn().mockReturnValue(true), + preHandleEvent: jest.fn().mockReturnValue(event), }; - const event = {} as Event; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - const result = await pluginManagerService.callBeforeEventHandleHooks( - ctx, - event, - ); + const result = await pluginManagerService.preHandleEvent(ctx, event); - expect(result).toBe(false); - expect(pluginA.beforeEventHandle).toHaveBeenCalledWith(ctx, event); - expect(pluginB.beforeEventHandle).not.toHaveBeenCalled(); + expect(result).toBeNull(); + expect(pluginA.preHandleEvent).toHaveBeenCalledWith(ctx, event); + expect(pluginB.preHandleEvent).not.toHaveBeenCalled(); }); }); - describe('callAfterEventHandleHooks', () => { + describe('postHandleEvent', () => { it('should run after event handle plugins', async () => { const handleResult = { needResponse: true, success: true }; const pluginA = { - afterEventHandle: jest.fn().mockImplementation(), + postHandleEvent: jest.fn().mockImplementation(), }; const pluginB = { - afterEventHandle: jest.fn().mockImplementation(), + postHandleEvent: jest.fn().mockImplementation(), }; const event = {} as Event; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - await pluginManagerService.callAfterEventHandleHooks( - ctx, - event, - handleResult, - ); + await pluginManagerService.postHandleEvent(ctx, event, handleResult); - expect(pluginB.afterEventHandle).toHaveBeenCalledWith( + expect(pluginB.postHandleEvent).toHaveBeenCalledWith( ctx, event, handleResult, ); - expect(pluginA.afterEventHandle).toHaveBeenCalledWith( + expect(pluginA.postHandleEvent).toHaveBeenCalledWith( ctx, event, handleResult, ); const pluginBCallOrder = - pluginB.afterEventHandle.mock.invocationCallOrder[0]; + pluginB.postHandleEvent.mock.invocationCallOrder[0]; const pluginACallOrder = - pluginA.afterEventHandle.mock.invocationCallOrder[0]; + pluginA.postHandleEvent.mock.invocationCallOrder[0]; expect(pluginBCallOrder).toBeLessThan(pluginACallOrder); }); }); - describe('callBeforeEventBroadcastHooks', () => { + describe('preBroadcast', () => { it('should run before event broadcast plugins', async () => { + const event = {} as Event; const pluginA = { - beforeEventBroadcast: jest.fn().mockReturnValue({ canContinue: true }), + preBroadcast: jest.fn().mockReturnValue(event), }; const pluginB = { - beforeEventBroadcast: jest.fn().mockReturnValue({ canContinue: true }), + preBroadcast: jest.fn().mockReturnValue(event), }; - const event = {} as Event; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - const result = await pluginManagerService.callBeforeEventBroadcastHooks( - ctx, - event, - ); + const result = await pluginManagerService.preBroadcast(ctx, event); - expect(result).toEqual({ canContinue: true }); - expect(pluginA.beforeEventBroadcast).toHaveBeenCalledWith(ctx, event); - expect(pluginB.beforeEventBroadcast).toHaveBeenCalledWith(ctx, event); + expect(result).toEqual(event); + expect(pluginA.preBroadcast).toHaveBeenCalledWith(ctx, event); + expect(pluginB.preBroadcast).toHaveBeenCalledWith(ctx, event); - const pluginACallOrder = - pluginA.beforeEventBroadcast.mock.invocationCallOrder[0]; - const pluginBCallOrder = - pluginB.beforeEventBroadcast.mock.invocationCallOrder[0]; + const pluginACallOrder = pluginA.preBroadcast.mock.invocationCallOrder[0]; + const pluginBCallOrder = pluginB.preBroadcast.mock.invocationCallOrder[0]; expect(pluginACallOrder).toBeLessThan(pluginBCallOrder); }); it('should return false if any plugin returns false', async () => { + const event = {} as Event; const pluginA = { - beforeEventBroadcast: jest.fn().mockReturnValue(false), + preBroadcast: jest.fn().mockReturnValue(null), }; const pluginB = { - beforeEventBroadcast: jest.fn().mockReturnValue(true), + preBroadcast: jest.fn().mockReturnValue(event), }; - const event = {} as Event; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - const result = await pluginManagerService.callBeforeEventBroadcastHooks( - ctx, - event, - ); + const result = await pluginManagerService.preBroadcast(ctx, event); - expect(result).toBe(false); - expect(pluginA.beforeEventBroadcast).toHaveBeenCalledWith(ctx, event); - expect(pluginB.beforeEventBroadcast).not.toHaveBeenCalled(); + expect(result).toBeNull(); + expect(pluginA.preBroadcast).toHaveBeenCalledWith(ctx, event); + expect(pluginB.preBroadcast).not.toHaveBeenCalled(); }); }); - describe('callAfterEventBroadcastHooks', () => { + describe('postBroadcast', () => { it('should run after event broadcast plugins', async () => { const pluginA = { - afterEventBroadcast: jest.fn(), + postBroadcast: jest.fn(), }; const pluginB = { - afterEventBroadcast: jest.fn(), + postBroadcast: jest.fn(), }; const event = {} as Event; pluginManagerService.register(pluginA); pluginManagerService.register(pluginB); - await pluginManagerService.callAfterEventBroadcastHooks(ctx, event); + await pluginManagerService.postBroadcast(ctx, event); - expect(pluginB.afterEventBroadcast).toHaveBeenCalledWith(ctx, event); - expect(pluginA.afterEventBroadcast).toHaveBeenCalledWith(ctx, event); + expect(pluginB.postBroadcast).toHaveBeenCalledWith(ctx, event); + expect(pluginA.postBroadcast).toHaveBeenCalledWith(ctx, event); const pluginBCallOrder = - pluginB.afterEventBroadcast.mock.invocationCallOrder[0]; + pluginB.postBroadcast.mock.invocationCallOrder[0]; const pluginACallOrder = - pluginA.afterEventBroadcast.mock.invocationCallOrder[0]; + pluginA.postBroadcast.mock.invocationCallOrder[0]; expect(pluginBCallOrder).toBeLessThan(pluginACallOrder); }); }); diff --git a/packages/core/src/interfaces/index.ts b/packages/core/src/interfaces/index.ts index 896a1193..5f30ef38 100644 --- a/packages/core/src/interfaces/index.ts +++ b/packages/core/src/interfaces/index.ts @@ -1 +1 @@ -export * from './handle-result.interface'; +export * from './options'; diff --git a/packages/core/src/interfaces/options.ts b/packages/core/src/interfaces/options.ts new file mode 100644 index 00000000..4bda45f8 --- /dev/null +++ b/packages/core/src/interfaces/options.ts @@ -0,0 +1,38 @@ +import { LogLevel, Logger } from '@nostr-relay/common'; + +/** + * Options for NostrRelay + */ +export type NostrRelayOptions = { + /** + * Domain name of the Nostr Relay server. If not set, NIP-42 is not enabled. + * More info: https://github.com/nostr-protocol/nips/blob/master/42.md + */ + domain?: string; + /** + * Logger to use. `Default: ConsoleLoggerService` + */ + logger?: Logger; + /** + * The minimum log level to log. `Default: LogLevel.INFO` + */ + logLevel?: LogLevel; + createdAtUpperLimit?: number; + createdAtLowerLimit?: number; + /** + * Allowed minimum PoW difficulty for events.` Default: 0` + */ + minPowDifficulty?: number; + /** + * Maximum number of subscriptions per client. `Default: 20` + */ + maxSubscriptionsPerClient?: number; + /** + * TTL for filter result cache in milliseconds. `Default: 1000` + */ + filterResultCacheTtl?: number; + /** + * TTL for event handling result cache in milliseconds. `Default: 600000` + */ + eventHandlingResultCacheTtl?: number; +}; diff --git a/packages/core/src/nostr-relay.ts b/packages/core/src/nostr-relay.ts index 19939a6a..739c4747 100644 --- a/packages/core/src/nostr-relay.ts +++ b/packages/core/src/nostr-relay.ts @@ -14,15 +14,13 @@ import { MessageType, NostrRelayPlugin, SubscriptionId, -} from '@nostr-relay/common'; -import { HandleAuthMessageResult, HandleCloseMessageResult, HandleEventMessageResult, HandleMessageResult, HandleReqMessageResult, - NostrRelayOptions, -} from './interfaces'; +} from '@nostr-relay/common'; +import { NostrRelayOptions } from './interfaces'; import { EventService } from './services/event.service'; import { PluginManagerService } from './services/plugin-manager.service'; import { SubscriptionService } from './services/subscription.service'; @@ -138,15 +136,21 @@ export class NostrRelay { */ async handleMessage( client: Client, - message: IncomingMessage, + msg: IncomingMessage, ): Promise { + const ctx = this.getClientContext(client); + const message = await this.pluginManagerService.preHandleMessage(ctx, msg); + if (!message) return; + if (message[0] === MessageType.EVENT) { const [, event] = message; const result = await this.handleEventMessage(client, event); - return { + const handleResult = { messageType: MessageType.EVENT, ...result, }; + await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); + return handleResult; } if (message[0] === MessageType.REQ) { const [, subscriptionId, ...filters] = message; @@ -155,31 +159,36 @@ export class NostrRelay { subscriptionId, filters, ); - return { + const handleResult = { messageType: MessageType.REQ, ...result, }; + await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); + return handleResult; } if (message[0] === MessageType.CLOSE) { const [, subscriptionId] = message; const result = this.handleCloseMessage(client, subscriptionId); - return { + const handleResult = { messageType: MessageType.CLOSE, ...result, }; + await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); + return handleResult; } if (message[0] === MessageType.AUTH) { const [, signedEvent] = message; const result = this.handleAuthMessage(client, signedEvent); - return { + const handleResult = { messageType: MessageType.AUTH, ...result, }; + await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); } - const ctx = this.getClientContext(client); ctx.sendMessage( createOutgoingNoticeMessage('invalid: unknown message type'), ); + await this.pluginManagerService.postHandleMessage(ctx, msg); } /** @@ -194,19 +203,12 @@ export class NostrRelay { ): Promise { const ctx = this.getClientContext(client); const callback = async (): Promise => { - const hookResult = - await this.pluginManagerService.callBeforeEventHandleHooks(ctx, event); - if (!hookResult.canContinue) { - return hookResult.result; - } + const e = await this.pluginManagerService.preHandleEvent(ctx, event); + if (!e) return { success: false, noReplyNeeded: true }; - const handleResult = await this.eventService.handleEvent(ctx, event); + const handleResult = await this.eventService.handleEvent(ctx, e); - await this.pluginManagerService.callAfterEventHandleHooks( - ctx, - event, - handleResult, - ); + await this.pluginManagerService.postHandleEvent(ctx, e, handleResult); return handleResult; }; diff --git a/packages/core/src/services/event.service.ts b/packages/core/src/services/event.service.ts index 194f8165..65e947c6 100644 --- a/packages/core/src/services/event.service.ts +++ b/packages/core/src/services/event.service.ts @@ -165,14 +165,13 @@ export class EventService { return !!exists; } - private async broadcast(ctx: ClientContext, event: Event): Promise { - const hookResult = - await this.pluginManagerService.callBeforeEventBroadcastHooks(ctx, event); - if (!hookResult.canContinue) return; + private async broadcast(ctx: ClientContext, e: Event): Promise { + const event = await this.pluginManagerService.preBroadcast(ctx, e); + if (!event) return; await this.subscriptionService.broadcast(event); - await this.pluginManagerService.callAfterEventBroadcastHooks(ctx, event); + await this.pluginManagerService.postBroadcast(ctx, event); } private mergeSortedEventArrays(arrays: Event[][]): Event[] { diff --git a/packages/core/src/services/plugin-manager.service.ts b/packages/core/src/services/plugin-manager.service.ts index 835efa94..b295c38e 100644 --- a/packages/core/src/services/plugin-manager.service.ts +++ b/packages/core/src/services/plugin-manager.service.ts @@ -1,106 +1,137 @@ import { - AfterEventBroadcast, - AfterEventHandle, - BeforeEventBroadcast, - BeforeEventHandle, - BeforeEventHandleResult, - BeforeHookResult, ClientContext, Event, EventHandleResult, + HandleMessageResult, + IncomingMessage, NostrRelayPlugin, + PostBroadcast, + PostHandleEvent, + PostHandleMessage, + PreBroadcast, + PreHandleEvent, + PreHandleMessage, } from '@nostr-relay/common'; export class PluginManagerService { - private readonly beforeEventHandlePlugins: BeforeEventHandle[] = []; - private readonly afterEventHandlePlugins: AfterEventHandle[] = []; - private readonly beforeEventBroadcastPlugins: BeforeEventBroadcast[] = []; - private readonly afterEventBroadcastPlugins: AfterEventBroadcast[] = []; + private readonly preHandleMessagePlugins: PreHandleMessage[] = []; + private readonly postHandleMessagePlugins: PostHandleMessage[] = []; + private readonly preHandleEventPlugins: PreHandleEvent[] = []; + private readonly postHandleEventPlugins: PostHandleEvent[] = []; + private readonly preBroadcastPlugins: PreBroadcast[] = []; + private readonly postBroadcastPlugins: PostBroadcast[] = []; register(plugin: NostrRelayPlugin): void { - if (this.hasBeforeEventHandleHook(plugin)) { - this.beforeEventHandlePlugins.push(plugin); + if (this.hasPreHandleMessage(plugin)) { + this.preHandleMessagePlugins.push(plugin); } - if (this.hasAfterEventHandleHook(plugin)) { - this.afterEventHandlePlugins.unshift(plugin); + if (this.hasPostHandleMessage(plugin)) { + this.postHandleMessagePlugins.unshift(plugin); } - if (this.hasBeforeEventBroadcastHook(plugin)) { - this.beforeEventBroadcastPlugins.push(plugin); + if (this.hasPreHandleEvent(plugin)) { + this.preHandleEventPlugins.push(plugin); } - if (this.hasAfterEventBroadcastHook(plugin)) { - this.afterEventBroadcastPlugins.unshift(plugin); + if (this.hasPostHandleEvent(plugin)) { + this.postHandleEventPlugins.unshift(plugin); + } + if (this.hasPreBroadcast(plugin)) { + this.preBroadcastPlugins.push(plugin); + } + if (this.hasPostBroadcast(plugin)) { + this.postBroadcastPlugins.unshift(plugin); } } - async callBeforeEventHandleHooks( + async preHandleMessage( ctx: ClientContext, - event: Event, - ): Promise { - for await (const plugin of this.beforeEventHandlePlugins) { - const result = await plugin.beforeEventHandle(ctx, event); - if (!result.canContinue) return result; + message: IncomingMessage, + ): Promise { + let result: IncomingMessage | null = message; + for await (const plugin of this.preHandleMessagePlugins) { + result = await plugin.preHandleMessage(ctx, message); + if (!result) return null; } - return { canContinue: true }; + return result; } - async callAfterEventHandleHooks( + async postHandleMessage( ctx: ClientContext, - event: Event, - handleResult: EventHandleResult, + message: IncomingMessage, + handleResult: HandleMessageResult, ): Promise { - for await (const plugin of this.afterEventHandlePlugins) { - await plugin.afterEventHandle(ctx, event, handleResult); + for await (const plugin of this.postHandleMessagePlugins) { + await plugin.postHandleMessage(ctx, message, handleResult); } } - async callBeforeEventBroadcastHooks( + async preHandleEvent( ctx: ClientContext, event: Event, - ): Promise { - for await (const plugin of this.beforeEventBroadcastPlugins) { - const result = await plugin.beforeEventBroadcast(ctx, event); - if (!result.canContinue) return result; + ): Promise { + let result: Event | null = event; + for await (const plugin of this.preHandleEventPlugins) { + result = await plugin.preHandleEvent(ctx, event); + if (!result) return null; } - return { canContinue: true }; + return result; } - async callAfterEventBroadcastHooks( + async postHandleEvent( ctx: ClientContext, event: Event, + handleResult: EventHandleResult, ): Promise { - for await (const plugin of this.afterEventBroadcastPlugins) { - await plugin.afterEventBroadcast(ctx, event); + for await (const plugin of this.postHandleEventPlugins) { + await plugin.postHandleEvent(ctx, event, handleResult); } } - private hasBeforeEventHandleHook( - plugin: NostrRelayPlugin, - ): plugin is BeforeEventHandle { - return ( - typeof (plugin as BeforeEventHandle).beforeEventHandle === 'function' - ); + async preBroadcast(ctx: ClientContext, event: Event): Promise { + let result: Event | null = event; + for await (const plugin of this.preBroadcastPlugins) { + const result = await plugin.preBroadcast(ctx, event); + if (!result) return null; + } + return result; + } + + async postBroadcast(ctx: ClientContext, event: Event): Promise { + for await (const plugin of this.postBroadcastPlugins) { + await plugin.postBroadcast(ctx, event); + } } - private hasAfterEventHandleHook( + private hasPreHandleMessage( plugin: NostrRelayPlugin, - ): plugin is AfterEventHandle { - return typeof (plugin as AfterEventHandle).afterEventHandle === 'function'; + ): plugin is PreHandleMessage { + return typeof (plugin as PreHandleMessage).preHandleMessage === 'function'; } - private hasBeforeEventBroadcastHook( + private hasPostHandleMessage( plugin: NostrRelayPlugin, - ): plugin is BeforeEventBroadcast { + ): plugin is PostHandleMessage { return ( - typeof (plugin as BeforeEventBroadcast).beforeEventBroadcast === - 'function' + typeof (plugin as PostHandleMessage).postHandleMessage === 'function' ); } - private hasAfterEventBroadcastHook( + private hasPreHandleEvent( plugin: NostrRelayPlugin, - ): plugin is AfterEventBroadcast { - return ( - typeof (plugin as AfterEventBroadcast).afterEventBroadcast === 'function' - ); + ): plugin is PreHandleEvent { + return typeof (plugin as PreHandleEvent).preHandleEvent === 'function'; + } + + private hasPostHandleEvent( + plugin: NostrRelayPlugin, + ): plugin is PostHandleEvent { + return typeof (plugin as PostHandleEvent).postHandleEvent === 'function'; + } + + private hasPreBroadcast(plugin: NostrRelayPlugin): plugin is PreBroadcast { + return typeof (plugin as PreBroadcast).preBroadcast === 'function'; + } + + private hasPostBroadcast(plugin: NostrRelayPlugin): plugin is PostBroadcast { + return typeof (plugin as PostBroadcast).postBroadcast === 'function'; } } From d2447e9fe274d984d224fdf815cc250282dcb675 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 23 Mar 2024 13:52:46 +0800 Subject: [PATCH 2/5] refactor --- packages/common/src/client-context.ts | 2 +- .../common/src/interfaces/common.interface.ts | 2 + .../common/src/interfaces/event.interface.ts | 2 +- packages/common/src/interfaces/index.ts | 1 + .../nostr-relay-options.interface.ts} | 3 +- .../common/src/interfaces/plugin.interface.ts | 53 +-- packages/core/__test__/nostr-relay.spec.ts | 24 -- .../__test__/services/event.service.spec.ts | 22 -- .../services/plugin-manager.service.spec.ts | 334 ++++++++---------- packages/core/src/index.ts | 1 - packages/core/src/interfaces/index.ts | 1 - packages/core/src/nostr-relay.ts | 64 ++-- packages/core/src/services/event.service.ts | 19 +- .../src/services/plugin-manager.service.ts | 178 +++++----- 14 files changed, 299 insertions(+), 407 deletions(-) rename packages/{core/src/interfaces/options.ts => common/src/interfaces/nostr-relay-options.interface.ts} (91%) delete mode 100644 packages/core/src/interfaces/index.ts diff --git a/packages/common/src/client-context.ts b/packages/common/src/client-context.ts index 4c4be04a..f3d68dc9 100644 --- a/packages/common/src/client-context.ts +++ b/packages/common/src/client-context.ts @@ -40,7 +40,7 @@ export class ClientContext { * @param options Client context options */ constructor( - private readonly client: Client, + readonly client: Client, options: ClientContextOptions = {}, ) { this.id = randomUUID(); diff --git a/packages/common/src/interfaces/common.interface.ts b/packages/common/src/interfaces/common.interface.ts index 9cdd77f1..daf788fb 100644 --- a/packages/common/src/interfaces/common.interface.ts +++ b/packages/common/src/interfaces/common.interface.ts @@ -4,3 +4,5 @@ export type Signature = string; export type Tag = string[]; export type SubscriptionId = string; + +export type KeysOfUnion = T extends T ? keyof T : never; diff --git a/packages/common/src/interfaces/event.interface.ts b/packages/common/src/interfaces/event.interface.ts index 32cab81f..2eb92274 100644 --- a/packages/common/src/interfaces/event.interface.ts +++ b/packages/common/src/interfaces/event.interface.ts @@ -10,7 +10,7 @@ export interface Event { sig: Signature; } -export type EventHandleResult = { +export type HandleEventResult = { success: boolean; message?: string; noReplyNeeded?: boolean; diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts index f6d15e6e..a1998409 100644 --- a/packages/common/src/interfaces/index.ts +++ b/packages/common/src/interfaces/index.ts @@ -6,4 +6,5 @@ export * from './filter.interface'; export * from './handle-result.interface'; export * from './logger.interface'; export * from './message.interface'; +export * from './nostr-relay-options.interface'; export * from './plugin.interface'; diff --git a/packages/core/src/interfaces/options.ts b/packages/common/src/interfaces/nostr-relay-options.interface.ts similarity index 91% rename from packages/core/src/interfaces/options.ts rename to packages/common/src/interfaces/nostr-relay-options.interface.ts index 4bda45f8..79a334aa 100644 --- a/packages/core/src/interfaces/options.ts +++ b/packages/common/src/interfaces/nostr-relay-options.interface.ts @@ -1,4 +1,5 @@ -import { LogLevel, Logger } from '@nostr-relay/common'; +import { LogLevel } from '../constants'; +import { Logger } from './logger.interface'; /** * Options for NostrRelay diff --git a/packages/common/src/interfaces/plugin.interface.ts b/packages/common/src/interfaces/plugin.interface.ts index 7813d41f..ba9a0f71 100644 --- a/packages/common/src/interfaces/plugin.interface.ts +++ b/packages/common/src/interfaces/plugin.interface.ts @@ -1,53 +1,36 @@ import { ClientContext } from '../client-context'; -import { Event, EventHandleResult } from './event.interface'; +import { Event, HandleEventResult } from './event.interface'; import { HandleMessageResult } from './handle-result.interface'; import { IncomingMessage } from './message.interface'; export type NostrRelayPlugin = - | PreHandleMessage - | PostHandleMessage - | PreHandleEvent - | PostHandleEvent - | PreBroadcast - | PostBroadcast; + | HandleMessageMiddleware + | HandleEventMiddleware + | BroadcastMiddleware; -export interface PreHandleMessage { - preHandleMessage( +export interface HandleMessageMiddleware { + handleMessage( ctx: ClientContext, message: IncomingMessage, - ): Promise | IncomingMessage | null; + next: ( + ctx: ClientContext, + message: IncomingMessage, + ) => Promise, + ): Promise | HandleMessageResult; } -export interface PostHandleMessage { - postHandleMessage( - ctx: ClientContext, - message: IncomingMessage, - handleResult: HandleMessageResult, - ): Promise | void; -} - -export interface PreHandleEvent { - preHandleEvent( +export interface HandleEventMiddleware { + handleEvent( ctx: ClientContext, event: Event, - ): Promise | Event | null; + next: (ctx: ClientContext, event: Event) => Promise, + ): Promise | HandleEventResult; } -export interface PostHandleEvent { - postHandleEvent( +export interface BroadcastMiddleware { + broadcast( ctx: ClientContext, event: Event, - handleResult: EventHandleResult, + next: (ctx: ClientContext, event: Event) => Promise, ): Promise | void; } - -export interface PreBroadcast { - preBroadcast( - ctx: ClientContext, - event: Event, - ): Promise | Event | null; -} - -export interface PostBroadcast { - postBroadcast(ctx: ClientContext, event: Event): Promise | void; -} diff --git a/packages/core/__test__/nostr-relay.spec.ts b/packages/core/__test__/nostr-relay.spec.ts index 40826ca3..da92a82b 100644 --- a/packages/core/__test__/nostr-relay.spec.ts +++ b/packages/core/__test__/nostr-relay.spec.ts @@ -81,11 +81,6 @@ describe('NostrRelay', () => { '', ]; - const mockPlugin = { - preHandleEvent: jest.fn().mockReturnValue(event), - postHandleEvent: jest.fn().mockReturnValue(handleResult), - }; - nostrRelay.register(mockPlugin); const mockHandleEvent = jest .spyOn(nostrRelay['eventService'], 'handleEvent') .mockResolvedValue(handleResult); @@ -93,12 +88,6 @@ describe('NostrRelay', () => { await nostrRelay.handleEventMessage(client, event); - expect(mockPlugin.preHandleEvent).toHaveBeenCalledWith(ctx, event); - expect(mockPlugin.postHandleEvent).toHaveBeenCalledWith( - ctx, - event, - handleResult, - ); expect(mockHandleEvent).toHaveBeenCalledWith(ctx, event); expect(client.send).toHaveBeenCalledWith(JSON.stringify(outgoingMessage)); }); @@ -156,19 +145,6 @@ describe('NostrRelay', () => { expect(client.send).toHaveBeenNthCalledWith(1, outgoingMessageStr); expect(client.send).toHaveBeenNthCalledWith(2, outgoingMessageStr); }); - - it('should not handle event due to plugin prevention', async () => { - jest - .spyOn(nostrRelay['pluginManagerService'], 'preHandleEvent') - .mockResolvedValue(null); - const mockHandleEvent = jest - .spyOn(nostrRelay['eventService'], 'handleEvent') - .mockResolvedValue({ success: true }); - - await nostrRelay.handleEventMessage(client, { id: 'eventId' } as Event); - - expect(mockHandleEvent).not.toHaveBeenCalled(); - }); }); describe('req', () => { diff --git a/packages/core/__test__/services/event.service.spec.ts b/packages/core/__test__/services/event.service.spec.ts index 33701a68..aaa78631 100644 --- a/packages/core/__test__/services/event.service.spec.ts +++ b/packages/core/__test__/services/event.service.spec.ts @@ -132,37 +132,15 @@ describe('eventService', () => { it('should handle ephemeral event successfully', async () => { const event = { id: 'a', kind: EventKind.EPHEMERAL_FIRST } as Event; - const mockPreBroadcast = jest - .spyOn(eventService['pluginManagerService'], 'preBroadcast') - .mockResolvedValue(event); - const mockPostBroadcast = jest - .spyOn(eventService['pluginManagerService'], 'postBroadcast') - .mockImplementation(); jest.spyOn(EventUtils, 'validate').mockReturnValue(undefined); expect(await eventService.handleEvent(ctx, event)).toEqual({ noReplyNeeded: true, success: true, }); - expect(mockPreBroadcast).toHaveBeenCalledWith(ctx, event); - expect(mockPostBroadcast).toHaveBeenCalledWith(ctx, event); expect(subscriptionService.broadcast).toHaveBeenCalledWith(event); }); - it('should not broadcast due to plugin prevention', async () => { - jest - .spyOn(eventService['pluginManagerService'], 'preBroadcast') - .mockResolvedValue(null); - jest.spyOn(EventUtils, 'validate').mockReturnValue(undefined); - - const event = { id: 'a', kind: EventKind.EPHEMERAL_FIRST } as Event; - expect(await eventService.handleEvent(ctx, event)).toEqual({ - noReplyNeeded: true, - success: true, - }); - expect(subscriptionService.broadcast).not.toHaveBeenCalled(); - }); - it('should handle regular event successfully', async () => { const event = { id: 'a', kind: EventKind.TEXT_NOTE } as Event; diff --git a/packages/core/__test__/services/plugin-manager.service.spec.ts b/packages/core/__test__/services/plugin-manager.service.spec.ts index d2bfc0e0..348efc65 100644 --- a/packages/core/__test__/services/plugin-manager.service.spec.ts +++ b/packages/core/__test__/services/plugin-manager.service.spec.ts @@ -1,4 +1,9 @@ -import { ClientContext, ClientReadyState, Event } from '../../../common'; +import { + ClientContext, + ClientReadyState, + Event, + IncomingMessage, +} from '../../../common'; import { PluginManagerService } from '../../src/services/plugin-manager.service'; describe('PluginManagerService', () => { @@ -14,214 +19,183 @@ describe('PluginManagerService', () => { }); describe('register', () => { - it('should register before event handler plugin', () => { + it('should register plugin', () => { const plugin = { - preHandleEvent: jest.fn(), + handleMessage: jest.fn(), + handleEvent: jest.fn(), + broadcast: jest.fn(), }; pluginManagerService.register(plugin); - expect(pluginManagerService['preHandleEventPlugins']).toEqual([plugin]); + expect(pluginManagerService['handleMessageMiddlewares']).toEqual([ + plugin, + ]); + expect(pluginManagerService['handleEventMiddlewares']).toEqual([plugin]); + expect(pluginManagerService['broadcastMiddlewares']).toEqual([plugin]); }); - it('should register after event handler plugin', () => { - const plugin = { - postHandleEvent: jest.fn(), - }; - - pluginManagerService.register(plugin); - - expect(pluginManagerService['postHandleEventPlugins']).toEqual([plugin]); - }); - - it('should register before event broadcast plugin', () => { - const plugin = { - preBroadcast: jest.fn(), - }; - - pluginManagerService.register(plugin); - - expect(pluginManagerService['preBroadcastPlugins']).toEqual([plugin]); - }); - - it('should register after event broadcast plugin', () => { - const plugin = { - postBroadcast: jest.fn(), - }; - - pluginManagerService.register(plugin); - - expect(pluginManagerService['postBroadcastPlugins']).toEqual([plugin]); - }); - - it('should register multiple plugins', () => { - const pluginA = { - preHandleEvent: jest.fn(), - preBroadcast: jest.fn(), - }; - const pluginB = { - postHandleEvent: jest.fn(), - postBroadcast: jest.fn(), - }; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); - - expect(pluginManagerService['preHandleEventPlugins']).toEqual([pluginA]); - expect(pluginManagerService['postHandleEventPlugins']).toEqual([pluginB]); - expect(pluginManagerService['preBroadcastPlugins']).toEqual([pluginA]); - expect(pluginManagerService['postBroadcastPlugins']).toEqual([pluginB]); + it('should register plugins', () => { + const plugin1 = { + handleMessage: jest.fn(), + }; + const plugin2 = { + handleEvent: jest.fn(), + }; + const plugin3 = { + broadcast: jest.fn(), + }; + const plugin4 = { + handleMessage: jest.fn(), + handleEvent: jest.fn(), + broadcast: jest.fn(), + }; + + pluginManagerService + .register(plugin1, plugin2, plugin3) + .register(plugin4); + + expect(pluginManagerService['handleMessageMiddlewares']).toEqual([ + plugin1, + plugin4, + ]); + expect(pluginManagerService['handleEventMiddlewares']).toEqual([ + plugin2, + plugin4, + ]); + expect(pluginManagerService['broadcastMiddlewares']).toEqual([ + plugin3, + plugin4, + ]); }); }); - describe('preHandleEvent', () => { - it('should run before event handle plugins', async () => { - const event = {} as Event; - const pluginA = { - preHandleEvent: jest.fn().mockReturnValue(event), - }; - const pluginB = { - preHandleEvent: jest.fn().mockReturnValue(event), - }; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); - - const result = await pluginManagerService.preHandleEvent(ctx, event); - - expect(result).toEqual(event); - expect(pluginA.preHandleEvent).toHaveBeenCalledWith(ctx, event); - expect(pluginB.preHandleEvent).toHaveBeenCalledWith(ctx, event); - - const pluginACallOrder = - pluginA.preHandleEvent.mock.invocationCallOrder[0]; - const pluginBCallOrder = - pluginB.preHandleEvent.mock.invocationCallOrder[0]; - expect(pluginACallOrder).toBeLessThan(pluginBCallOrder); - }); - - it('should return false if any plugin returns false', async () => { - const event = {} as Event; - const pluginA = { - preHandleEvent: jest.fn().mockReturnValue(null), - }; - const pluginB = { - preHandleEvent: jest.fn().mockReturnValue(event), - }; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); + describe('handleMessage', () => { + it('should call middlewares in order', async () => { + const arr: number[] = []; + pluginManagerService.register( + { + handleMessage: async (ctx, message, next) => { + arr.push(1); + const result = await next(ctx, message); + arr.push(5); + return result; + }, + }, + { + handleMessage: async (ctx, message, next) => { + arr.push(2); + const result = await next(ctx, message); + arr.push(4); + return result; + }, + }, + ); - const result = await pluginManagerService.preHandleEvent(ctx, event); + await pluginManagerService.handleMessage( + ctx, + {} as IncomingMessage, + async () => { + arr.push(3); + return { messageType: 'EVENT', success: true }; + }, + ); - expect(result).toBeNull(); - expect(pluginA.preHandleEvent).toHaveBeenCalledWith(ctx, event); - expect(pluginB.preHandleEvent).not.toHaveBeenCalled(); + expect(arr).toEqual([1, 2, 3, 4, 5]); }); - }); - describe('postHandleEvent', () => { - it('should run after event handle plugins', async () => { - const handleResult = { needResponse: true, success: true }; - const pluginA = { - postHandleEvent: jest.fn().mockImplementation(), - }; - const pluginB = { - postHandleEvent: jest.fn().mockImplementation(), - }; - const event = {} as Event; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); - - await pluginManagerService.postHandleEvent(ctx, event, handleResult); + it('should directly return if middleware does not call next', async () => { + pluginManagerService.register({ + handleMessage: async () => { + return { messageType: 'EVENT', success: false }; + }, + }); - expect(pluginB.postHandleEvent).toHaveBeenCalledWith( + const result = await pluginManagerService.handleMessage( ctx, - event, - handleResult, - ); - expect(pluginA.postHandleEvent).toHaveBeenCalledWith( - ctx, - event, - handleResult, + {} as IncomingMessage, + async () => { + return { messageType: 'EVENT', success: true }; + }, ); - const pluginBCallOrder = - pluginB.postHandleEvent.mock.invocationCallOrder[0]; - const pluginACallOrder = - pluginA.postHandleEvent.mock.invocationCallOrder[0]; - expect(pluginBCallOrder).toBeLessThan(pluginACallOrder); + expect(result).toEqual({ messageType: 'EVENT', success: false }); }); - }); - describe('preBroadcast', () => { - it('should run before event broadcast plugins', async () => { - const event = {} as Event; - const pluginA = { - preBroadcast: jest.fn().mockReturnValue(event), - }; - const pluginB = { - preBroadcast: jest.fn().mockReturnValue(event), - }; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); - - const result = await pluginManagerService.preBroadcast(ctx, event); - - expect(result).toEqual(event); - expect(pluginA.preBroadcast).toHaveBeenCalledWith(ctx, event); - expect(pluginB.preBroadcast).toHaveBeenCalledWith(ctx, event); - - const pluginACallOrder = pluginA.preBroadcast.mock.invocationCallOrder[0]; - const pluginBCallOrder = pluginB.preBroadcast.mock.invocationCallOrder[0]; - expect(pluginACallOrder).toBeLessThan(pluginBCallOrder); + it('should throw error if next() called multiple times', async () => { + pluginManagerService.register({ + handleMessage: async (ctx, message, next) => { + await next(ctx, message); + await next(ctx, message); + }, + }); + + await expect( + pluginManagerService.handleMessage( + ctx, + {} as IncomingMessage, + async () => {}, + ), + ).rejects.toThrow('next() called multiple times'); }); + }); - it('should return false if any plugin returns false', async () => { - const event = {} as Event; - const pluginA = { - preBroadcast: jest.fn().mockReturnValue(null), - }; - const pluginB = { - preBroadcast: jest.fn().mockReturnValue(event), - }; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); + describe('handleEvent', () => { + it('should call middlewares in order', async () => { + const arr: number[] = []; + pluginManagerService.register( + { + handleEvent: async (ctx, event, next) => { + arr.push(1); + const result = await next(ctx, event); + arr.push(5); + return result; + }, + }, + { + handleEvent: async (ctx, event, next) => { + arr.push(2); + const result = await next(ctx, event); + arr.push(4); + return result; + }, + }, + ); - const result = await pluginManagerService.preBroadcast(ctx, event); + await pluginManagerService.handleEvent(ctx, {} as Event, async () => { + arr.push(3); + return { success: true }; + }); - expect(result).toBeNull(); - expect(pluginA.preBroadcast).toHaveBeenCalledWith(ctx, event); - expect(pluginB.preBroadcast).not.toHaveBeenCalled(); + expect(arr).toEqual([1, 2, 3, 4, 5]); }); }); - describe('postBroadcast', () => { - it('should run after event broadcast plugins', async () => { - const pluginA = { - postBroadcast: jest.fn(), - }; - const pluginB = { - postBroadcast: jest.fn(), - }; - const event = {} as Event; - - pluginManagerService.register(pluginA); - pluginManagerService.register(pluginB); - - await pluginManagerService.postBroadcast(ctx, event); + describe('broadcast', () => { + it('should call middlewares in order', async () => { + const arr: number[] = []; + pluginManagerService.register( + { + broadcast: async (ctx, event, next) => { + arr.push(1); + await next(ctx, event); + arr.push(5); + }, + }, + { + broadcast: async (ctx, event, next) => { + arr.push(2); + await next(ctx, event); + arr.push(4); + }, + }, + ); - expect(pluginB.postBroadcast).toHaveBeenCalledWith(ctx, event); - expect(pluginA.postBroadcast).toHaveBeenCalledWith(ctx, event); + await pluginManagerService.broadcast(ctx, {} as Event, async () => { + arr.push(3); + }); - const pluginBCallOrder = - pluginB.postBroadcast.mock.invocationCallOrder[0]; - const pluginACallOrder = - pluginA.postBroadcast.mock.invocationCallOrder[0]; - expect(pluginBCallOrder).toBeLessThan(pluginACallOrder); + expect(arr).toEqual([1, 2, 3, 4, 5]); }); }); }); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1491b97e..b4c2f8da 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,2 @@ -export * from './interfaces'; export * from './nostr-relay'; export * from './utils/response'; diff --git a/packages/core/src/interfaces/index.ts b/packages/core/src/interfaces/index.ts deleted file mode 100644 index 5f30ef38..00000000 --- a/packages/core/src/interfaces/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './options'; diff --git a/packages/core/src/nostr-relay.ts b/packages/core/src/nostr-relay.ts index 739c4747..acbbbc26 100644 --- a/packages/core/src/nostr-relay.ts +++ b/packages/core/src/nostr-relay.ts @@ -3,24 +3,24 @@ import { ClientContext, ConsoleLoggerService, Event, - EventHandleResult, EventId, EventRepository, EventUtils, Filter, FilterUtils, - IncomingMessage, - LogLevel, - MessageType, - NostrRelayPlugin, - SubscriptionId, HandleAuthMessageResult, HandleCloseMessageResult, HandleEventMessageResult, + HandleEventResult, HandleMessageResult, HandleReqMessageResult, + IncomingMessage, + LogLevel, + MessageType, + NostrRelayOptions, + NostrRelayPlugin, + SubscriptionId, } from '@nostr-relay/common'; -import { NostrRelayOptions } from './interfaces'; import { EventService } from './services/event.service'; import { PluginManagerService } from './services/plugin-manager.service'; import { SubscriptionService } from './services/subscription.service'; @@ -39,7 +39,7 @@ export class NostrRelay { private readonly eventService: EventService; private readonly subscriptionService: SubscriptionService; private readonly eventHandlingLazyCache: - | LazyCache> + | LazyCache> | undefined; private readonly domain?: string; private readonly pluginManagerService: PluginManagerService; @@ -136,21 +136,25 @@ export class NostrRelay { */ async handleMessage( client: Client, - msg: IncomingMessage, + message: IncomingMessage, ): Promise { const ctx = this.getClientContext(client); - const message = await this.pluginManagerService.preHandleMessage(ctx, msg); - if (!message) return; + return await this.pluginManagerService.handleMessage( + ctx, + message, + this._handleMessage.bind(this), + ); + } + private async _handleMessage(ctx: ClientContext, message: IncomingMessage) { + const client = ctx.client; if (message[0] === MessageType.EVENT) { const [, event] = message; const result = await this.handleEventMessage(client, event); - const handleResult = { + return { messageType: MessageType.EVENT, ...result, }; - await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); - return handleResult; } if (message[0] === MessageType.REQ) { const [, subscriptionId, ...filters] = message; @@ -159,36 +163,30 @@ export class NostrRelay { subscriptionId, filters, ); - const handleResult = { + return { messageType: MessageType.REQ, ...result, }; - await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); - return handleResult; } if (message[0] === MessageType.CLOSE) { const [, subscriptionId] = message; const result = this.handleCloseMessage(client, subscriptionId); - const handleResult = { + return { messageType: MessageType.CLOSE, ...result, }; - await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); - return handleResult; } if (message[0] === MessageType.AUTH) { const [, signedEvent] = message; const result = this.handleAuthMessage(client, signedEvent); - const handleResult = { + return { messageType: MessageType.AUTH, ...result, }; - await this.pluginManagerService.postHandleMessage(ctx, msg, handleResult); } ctx.sendMessage( createOutgoingNoticeMessage('invalid: unknown message type'), ); - await this.pluginManagerService.postHandleMessage(ctx, msg); } /** @@ -202,16 +200,18 @@ export class NostrRelay { event: Event, ): Promise { const ctx = this.getClientContext(client); - const callback = async (): Promise => { - const e = await this.pluginManagerService.preHandleEvent(ctx, event); - if (!e) return { success: false, noReplyNeeded: true }; - - const handleResult = await this.eventService.handleEvent(ctx, e); - - await this.pluginManagerService.postHandleEvent(ctx, e, handleResult); + return await this.pluginManagerService.handleEvent( + ctx, + event, + this._handleEventMessage.bind(this), + ); + } - return handleResult; - }; + private async _handleEventMessage( + ctx: ClientContext, + event: Event, + ): Promise { + const callback = () => this.eventService.handleEvent(ctx, event); const handleResult = this.eventHandlingLazyCache ? await this.eventHandlingLazyCache.get(event.id, callback) diff --git a/packages/core/src/services/event.service.ts b/packages/core/src/services/event.service.ts index 65e947c6..8c15b282 100644 --- a/packages/core/src/services/event.service.ts +++ b/packages/core/src/services/event.service.ts @@ -1,12 +1,12 @@ import { ClientContext, Event, - EventHandleResult, EventKind, EventRepository, EventType, EventUtils, Filter, + HandleEventResult, Logger, } from '@nostr-relay/common'; import { LazyCache } from '../utils'; @@ -66,7 +66,7 @@ export class EventService { async handleEvent( ctx: ClientContext, event: Event, - ): Promise { + ): Promise { if (event.kind === EventKind.AUTHENTICATION) { return { success: true, noReplyNeeded: true }; } @@ -138,7 +138,7 @@ export class EventService { private async handleEphemeralEvent( ctx: ClientContext, event: Event, - ): Promise { + ): Promise { await this.broadcast(ctx, event); return { noReplyNeeded: true, success: true }; } @@ -146,7 +146,7 @@ export class EventService { private async handleRegularEvent( ctx: ClientContext, event: Event, - ): Promise { + ): Promise { const { isDuplicate } = await this.eventRepository.upsert(event); if (!isDuplicate) { @@ -165,13 +165,10 @@ export class EventService { return !!exists; } - private async broadcast(ctx: ClientContext, e: Event): Promise { - const event = await this.pluginManagerService.preBroadcast(ctx, e); - if (!event) return; - - await this.subscriptionService.broadcast(event); - - await this.pluginManagerService.postBroadcast(ctx, event); + private async broadcast(ctx: ClientContext, event: Event): Promise { + return this.pluginManagerService.broadcast(ctx, event, (_, e) => + this.subscriptionService.broadcast(e), + ); } private mergeSortedEventArrays(arrays: Event[][]): Event[] { diff --git a/packages/core/src/services/plugin-manager.service.ts b/packages/core/src/services/plugin-manager.service.ts index b295c38e..7c506534 100644 --- a/packages/core/src/services/plugin-manager.service.ts +++ b/packages/core/src/services/plugin-manager.service.ts @@ -1,137 +1,119 @@ import { + BroadcastMiddleware, ClientContext, Event, - EventHandleResult, + HandleEventMiddleware, + HandleEventResult, + HandleMessageMiddleware, HandleMessageResult, IncomingMessage, + KeysOfUnion, NostrRelayPlugin, - PostBroadcast, - PostHandleEvent, - PostHandleMessage, - PreBroadcast, - PreHandleEvent, - PreHandleMessage, } from '@nostr-relay/common'; export class PluginManagerService { - private readonly preHandleMessagePlugins: PreHandleMessage[] = []; - private readonly postHandleMessagePlugins: PostHandleMessage[] = []; - private readonly preHandleEventPlugins: PreHandleEvent[] = []; - private readonly postHandleEventPlugins: PostHandleEvent[] = []; - private readonly preBroadcastPlugins: PreBroadcast[] = []; - private readonly postBroadcastPlugins: PostBroadcast[] = []; + private readonly handleMessageMiddlewares: HandleMessageMiddleware[] = []; + private readonly handleEventMiddlewares: HandleEventMiddleware[] = []; + private readonly broadcastMiddlewares: BroadcastMiddleware[] = []; - register(plugin: NostrRelayPlugin): void { - if (this.hasPreHandleMessage(plugin)) { - this.preHandleMessagePlugins.push(plugin); - } - if (this.hasPostHandleMessage(plugin)) { - this.postHandleMessagePlugins.unshift(plugin); - } - if (this.hasPreHandleEvent(plugin)) { - this.preHandleEventPlugins.push(plugin); - } - if (this.hasPostHandleEvent(plugin)) { - this.postHandleEventPlugins.unshift(plugin); - } - if (this.hasPreBroadcast(plugin)) { - this.preBroadcastPlugins.push(plugin); - } - if (this.hasPostBroadcast(plugin)) { - this.postBroadcastPlugins.unshift(plugin); - } - } - - async preHandleMessage( - ctx: ClientContext, - message: IncomingMessage, - ): Promise { - let result: IncomingMessage | null = message; - for await (const plugin of this.preHandleMessagePlugins) { - result = await plugin.preHandleMessage(ctx, message); - if (!result) return null; - } - return result; + register(...plugins: NostrRelayPlugin[]): PluginManagerService { + plugins.forEach(plugin => { + if (this.hasHandleMessageMiddleware(plugin)) { + this.handleMessageMiddlewares.push(plugin); + } + if (this.hasHandleEventMiddleware(plugin)) { + this.handleEventMiddlewares.push(plugin); + } + if (this.hasBroadcastMiddleware(plugin)) { + this.broadcastMiddlewares.push(plugin); + } + }); + return this; } - async postHandleMessage( + async handleMessage( ctx: ClientContext, message: IncomingMessage, - handleResult: HandleMessageResult, - ): Promise { - for await (const plugin of this.postHandleMessagePlugins) { - await plugin.postHandleMessage(ctx, message, handleResult); - } + next: ( + ctx: ClientContext, + message: IncomingMessage, + ) => Promise, + ): Promise { + return this.compose( + this.handleMessageMiddlewares, + 'handleMessage', + next, + ctx, + message, + ); } - async preHandleEvent( + async handleEvent( ctx: ClientContext, event: Event, - ): Promise { - let result: Event | null = event; - for await (const plugin of this.preHandleEventPlugins) { - result = await plugin.preHandleEvent(ctx, event); - if (!result) return null; - } - return result; + next: (ctx: ClientContext, event: Event) => Promise, + ): Promise { + return this.compose( + this.handleEventMiddlewares, + 'handleEvent', + next, + ctx, + event, + ); } - async postHandleEvent( + async broadcast( ctx: ClientContext, event: Event, - handleResult: EventHandleResult, + next: (ctx: ClientContext, event: Event) => Promise, ): Promise { - for await (const plugin of this.postHandleEventPlugins) { - await plugin.postHandleEvent(ctx, event, handleResult); - } - } - - async preBroadcast(ctx: ClientContext, event: Event): Promise { - let result: Event | null = event; - for await (const plugin of this.preBroadcastPlugins) { - const result = await plugin.preBroadcast(ctx, event); - if (!result) return null; - } - return result; + return this.compose( + this.broadcastMiddlewares, + 'broadcast', + next, + ctx, + event, + ); } - async postBroadcast(ctx: ClientContext, event: Event): Promise { - for await (const plugin of this.postBroadcastPlugins) { - await plugin.postBroadcast(ctx, event); + private compose( + plugins: NostrRelayPlugin[], + funcName: KeysOfUnion, + next: (...args: any[]) => Promise, + ...args: any[] + ): Promise { + let index = -1; + async function dispatch(i: number): Promise { + if (i <= index) { + throw new Error('next() called multiple times'); + } + index = i; + const middleware = plugins[i]?.[funcName]; + if (!middleware) { + return next(...args); + } + return middleware(...args, dispatch.bind(null, i + 1)); } + return dispatch(0); } - private hasPreHandleMessage( + private hasHandleMessageMiddleware( plugin: NostrRelayPlugin, - ): plugin is PreHandleMessage { - return typeof (plugin as PreHandleMessage).preHandleMessage === 'function'; - } - - private hasPostHandleMessage( - plugin: NostrRelayPlugin, - ): plugin is PostHandleMessage { + ): plugin is HandleMessageMiddleware { return ( - typeof (plugin as PostHandleMessage).postHandleMessage === 'function' + typeof (plugin as HandleMessageMiddleware).handleMessage === 'function' ); } - private hasPreHandleEvent( + private hasHandleEventMiddleware( plugin: NostrRelayPlugin, - ): plugin is PreHandleEvent { - return typeof (plugin as PreHandleEvent).preHandleEvent === 'function'; + ): plugin is HandleEventMiddleware { + return typeof (plugin as HandleEventMiddleware).handleEvent === 'function'; } - private hasPostHandleEvent( + private hasBroadcastMiddleware( plugin: NostrRelayPlugin, - ): plugin is PostHandleEvent { - return typeof (plugin as PostHandleEvent).postHandleEvent === 'function'; - } - - private hasPreBroadcast(plugin: NostrRelayPlugin): plugin is PreBroadcast { - return typeof (plugin as PreBroadcast).preBroadcast === 'function'; - } - - private hasPostBroadcast(plugin: NostrRelayPlugin): plugin is PostBroadcast { - return typeof (plugin as PostBroadcast).postBroadcast === 'function'; + ): plugin is BroadcastMiddleware { + return typeof (plugin as BroadcastMiddleware).broadcast === 'function'; } } From 06b7b3299b2b80c2b94759530f992c72072b1d0d Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 23 Mar 2024 13:59:07 +0800 Subject: [PATCH 3/5] feat: delete handle event middleware --- .../common/src/interfaces/plugin.interface.ts | 15 +----- .../services/plugin-manager.service.spec.ts | 51 ++----------------- packages/core/src/nostr-relay.ts | 11 ---- .../src/services/plugin-manager.service.ts | 26 ---------- 4 files changed, 6 insertions(+), 97 deletions(-) diff --git a/packages/common/src/interfaces/plugin.interface.ts b/packages/common/src/interfaces/plugin.interface.ts index ba9a0f71..dadb95c1 100644 --- a/packages/common/src/interfaces/plugin.interface.ts +++ b/packages/common/src/interfaces/plugin.interface.ts @@ -1,12 +1,9 @@ import { ClientContext } from '../client-context'; -import { Event, HandleEventResult } from './event.interface'; +import { Event } from './event.interface'; import { HandleMessageResult } from './handle-result.interface'; import { IncomingMessage } from './message.interface'; -export type NostrRelayPlugin = - | HandleMessageMiddleware - | HandleEventMiddleware - | BroadcastMiddleware; +export type NostrRelayPlugin = HandleMessageMiddleware | BroadcastMiddleware; export interface HandleMessageMiddleware { handleMessage( @@ -19,14 +16,6 @@ export interface HandleMessageMiddleware { ): Promise | HandleMessageResult; } -export interface HandleEventMiddleware { - handleEvent( - ctx: ClientContext, - event: Event, - next: (ctx: ClientContext, event: Event) => Promise, - ): Promise | HandleEventResult; -} - export interface BroadcastMiddleware { broadcast( ctx: ClientContext, diff --git a/packages/core/__test__/services/plugin-manager.service.spec.ts b/packages/core/__test__/services/plugin-manager.service.spec.ts index 348efc65..3bd55232 100644 --- a/packages/core/__test__/services/plugin-manager.service.spec.ts +++ b/packages/core/__test__/services/plugin-manager.service.spec.ts @@ -22,7 +22,6 @@ describe('PluginManagerService', () => { it('should register plugin', () => { const plugin = { handleMessage: jest.fn(), - handleEvent: jest.fn(), broadcast: jest.fn(), }; @@ -31,7 +30,6 @@ describe('PluginManagerService', () => { expect(pluginManagerService['handleMessageMiddlewares']).toEqual([ plugin, ]); - expect(pluginManagerService['handleEventMiddlewares']).toEqual([plugin]); expect(pluginManagerService['broadcastMiddlewares']).toEqual([plugin]); }); @@ -40,32 +38,22 @@ describe('PluginManagerService', () => { handleMessage: jest.fn(), }; const plugin2 = { - handleEvent: jest.fn(), - }; - const plugin3 = { broadcast: jest.fn(), }; - const plugin4 = { + const plugin3 = { handleMessage: jest.fn(), - handleEvent: jest.fn(), broadcast: jest.fn(), }; - pluginManagerService - .register(plugin1, plugin2, plugin3) - .register(plugin4); + pluginManagerService.register(plugin1, plugin2).register(plugin3); expect(pluginManagerService['handleMessageMiddlewares']).toEqual([ plugin1, - plugin4, - ]); - expect(pluginManagerService['handleEventMiddlewares']).toEqual([ - plugin2, - plugin4, + plugin3, ]); expect(pluginManagerService['broadcastMiddlewares']).toEqual([ + plugin2, plugin3, - plugin4, ]); }); }); @@ -140,37 +128,6 @@ describe('PluginManagerService', () => { }); }); - describe('handleEvent', () => { - it('should call middlewares in order', async () => { - const arr: number[] = []; - pluginManagerService.register( - { - handleEvent: async (ctx, event, next) => { - arr.push(1); - const result = await next(ctx, event); - arr.push(5); - return result; - }, - }, - { - handleEvent: async (ctx, event, next) => { - arr.push(2); - const result = await next(ctx, event); - arr.push(4); - return result; - }, - }, - ); - - await pluginManagerService.handleEvent(ctx, {} as Event, async () => { - arr.push(3); - return { success: true }; - }); - - expect(arr).toEqual([1, 2, 3, 4, 5]); - }); - }); - describe('broadcast', () => { it('should call middlewares in order', async () => { const arr: number[] = []; diff --git a/packages/core/src/nostr-relay.ts b/packages/core/src/nostr-relay.ts index acbbbc26..8dd6f7ac 100644 --- a/packages/core/src/nostr-relay.ts +++ b/packages/core/src/nostr-relay.ts @@ -200,17 +200,6 @@ export class NostrRelay { event: Event, ): Promise { const ctx = this.getClientContext(client); - return await this.pluginManagerService.handleEvent( - ctx, - event, - this._handleEventMessage.bind(this), - ); - } - - private async _handleEventMessage( - ctx: ClientContext, - event: Event, - ): Promise { const callback = () => this.eventService.handleEvent(ctx, event); const handleResult = this.eventHandlingLazyCache diff --git a/packages/core/src/services/plugin-manager.service.ts b/packages/core/src/services/plugin-manager.service.ts index 7c506534..1864331b 100644 --- a/packages/core/src/services/plugin-manager.service.ts +++ b/packages/core/src/services/plugin-manager.service.ts @@ -2,8 +2,6 @@ import { BroadcastMiddleware, ClientContext, Event, - HandleEventMiddleware, - HandleEventResult, HandleMessageMiddleware, HandleMessageResult, IncomingMessage, @@ -13,7 +11,6 @@ import { export class PluginManagerService { private readonly handleMessageMiddlewares: HandleMessageMiddleware[] = []; - private readonly handleEventMiddlewares: HandleEventMiddleware[] = []; private readonly broadcastMiddlewares: BroadcastMiddleware[] = []; register(...plugins: NostrRelayPlugin[]): PluginManagerService { @@ -21,9 +18,6 @@ export class PluginManagerService { if (this.hasHandleMessageMiddleware(plugin)) { this.handleMessageMiddlewares.push(plugin); } - if (this.hasHandleEventMiddleware(plugin)) { - this.handleEventMiddlewares.push(plugin); - } if (this.hasBroadcastMiddleware(plugin)) { this.broadcastMiddlewares.push(plugin); } @@ -48,20 +42,6 @@ export class PluginManagerService { ); } - async handleEvent( - ctx: ClientContext, - event: Event, - next: (ctx: ClientContext, event: Event) => Promise, - ): Promise { - return this.compose( - this.handleEventMiddlewares, - 'handleEvent', - next, - ctx, - event, - ); - } - async broadcast( ctx: ClientContext, event: Event, @@ -105,12 +85,6 @@ export class PluginManagerService { ); } - private hasHandleEventMiddleware( - plugin: NostrRelayPlugin, - ): plugin is HandleEventMiddleware { - return typeof (plugin as HandleEventMiddleware).handleEvent === 'function'; - } - private hasBroadcastMiddleware( plugin: NostrRelayPlugin, ): plugin is BroadcastMiddleware { From 1159abde40fb1d3ad0b3356fac0405ddf20dee4f Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 23 Mar 2024 23:24:08 +0800 Subject: [PATCH 4/5] fix --- .../common/src/interfaces/plugin.interface.ts | 7 ++-- .../services/plugin-manager.service.spec.ts | 33 ++++++++++--------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/common/src/interfaces/plugin.interface.ts b/packages/common/src/interfaces/plugin.interface.ts index dadb95c1..0e5e0cd7 100644 --- a/packages/common/src/interfaces/plugin.interface.ts +++ b/packages/common/src/interfaces/plugin.interface.ts @@ -9,10 +9,7 @@ export interface HandleMessageMiddleware { handleMessage( ctx: ClientContext, message: IncomingMessage, - next: ( - ctx: ClientContext, - message: IncomingMessage, - ) => Promise, + next: () => Promise, ): Promise | HandleMessageResult; } @@ -20,6 +17,6 @@ export interface BroadcastMiddleware { broadcast( ctx: ClientContext, event: Event, - next: (ctx: ClientContext, event: Event) => Promise, + next: () => Promise, ): Promise | void; } diff --git a/packages/core/__test__/services/plugin-manager.service.spec.ts b/packages/core/__test__/services/plugin-manager.service.spec.ts index 3bd55232..42f86255 100644 --- a/packages/core/__test__/services/plugin-manager.service.spec.ts +++ b/packages/core/__test__/services/plugin-manager.service.spec.ts @@ -63,33 +63,36 @@ describe('PluginManagerService', () => { const arr: number[] = []; pluginManagerService.register( { - handleMessage: async (ctx, message, next) => { + handleMessage: async (_ctx, _message, next) => { arr.push(1); - const result = await next(ctx, message); + const result = await next(); arr.push(5); return result; }, }, { - handleMessage: async (ctx, message, next) => { + handleMessage: async (_ctx, _message, next) => { arr.push(2); - const result = await next(ctx, message); + const result = await next(); arr.push(4); return result; }, }, ); + const mockNext = jest.fn().mockImplementation(async () => { + arr.push(3); + return { messageType: 'EVENT', success: true }; + }); await pluginManagerService.handleMessage( ctx, {} as IncomingMessage, - async () => { - arr.push(3); - return { messageType: 'EVENT', success: true }; - }, + mockNext, ); expect(arr).toEqual([1, 2, 3, 4, 5]); + expect(mockNext).toHaveBeenCalledTimes(1); + expect(mockNext).toHaveBeenCalledWith(ctx, {}); }); it('should directly return if middleware does not call next', async () => { @@ -112,9 +115,9 @@ describe('PluginManagerService', () => { it('should throw error if next() called multiple times', async () => { pluginManagerService.register({ - handleMessage: async (ctx, message, next) => { - await next(ctx, message); - await next(ctx, message); + handleMessage: async (_ctx, _message, next) => { + await next(); + await next(); }, }); @@ -133,16 +136,16 @@ describe('PluginManagerService', () => { const arr: number[] = []; pluginManagerService.register( { - broadcast: async (ctx, event, next) => { + broadcast: async (_ctx, _message, next) => { arr.push(1); - await next(ctx, event); + await next(); arr.push(5); }, }, { - broadcast: async (ctx, event, next) => { + broadcast: async (_ctx, _message, next) => { arr.push(2); - await next(ctx, event); + await next(); arr.push(4); }, }, From 51d67f7a8a641c69ff8a437c04c6342c6043df85 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 23 Mar 2024 23:26:20 +0800 Subject: [PATCH 5/5] fix: lint --- packages/core/src/nostr-relay.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/core/src/nostr-relay.ts b/packages/core/src/nostr-relay.ts index 8dd6f7ac..278c9a24 100644 --- a/packages/core/src/nostr-relay.ts +++ b/packages/core/src/nostr-relay.ts @@ -146,7 +146,10 @@ export class NostrRelay { ); } - private async _handleMessage(ctx: ClientContext, message: IncomingMessage) { + private async _handleMessage( + ctx: ClientContext, + message: IncomingMessage, + ): Promise { const client = ctx.client; if (message[0] === MessageType.EVENT) { const [, event] = message; @@ -200,7 +203,9 @@ export class NostrRelay { event: Event, ): Promise { const ctx = this.getClientContext(client); - const callback = () => this.eventService.handleEvent(ctx, event); + const callback = (): Promise => { + return this.eventService.handleEvent(ctx, event); + }; const handleResult = this.eventHandlingLazyCache ? await this.eventHandlingLazyCache.get(event.id, callback)