Skip to content

Stream status socket events are never emitted: StreamsGateway emit helpers have no production callers #519

Description

@Xhristin3

Problem

The WebSocket gateway's stream-status emit helpers are never called from production code. StreamsGateway (api/src/gateways/streams.gateway.ts) provides emitStarted, emitStopped, and emitError — the only production paths that would broadcast stream:started / stream:stopped / stream:error to subscribed clients — and a repo-wide search shows they are referenced only in the gateway's own spec (api/src/gateways/streams.gateway.spec.ts). No service or controller invokes them.

Meanwhile StreamsService.update() (api/src/streams/streams.service.ts) already fires the status-transition side effects: it validates transitions, persists the change, and dispatches webhooks via dispatchStatusWebhook — but nothing emits the socket event. The dashboard's realtime status feature (app/hooks/useStreamSocket.ts, which subscribes to stream:started/stream:stopped/stream:error and updates streamStatus) can never receive a status update, so the "live" status badge is permanently stale and issue #362's realtime requirement is unmet in practice.

The gateway class JSDoc promises this behaviour:

Wire events (server → client):

  • stream:started { streamId, userId, startedAt }
  • stream:stopped { streamId, userId, stoppedAt, reason? }
  • stream:error { streamId, userId?, occurredAt, code, message }

Root cause

// api/src/streams/streams.service.ts — update()
if (changes.status !== undefined && changes.status !== stream.status) {
  this.dispatchStatusWebhook(updated, changes.status)   // ← webhooks, not sockets
}

// api/src/gateways/streams.gateway.ts
emitStarted(payload: StreamStartedPayload): void { ... }  // ← never called anywhere

StreamsService depends on WebhooksService but not on StreamsGateway; the gateway is only injected where notifications are emitted (NotificationsService).

Why this is architecturally hard

  1. StreamsGateway is not a dependency of StreamsModule today, so wiring the emit means adding the dependency and deciding how the service reaches the gateway without a circular import (the gateway lives in GatewaysModule; NotificationsService injects it via @Optional()). There is an established pattern to copy (api/src/notifications/notifications.service.ts).
  2. The emit payloads (StreamStartedPayload, StreamStoppedPayload, StreamErrorPayload in api/src/gateways/stream-events.ts) must be built from the updated stream state and the transition — mirroring the payload shapes dispatchStatusWebhook already builds in StreamsService — so the two side-effect paths (webhook + socket) should share a single event-shape source to avoid drifting.
  3. The error transition is special: PATCH /streams/:id can set status: "error" but the payload contract for stream:error (code, message) has no source in UpdateStreamDto today. Decide whether the error event is emitted with defaults or whether UpdateStreamDto gains optional error fields — that is a small API contract decision.
  4. Scope of the broadcast: emitStarted/emitStopped/emitError broadcast to the per-stream room; the ownership story for who may subscribe to a room is a separate security issue, but the wiring here should not accidentally widen visibility.

Acceptance criteria

Service

  • A status transition via PATCH /streams/:id (e.g. inactive -> active) emits the matching stream:started/stream:stopped/stream:error event on the /streams namespace, scoped to the affected stream's room, with the payload shapes from api/src/gateways/stream-events.ts.
  • Invalid transitions (rejected by validateStatusTransition) do not emit any event.
  • A failed webhook dispatch does not suppress the socket emit (the two side effects are independent).

Tests

  • A StreamsService test asserts the gateway emit is invoked with the correct event name and payload for each valid transition (inactive->active, active->inactive, *->error, error->inactive).
  • A gateway test asserts the room-scoped broadcast for a connected socket that subscribed via stream:subscribe.

Documentation

  • The StreamsGateway class JSDoc's wire-event list matches what the service actually emits after the change.

Out of scope

Ownership enforcement on stream:subscribe (separate issue), and emitting events for anything other than status transitions.

Getting started

Real files in scope: api/src/streams/streams.service.ts, api/src/gateways/streams.gateway.ts, api/src/gateways/stream-events.ts, api/src/gateways/streams.gateway.spec.ts, api/src/streams/streams.service.spec.ts, api/src/notifications/notifications.service.ts (injection pattern), api/src/streams/streams.module.ts.

Verify with:

cd api && npm run typecheck && npm test

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignapiREST API design and endpointsbugSomething isn't workingwebsocketWebSocket / real-time features

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions