openvidu-dashboard: the writer stores values starting with $ as received

chore/update-openvidu-actions-1.0.25
juancarmore 2026-10-27 18:09:41 +01:00
parent da6e6a84f6
commit cc7c0b91f7
5 changed files with 119 additions and 0 deletions

View File

@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-27

View File

@ -0,0 +1,50 @@
# Design
## Context
- Every entity update of the writer is a pipeline of `$set` stages built by `stage()` from the assignments of a
`fields` set. Values are mixed: expressions from the helpers of `expr.go` (`$ifNull`, `$cond`, `$setUnion`…), field
paths and variables (`$name`, `$$this.v`, `$$REMOVE`) and data from the event (names, identities, errors, metadata,
encoded messages, attributes).
- Nothing tells data apart from expressions, so MongoDB evaluates a data string starting with `$` and a data document
whose keys look like operators or paths.
## Goals / Non-Goals
**Goals:**
- Data is never evaluated, whatever it holds, without touching each of the call sites.
**Non-Goals:**
- Changing how expressions are built, or the classic updates of the rollups and the heartbeat, where values are never
evaluated.
## Decisions
### Typed references, escaped stages
Field paths and variables get their own type, `ref`, returned by `field()` and used for `$$REMOVE` and the `$$this`
variables. `stage()` walks its assignments and wraps in `$literal` every plain string that starts with `$`; a `ref`
becomes its string. The walk enters documents and arrays, and leaves an existing `$literal` as it is.
Alternative rejected: wrapping data at each call site. There are about 300 assignments, and a missed one fails only
with an unusual value.
### Maps wrapped at the source
Participant attributes are the only document whose keys come from the client. The projection wraps them in
`$literal`, so their keys are neither operators nor paths. The other documents (encoded messages, the request summary,
outputs, permissions, client info) have fixed keys and are walked like any expression.
### Data model
- No collection, field, index, retention or schema-version change. Values that were lost or evaluated are now stored
as received.
- Migration from legacy events: the legacy migration projects with the same code, so migrated documents get the fix
too.
- Performance: one walk of each stage before it is sent, linear in its size; negligible next to the write.
## Risks / Trade-offs
- [A future expression writes a field path as a plain string] → It would be stored as a string instead of evaluated.
The integration suites compare every projected document with its expected contract fields, so the change shows up
there.

View File

@ -0,0 +1,38 @@
# Proposal
## Why
The writer updates entity documents with aggregation pipelines, where a value is an expression. A string that starts
with `$` is read as a field path or a variable, and the keys of a map as operators or paths. A room named `$demo`
loses its name, an identity `$$ROOT` is replaced by the document being updated, and a participant attribute whose key
starts with `$` makes the update fail.
## What Changes
- Every value the projectors write is stored as received, after the allow-list and its redactions: strings starting
with `$`, and map keys that start with `$` or hold a dot, included.
Scope excludes the following:
- Raw events, which are inserted, not updated, and keep their values already.
- Field names the writer derives from fixed vocabularies (kinds, sources, families, API methods).
Edition: CE and PRO (writer). Repositories: openvidu-livekit (`openvidu/analytics/projection/`) and the specs of the
dashboard.
CHANGELOG: none. The analytics data model has not shipped in a release.
## Capabilities
### New Capabilities
None.
### Modified Capabilities
- `server-analytics-writer`: values are stored as received.
## Impact
- Writer: the expression helpers and the stage builder of `openvidu/analytics/projection/`, and the participant
attributes. Integration tests of the writer.
- Dashboard: none.

View File

@ -0,0 +1,10 @@
## ADDED Requirements
### Requirement: Values stored as received
Edition: CE and PRO. Every value the projectors write into a document SHALL be stored as received, after the
allow-list and its redactions, including strings that start with `$` and map keys that start with `$` or hold a dot.
#### Scenario: Values starting with a dollar sign
- **GIVEN** `store_metadata: true`, a room `$room` with metadata `$$ROOT`, a participant `$$ROOT` named `$name` with metadata `$metadata` and attributes `{ "$role": "$host", "team.name": "$blue" }`, and an egress of the room that fails with error `$error`
- **WHEN** their events are projected
- **THEN** the room has `name: "$room"`, `config.metadata: "$$ROOT"` and one unique identity, the participant has `identity: "$$ROOT"`, `name: "$name"`, `metadata: "$metadata"` and those attributes, and the egress has `error: "$error"`

View File

@ -0,0 +1,19 @@
# Tasks
## 1. Writer (openvidu-livekit)
- [x] 1.1 Add the `ref` type to `openvidu/analytics/projection/expr.go` for `field()`, `$$REMOVE` and the `$$this`
variables of the projection, and escape every other string starting with `$` in `stage()`. Wrap the participant
attributes in `$literal`. Verify with the unit tests of `openvidu/analytics/...` and the lint of the fork.
- [x] 1.2 Add a writer integration test with a room, a participant and an egress whose values start with `$`. Verify
with the analytics integration tests, the migration and contract suites included.
## 2. Docs and CHANGELOG
- [x] 2.1 No CHANGELOG line: the analytics data model has not shipped. No `AGENTS.md` change. Verify by reading the
change against both files.
## 3. Integration check
- [x] 3.1 Run the spec validation and the format check of the dashboard, and `typings/contract/writer-check.sh` against
the fork. Verify that they pass.