feat: Add global Notifications management (admin endpoints + Svelte UI)#2256
Merged
Conversation
Extract NotificationService from StatusController to centralize notification logic (system notifications, release notifications, force refresh) while maintaining full backward compatibility with existing StatusController endpoints. Backend: - NotificationService with get/set/clear system notification and send release - 5 new admin endpoints under /admin/notifications (GET settings, PUT/DELETE system, POST release, POST force-refresh) - Admin DTOs for request/response models - DI registration in Bootstrapper Frontend: - Notification feature module with models and TanStack Query API layer - NotificationBanners component in app layout for system/release messages - Admin page at /system/notifications with dialog-based actions - Nav entry in system routes Tests: - 13 backend integration tests covering auth, validation, and legacy compat - 6 frontend unit tests for notification event logic - HTTP test samples for all admin endpoints Co-authored-by: Claude <noreply@anthropic.com>
[Consumes("application/json")] on an endpoint with optional body causes
ASP.NET Core to return 404 when called without Content-Type header.
Since the request body is nullable/optional, remove the constraint.
Added test: ForceRefresh_WithNoBody_UsesDefaultMessage
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…system-notifications Architectural cleanup: - Removed duplicate admin endpoints from AdminController — StatusController already had these endpoints with proper auth - Added GET /notifications/settings and POST /notifications/force-refresh to StatusController where they belong - Added 'publish' query param to POST/DELETE system notification (backward compatible, defaults to true) - Deleted NotificationModels.cs — use existing ValueFromBody + query param patterns instead of custom DTOs - Renamed frontend feature from 'notifications' to 'system-notifications' - Renamed component from 'notification-banners' to 'system-notification-banner' - Moved tests from AdminNotificationTests to StatusControllerTests (with AAA) - Deleted admin-notifications.http, added samples to status.http - Updated OpenSpec tasks to reflect final architecture Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a global system-notifications management feature: backend NotificationService plus two new StatusController endpoints (GET notifications/settings, POST notifications/force-refresh) and a publish flag on the existing set/clear endpoints, a Svelte 5 admin page at /system/notifications, a global banner component that resolves messages from realtime WebSocket → persisted API → env fallback, and accompanying tests and OpenSpec docs.
Changes:
- Extract notification logic into
NotificationServiceand refactorStatusControllerto delegate, addingnotifications/settings,notifications/force-refresh, and apublishquery param. - Add Svelte
system-notification-banner(mounted in(app)/+layout.svelte) and a/system/notificationsadmin page with TanStack Query API wrappers and models. - Add backend integration tests, frontend unit tests, HTTP samples, and an OpenSpec proposal/design/spec/tasks set.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Exceptionless.Core/Services/NotificationService.cs | New service centralizing cache + message-bus notification operations. |
| src/Exceptionless.Core/Bootstrapper.cs | Registers NotificationService as a singleton. |
| src/Exceptionless.Web/Controllers/StatusController.cs | Delegates to NotificationService; adds settings, force-refresh, and publish flag. |
| tests/Exceptionless.Tests/Controllers/StatusControllerTests.cs | Adds tests for new endpoints and publish=false. |
| tests/http/status.http | HTTP samples for force-refresh and settings. |
| src/Exceptionless.Web/ClientApp/src/lib/features/system-notifications/models.ts | Frontend types for settings response. |
| src/Exceptionless.Web/ClientApp/src/lib/features/system-notifications/api.svelte.ts | TanStack Query wrappers for notification endpoints. |
| .../components/system-notification-banner.svelte | Global banner with realtime/persisted/env-fallback resolution. |
| .../components/system-notification-banner.test.ts | Unit tests (currently only exercise CustomEvent semantics). |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte | Mounts SystemNotificationBanner in the app layout. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/system/notifications/+page.svelte | New admin management page with action dialogs. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/system/routes.svelte.ts | Adds Bell-icon nav entry for global admins. |
| openspec/changes/add-svelte-notifications-management/* | Proposal, design, spec, tasks for the change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Regenerate controller-manifest.json with new endpoints (CI RCA) - Add typed NotificationSettingsResponse record (replaces anonymous object) - Fix validation: empty message returns 400 BadRequest (was 404 NotFound) - Rename misleading test names to match actual behavior - Strengthen publish=false test to verify cache persistence - Extract resolveDisplayMessage() into testable utility - Rewrite frontend tests to actually cover resolution logic - Update OpenSpec spec to reflect StatusController architecture Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…esh with no message - Replace boolean string interpolation in query strings with URLSearchParams - forceRefreshClientsMutation: omit body entirely when no message is provided, so the no-body backend code path is exercised from the UI (not just tests) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…/prefer-svelte-reactivity lint rule Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…notifications-management
- Fix self-reload race on force refresh: admin initiating tab now delays
window.location.reload() by 1500ms via force-refresh-coordinator.ts flag;
all other clients still reload immediately
- Add MaxNotificationMessageLength = 1000 cap to PostSystemNotificationAsync,
PostReleaseNotificationAsync, and ForceRefreshAsync; add frontend maxlength={1000}
on all textareas
- Rename system-notification-banner.test.ts → resolve-message.test.ts (tests only
cover resolveDisplayMessage, not the banner component)
- Add force-refresh-coordinator.test.ts with 4 tests for the flag module
- Clarify resolve-message.ts docstring: null realtime skips persisted but still
shows env fallback; empty strings treated as absent
- Update OpenSpec design.md, proposal.md, spec.md, tasks.md to reflect actual
StatusController implementation (not AdminController as originally proposed)
- Merge origin/main (81 files changed, no conflicts)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
message parameter is non-nullable ValueFromBody<string>; use message.Value?.Length instead of message?.Value?.Length to avoid false positive null dereference warning Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename feature folder: system-notifications -> notifications - Rename component: SystemNotificationBanner -> Notifications - Remove NotificationSettingsResponse DTO (use anonymous object) - Remove force-refresh-coordinator (consumeSelfInitiatedFlag hack) - Remove resolveDisplayMessage (over-engineered resolution) - Remove MaxNotificationMessageLength validation (unwanted) - Use useEventListener from runed (project pattern) - Simplify banner: just show message or fallback like legacy Angular - Add newline before return after control statements - Revert .gitignore gstack addition - Remove maxlength from textareas Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d450ee9 to
01956a3
Compare
- Remove /notifications/settings endpoint (unnecessary complexity) - Use typography components (H2, Muted, P) instead of raw HTML - Restore original NotFound() response for empty notification message - Delete unused NotificationSettings model - Remove settings endpoint tests - Regenerate controller manifest Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
01956a3 to
346e78f
Compare
- Remove POST notifications/force-refresh endpoint (unnecessary) - Remove forceRefreshClientsMutation and UI card/dialog - Fix notification banner icon alignment (items-center, remove mt-0.5) - Remove force-refresh tests and .http samples - Regenerate controller manifest Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
niemyjski
commented
May 29, 2026
niemyjski
commented
May 29, 2026
niemyjski
commented
May 29, 2026
Co-authored-by: Blake Niemyjski <bniemyjski@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds system notifications management: persistent banners, release announcements, and force-client-refresh — all via new admin endpoints on
StatusControllerand a Svelte 5 admin UI.What changed
Backend
NotificationService(new): cache-backed system notification storage + WebSocket publishingStatusController— 2 new endpoints +publishflag on existing:GET /api/v2/notifications/settings(new) — active notification + env fallback configPOST /api/v2/notifications/force-refresh(new) — force-reload all connected clientsPOST/DELETE /api/v2/notifications/system?publish=true— existing +publishflag (defaulttrue, backward compat)NotificationSettingsResponse(new record): typed DTO for settings endpointFrontend (Svelte 5)
system-notification-banner.svelte(new): 3-tier message resolution (realtime WebSocket → persisted API → env fallback); added to(app)/+layout.svelte/system/notificationspage (new): admin UI with Set/Clear/Send Release/Force Refresh dialogsapi.svelte.tsandmodels.ts: TanStack Query wrappers for all notification endpointsroutes.svelte.ts: Bell icon + Notifications entry under SystemTests
StatusControllerTests.cs)Breaking changes
None. All existing endpoints behave identically. The
publishparameter defaults totrue.OpenSpec
Designed in
openspec/changes/add-svelte-notifications-management/.