12 KiB
rooms-api Specification
Purpose
Read access to room sessions: the rooms list and its summary numbers, every query backed by an index.
Requirements
Requirement: Rooms list endpoint
Edition: CE. GET /api/v1/rooms SHALL list the room sessions created in the range with the common query contract, the
filters status, q, features and nodeId, the sort keys createdAt (default, index-backed) and the ranked
endedAt, duration, uniqueIdentities and active, order (desc default, asc), and the list envelope
{ items, nextCursor, prevCursor, total? } with total under the DASHBOARD_LIST_TOTAL_MAX_RANGE rule.
Scenario: Default list
- GIVEN 40 rooms created in the last 24 hours and 5 older ones
- WHEN the client requests
GET /api/v1/roomswith a valid token - THEN the response is
200with the 25 most recently created rooms of the last 24 hours,nextCursorset,prevCursor: nullandtotal: 40
Scenario: Cursor walk
- GIVEN 60 rooms in range and
limit=25 - WHEN the client follows
nextCursortwice and thenprevCursoronce - THEN the pages hold rooms 1-25, 26-50, 51-60 and 26-50 with no gaps or duplicates, each page ordered by
createdAt descwith_id descas tie-breaker
Scenario: Ranked cursor walk
- GIVEN 60 rooms created in range, two of them active, and
sort=duration&limit=25 - WHEN the client follows
nextCursortwice - THEN the pages hold rooms 1-25, 26-50 and 51-60 of the duration ranking at the first page's
asOf, withtotal: 60on each page and no gaps or duplicates
Scenario: Common query contract
- WHEN the client requests
GET /api/v1/rooms - THEN the common query contract it accepts is
from,to,tz,limit(1-100, default 25) and an opaquecursor
Requirement: Room list item
Edition: CE. Each rooms list item SHALL be the room document's public projection: identity, lifecycle, node,
participant, egress and ingress counters, bandwidth, feature flags, the end cause behind the "estimated" cue, the last
stats flush, the media statistics mark and the stale flag of api-conventions, never internal id arrays,
schemaVersion or expiresAt.
Participant counters count STANDARD sessions only; participants.unique counts distinct STANDARD identities.
Scenario: Fields of an item
- WHEN an item is read
- THEN it has
sid,name,status(active|closed),createdAt,endedAt?,durationMs?,closedBy?(event|reconciler),nodeId?,participants { sessions, unique, active, peakActive },egress { total, active },ingress { sessions, active },bytes { up, down },features { egress, ingress, agent, sip },lastStatsAt?,stale?,mediaStats?and noparticipantSids,identitiesor similar array
Scenario: Active room
- GIVEN a room still open with three active STANDARD participants and one active EGRESS participant
- WHEN it is returned
- THEN
statusisactive,endedAt,durationMsandclosedByare absent andparticipants.activeis 3
Scenario: Unique identities
- GIVEN a closed room where STANDARD identity
alicehad three sessions,bobone, and an AGENT session joined - WHEN it is returned
- THEN
participants.sessionsis 4 andparticipants.uniqueis 2
Scenario: Stale active room
- GIVEN the default writer batch interval of 10 s and an active room whose
lastStatsAtis 3 minutes old at the server's clock - WHEN it is returned
- THEN it has
stale: true, and an active room whoselastStatsAtis 20 s old has nostaleproperty
Scenario: Room of a node without media statistics
- GIVEN a room document with
mediaStats: falseandbytes { up: 0, down: 0 } - WHEN it is returned
- THEN it has
mediaStats: falseandbytes { up: 0, down: 0 }, and an item of a room without the field has nomediaStatsproperty
Requirement: Status filter
Edition: CE. The rooms list SHALL accept status as active, closed or all (default all) and reject any other
value with 400 VALIDATION_ERROR.
Scenario: Active rooms only
- WHEN the client sends
?status=active - THEN every item has
status: "active"and the time range still applies tocreatedAt
Scenario: Unknown status
- WHEN the client sends
?status=open - THEN the response is
400with codeVALIDATION_ERRORanddetails.issues[0].pathequal to["status"]
Requirement: Time range and sort keys
Edition: CE. The rooms list SHALL apply from/to to createdAt for every sort key. createdAt SHALL be served in
index order; endedAt, duration, uniqueIdentities and active SHALL be ranked sorts as api-conventions defines,
accepted with any status when the range is within DASHBOARD_LIST_TOTAL_MAX_RANGE and answered 400
SORT_RANGE_TOO_WIDE beyond it. Any other sort key SHALL be rejected with 400 VALIDATION_ERROR.
Scenario: Closed rooms by end time
- GIVEN a room created 3 days ago that ended 2 hours ago, and rooms created 5 and 8 hours ago that ended 3 hours and 1 hour ago
- WHEN the client sends
?status=closed&sort=endedAt&from=now-24h&to=now - THEN
itemsholds the room ended 1 hour ago and the room ended 3 hours ago, in that order, and not the room created 3 days ago, whosecreatedAtis outside the range
Scenario: End-time sort without the closed filter
- GIVEN created in the last 24 hours: a room ended 1 hour ago, a room ended 10 minutes ago and an active room
- WHEN the client sends
?sort=endedAt&status=all - THEN the response is
200with the active room (ranked atasOf), the room ended 10 minutes ago and the room ended 1 hour ago, in that order
Scenario: Rooms by unique identities
- GIVEN three rooms created in range with
participants.unique12, 3 and 7 - WHEN the client sends
?sort=uniqueIdentities - THEN
itemsholds the rooms with 12, 7 and 3 unique identities, in that order
Scenario: Rooms by active participants
- GIVEN an active room with 5 active participants, an active room with 2 and a closed room, all created in range
- WHEN the client sends
?sort=active&order=desc - THEN
itemsholds the room with 5, the room with 2 and the closed room (0), in that order
Scenario: Ranked sort beyond the limit
- GIVEN the default
DASHBOARD_LIST_TOTAL_MAX_RANGEof 7 days - WHEN the client sends
?sort=uniqueIdentities&from=now-30d&to=now - THEN the response is
400with codeSORT_RANGE_TOO_WIDE
Scenario: Unsupported sort key
- WHEN the client sends
?sort=name - THEN the response is
400with codeVALIDATION_ERRORanddetails.issues[0].pathequal to["sort"]
Scenario: Ranked values
- WHEN the client requests a ranked sort
- THEN
endedAtranksendedAt(asOfwhile active),durationranksdurationMs(asOf − createdAtwhile active),uniqueIdentitiesranksparticipants.uniqueandactiveranksparticipants.active
Requirement: Name prefix search and SID lookup
Edition: CE. The rooms list SHALL treat q as a case-insensitive prefix over the room name when it has at least two
characters, combined with status, features and nodeId, SHALL reject a one-character q with 400, and SHALL
treat a value matching ^RM_[A-Za-z0-9]+$ as an exact SID lookup that ignores from, to, status, features,
nodeId and sort and returns at most one item.
Scenario: Prefix search
- GIVEN rooms named
Demo-Room,demo-2andmy-democreated in range - WHEN the client sends
?q=DE - THEN
itemsholdsDemo-Roomanddemo-2ordered bycreatedAt desc, and notmy-demo
Scenario: One character
- WHEN the client sends
?q=d - THEN the response is
400with codeVALIDATION_ERROR,details.issues[0].pathequal to["q"]and a message stating the two-character minimum
Scenario: Exact SID outside the range
- GIVEN a closed room
RM_ab12cdcreated 40 days ago - WHEN the client sends
?q=RM_ab12cd&from=now-24h&status=active - THEN the response is
200with that single room initems,total: 1and both cursorsnull
Scenario: Prefix within one node
- GIVEN rooms named
demo-aonnode-aanddemo-bonnode-b, both created in range - WHEN the client sends
?q=demo&nodeId=node-a - THEN
itemsholdsdemo-aonly
Requirement: Feature and node filters
Edition: CE. The rooms list SHALL accept features as a comma-separated subset of egress, ingress, agent, sip
and return rooms whose flag is set for any of the listed values, SHALL accept nodeId as an exact match on the room's
node, and SHALL reject an unknown feature value with 400.
Scenario: Rooms with egress or ingress
- GIVEN a room with an egress, a room with an ingress session and a plain room
- WHEN the client sends
?features=egress,ingress - THEN the first two rooms are returned and the plain room is not
Scenario: Rooms of one node
- WHEN the client sends
?nodeId=node-a&status=active - THEN every item has
nodeId: "node-a"andstatus: "active"
Scenario: Unknown feature
- WHEN the client sends
?features=egress,webhook - THEN the response is
400with codeVALIDATION_ERRORanddetails.issues[0].pathequal to["features"]
Requirement: Rooms summary endpoint
Edition: CE. GET /api/v1/rooms/summary SHALL accept from, to and tz and answer 200 with
{ activeRooms, roomsInRange, participantSessions, activeParticipants, approximate }, where the participant counts
are STANDARD sessions and the two range counts are exact when the range is within DASHBOARD_LIST_TOTAL_MAX_RANGE
and are hour-aligned sums of the hourly rollups with approximate: true beyond it.
Scenario: Short range
- GIVEN 7 active rooms, 120 rooms created, 900 STANDARD and 40 EGRESS participant sessions joined in the last 24 hours, and 31 active STANDARD participants
- WHEN the client requests
GET /api/v1/rooms/summary?from=now-24h&to=now - THEN the response is
{ activeRooms: 7, roomsInRange: 120, participantSessions: 900, activeParticipants: 31, approximate: false }
Scenario: Long range
- GIVEN the default threshold of 7 days
- WHEN the client requests
GET /api/v1/rooms/summary?from=now-30d&to=now - THEN
roomsInRangeandparticipantSessionsequal the sums ofrooms.createdandparticipants.joinedof the global hourly rollups whose bucket starts inside the range, andapproximateistrue
Requirement: Index-backed room queries
Edition: CE. Every query of this capability SHALL run on an index of the shared contract
(dashboard-ce/typings/contract/indexes.ts), or on _id for the hourly rollups the long-range summary reads: no
winning plan contains a COLLSCAN, and totalDocsExamined is bounded
by the page size plus the documented residual filters for the index-ordered pages and by the rooms created in the range
for the ranked sorts.
Scenario: Explain plans on the generated database
- GIVEN a database generated by the data generator at scenario M
- WHEN the list (every filter combination and sort) and summary queries are explained
- THEN each winning plan uses an index of the shared contract and none contains
COLLSCAN
Scenario: Ranked sorts over 7 days
- GIVEN a database generated at scenario M for 7 days
- WHEN the list is explained with each ranked sort key, with and without
status,q,nodeIdandfeatures - THEN each plan scans the range index of its filters (
createdAt_id,status_createdAt_id,nameLower_createdAt,nodeId_status_createdAt_idor the feature indexes),totalDocsExaminedis at most the rooms created in the range, and the p95 of a page is within 300 ms
Scenario: Name prefix within one node
- WHEN the list with
qandnodeIdis explained - THEN the plan uses
nameLower_createdAtwithnodeIdas a residual filter