Skip to content

Feature: Notifications On Stream Lifecycle Events #533

Description

@Xhristin3

Context

The notifications subsystem exists but nothing ever creates a notification. NotificationsService.create() (api/src/notifications/notifications.service.ts) persists a row, pushes it over the /streams namespace (gateway.emitNotification), and the user-facing surface — GET /notifications, PATCH /notifications/read-all, PATCH /notifications/:id/read, DELETE /notifications/:id (api/src/notifications/notifications.controller.ts) — is fully built and tested. A repo-wide search shows NotificationsService.create has no callers outside its own spec. The notifications table stays empty, the unread badge can never light up, and the entire feature (issue #398's delivery mechanism) is dead weight.

The natural trigger already exists: StreamsService.update() fires dispatchStatusWebhook on every valid status transition and StreamsService is where the event payloads (startedAt/stoppedAt/occurredAt) are built. A naive implementation would scatter notifications.create() calls through services; the maintainable shape is a single hook site that turns the same lifecycle events into notifications.

Goal

Wire notifications to real events so the notification store, WebSocket push, and unread endpoints become live: notify the stream owner on stream status transitions (and optionally notify on webhook delivery failure, reusing WebhooksService's failure path), driven from one integration point.

Scope

1. Stream lifecycle notifications

  • On a valid PATCH /streams/:id status transition (the same transitions dispatchStatusWebhook handles), create a notification for the stream owner with a typed payload ({ streamId, status, occurredAt }). Reuse the payload shapes in api/src/gateways/stream-events.ts (StreamStartedPayload etc.) so notification payloads and socket payloads cannot drift. Decide whether error transitions get a distinct notification type.

2. Webhook delivery failure notifications (optional but recommended)

  • When a delivery becomes terminally failed (attemptCount exhausts MAX_RETRIES in api/src/webhooks/webhooks.service.ts), create a notification for the subscription owner. This requires WebhooksService to know the owner (the subscription row already carries userId) and to depend on NotificationsService — mirror the @Optional() gateway injection pattern in NotificationsService to keep the webhook path testable.

3. App-side surface

  • The app already has app/src/components/NotificationsDropdown/NotificationsDropdown.tsx; wire it to GET /notifications with the auth header story from the dashboard-auth work, and make the unread badge react to the notification:new socket event (NOTIFICATION_EVENTS.NEW in api/src/gateways/stream-events.ts). No new UI components are required beyond what exists.

Downstream impact

  • api/src/streams/streams.service.ts (notification trigger in update), api/src/webhooks/webhooks.service.ts (failure notifications), api/src/notifications/notifications.service.ts (unchanged API, new callers), api/src/webhooks/webhooks.module.ts / streams.module.ts (module wiring).
  • app/src/components/NotificationsDropdown/NotificationsDropdown.tsx, app/hooks/useNotifications.ts if created.
  • Contracts: extend tests/contracts with GET /notifications so the API shape is pinned.
  • SDK: Notification type if the SDK gains a notifications surface (optional; keep out of scope unless trivial).

Acceptance criteria

Service

  • A valid status transition creates exactly one notification for the stream owner, with read_at null and a payload containing the stream id, new status, and timestamp; invalid transitions create none.
  • The notification is pushed over the socket (notification:new) to the owner's user room and is returned by GET /notifications.
  • Terminally failed webhook deliveries create an owner notification (if the optional scope is taken) with the delivery id and error in the payload.

Tests

  • api/src/streams/streams.service.spec.ts: transitions create notifications with correct payloads; a webhook fan-out failure does not prevent the notification.
  • api/src/webhooks/webhooks.service.spec.ts: exhausted-retry delivery creates a notification; transient failures do not.
  • Contract test for GET /notifications passes in provider and consumer suites.
  • App test: the dropdown renders unread count from the endpoint and updates on notification:new.

Documentation

  • Notification types and triggers documented in the NotificationsService JSDoc and Swagger.

Out of scope

Email/push-channel delivery, notification preferences, and the notification retention sweep (already implemented in NotificationsService.sweepExpired).

Getting started

Real files in scope: api/src/notifications/notifications.service.ts, api/src/streams/streams.service.ts (update, dispatchStatusWebhook), api/src/webhooks/webhooks.service.ts, api/src/gateways/stream-events.ts, app/src/components/NotificationsDropdown/NotificationsDropdown.tsx, tests/contracts/src/ (new contract).

Verify with:

cd api && npm run typecheck && npm test
cd ../app && npm run typecheck && npm test

Good first files to read: api/src/notifications/notifications.service.ts, api/src/streams/streams.service.ts (dispatchStatusWebhook), api/src/gateways/stream-events.ts.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignapiREST API design and endpointsenhancementNew feature or requestfeaturewebsocketWebSocket / real-time features

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions