From f16e1558d8d58bfe80956fcd53829c5dcf2e14be Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Tue, 28 Apr 2026 19:06:29 +0200 Subject: [PATCH] Gate notifications SSE on enable_sse_updates Notifications now follow the same flag as history and entry-point updates: SSE when enable_sse_updates is true, polling against /api/notifications/status otherwise. enable_notification_system remains the outer gate that decides whether the system runs at all. Update the store test to drive the SSE-vs-polling split off enable_sse_updates, mirroring the historyStore test layout. --- client/src/stores/notificationsStore.test.ts | 15 +++++++++------ client/src/stores/notificationsStore.ts | 14 ++++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/client/src/stores/notificationsStore.test.ts b/client/src/stores/notificationsStore.test.ts index ef568348f7b..a88fceac595 100644 --- a/client/src/stores/notificationsStore.test.ts +++ b/client/src/stores/notificationsStore.test.ts @@ -50,11 +50,14 @@ const { server, http } = useServerMock(); const statusSpy = vi.fn(); -function registerDefaultHandlers({ enableNotificationSystem }: { enableNotificationSystem: boolean }) { +function registerDefaultHandlers({ enableSseUpdates }: { enableSseUpdates: boolean }) { server.use( http.get("/api/configuration", ({ response }) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any - return response(200).json({ enable_notification_system: enableNotificationSystem } as any); + return response(200).json({ + enable_notification_system: true, + enable_sse_updates: enableSseUpdates, + } as any); }), http.get("/api/notifications", ({ response }) => { return response(200).json([SCENARIO_NOTIFICATION]); @@ -99,9 +102,9 @@ describe("notificationsStore — config-driven SSE vs polling", () => { vi.useRealTimers(); }); - describe("when enable_notification_system is true (SSE scenario)", () => { + describe("when enable_sse_updates is true (SSE scenario)", () => { beforeEach(() => { - registerDefaultHandlers({ enableNotificationSystem: true }); + registerDefaultHandlers({ enableSseUpdates: true }); }); it("connects SSE and does not poll the status endpoint", async () => { @@ -161,9 +164,9 @@ describe("notificationsStore — config-driven SSE vs polling", () => { }); }); - describe("when enable_notification_system is false (polling scenario)", () => { + describe("when enable_sse_updates is false (polling scenario)", () => { beforeEach(() => { - registerDefaultHandlers({ enableNotificationSystem: false }); + registerDefaultHandlers({ enableSseUpdates: false }); }); it("does not connect SSE and polls the status endpoint on the configured interval", async () => { diff --git a/client/src/stores/notificationsStore.ts b/client/src/stores/notificationsStore.ts index a935b97e0b4..3f5acf414f2 100644 --- a/client/src/stores/notificationsStore.ts +++ b/client/src/stores/notificationsStore.ts @@ -113,9 +113,11 @@ export const useNotificationsStore = defineStore("notificationsStore", () => { } // Choose between SSE and polling based on the server config flag - // `enable_notification_system`. The `/api/events/stream` endpoint accepts + // `enable_sse_updates`. The `/api/events/stream` endpoint accepts // connections regardless of the flag, so we cannot rely on EventSource - // connectivity to decide — config is the source of truth. + // connectivity to decide — config is the source of truth. Callers gate + // on `enable_notification_system` before reaching here, so we only have + // to pick the transport. // // `useResourceWatcher` is instantiated lazily because it registers a // `visibilitychange` listener that calls `startWatchingResourceIfNeeded` @@ -130,7 +132,7 @@ export const useNotificationsStore = defineStore("notificationsStore", () => { const configStore = useConfigStore(); const decide = () => { - if (configStore.config?.enable_notification_system) { + if (configStore.config?.enable_sse_updates) { sseConnect(); } else { const { startWatchingResource: startPolling, stopWatchingResource } = useResourceWatcher( @@ -191,9 +193,9 @@ export const useNotificationsStore = defineStore("notificationsStore", () => { if (request.changes.deleted) { notifications.value = notifications.value.filter((n) => !request.notification_ids.includes(n.id)); } - // If the notification system (and therefore SSE) is disabled, trigger - // a poll to refresh state after a local mutation. - if (!useConfigStore().config?.enable_notification_system) { + // When polling is the active transport, trigger a poll to refresh + // state after a local mutation. SSE delivers the update on its own. + if (!useConfigStore().config?.enable_sse_updates) { startWatchingNotifications(); } }