mirror of https://github.com/OpenVidu/openvidu.git
openvidu-dashboard: archive openapi-reference, keep only the specs
parent
0a27846ecf
commit
322781dd73
|
|
@ -1,2 +0,0 @@
|
||||||
schema: spec-driven
|
|
||||||
created: 2026-10-27
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
# Design
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
- `RouteRegistry` holds one `RouteDefinition` per route, and `openApiDocument(registry, version)`
|
|
||||||
(`src/api/openapi.ts`) turns the listed routes into a zod-openapi 6 document.
|
|
||||||
- How the generator fills error responses today:
|
|
||||||
- It adds `400` to every operation, `401` to every non-public one, and `403` "PRO feature on CE" to every PRO route.
|
|
||||||
No document lists those routes in an edition that answers that 403: locks are left out in CE, and PRO never
|
|
||||||
answers it.
|
|
||||||
- It adds the `ApiError` schema only to the errors it adds itself. Errors a route declares (login `401`/`429`,
|
|
||||||
`ready` `503`, analytics `403`, detail `404`) have no body.
|
|
||||||
- Parameter descriptions come from `.meta()` on the Zod schemas:
|
|
||||||
- A `.meta()` placed before `.default()` stays inside the parameter's schema and never reaches the parameter object.
|
|
||||||
This is why `from` and `to` show no description.
|
|
||||||
- `csvParam()` parses a string, so its parameters render as plain strings.
|
|
||||||
- `DashboardOptions` carries the edition and version. The public base path is bound to `TOKENS.BasePath` by a CE
|
|
||||||
startup hook, before the docs router is built.
|
|
||||||
|
|
||||||
## Goals / Non-Goals
|
|
||||||
|
|
||||||
**Goals:**
|
|
||||||
- A document that a client generator and a reader can use as is.
|
|
||||||
- Pieces that let PRO document its routes the same way, without CE knowing them.
|
|
||||||
- Tests that keep the invariants for every route, not only for today's.
|
|
||||||
|
|
||||||
**Non-Goals:**
|
|
||||||
- Changing requests, responses, status codes or authentication.
|
|
||||||
- Serving the document in production.
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
### Explicit `operationId`, checked by the registry
|
|
||||||
|
|
||||||
- `RouteDefinition` gains a required `operationId`, camelCase, verb first:
|
|
||||||
- `list<Resources>` for paged lists;
|
|
||||||
- `get<Resource>` for single documents;
|
|
||||||
- `get<Resource><Sub>` for sub-resources and summaries;
|
|
||||||
- `login`, `refreshSession`, `logout`, `getCurrentAdmin` for auth;
|
|
||||||
- `getHealth`, `getReadiness` for the probes.
|
|
||||||
- `register()` and `replace()` throw on an `operationId` another route already uses. A lock has none and is never
|
|
||||||
listed.
|
|
||||||
|
|
||||||
Rejected: deriving IDs from method and path. Names such as `getApiV1RoomsSidSeries` read badly, and they would change
|
|
||||||
when a path changes.
|
|
||||||
|
|
||||||
### Declared tags
|
|
||||||
|
|
||||||
- `registry.tag(name, description)` declares a tag. Each route module declares its tags before registering its
|
|
||||||
routes.
|
|
||||||
- `register()` throws on an undeclared tag.
|
|
||||||
- The document lists the declared tags, in declaration order, that at least one listed operation uses.
|
|
||||||
- Tag names are Title Case nouns: `Probes`, `Auth`, `Info`, `Overview`, `Rooms`, `Participants`, `Agents`, `Egress`,
|
|
||||||
`Ingress`, `Analytics`, `Status`.
|
|
||||||
- PRO declares its own tags (such as `Tracks`) the same way.
|
|
||||||
|
|
||||||
### Error responses built by the generator
|
|
||||||
|
|
||||||
- `400` (`Invalid request`) is added only when the route has params, a query or a body.
|
|
||||||
- `401` is added for `bearer` and `optional-bearer` routes.
|
|
||||||
- The automatic `403` for PRO routes is removed.
|
|
||||||
- Every response with status ≥ 400 gets `application/json` with `ApiError` when the route gives no schema.
|
|
||||||
- `RouteResponse` gains `edition?: Edition`. A response with an edition is left out of other editions' documents. The
|
|
||||||
CE analytics routes mark their `PRO_FEATURE` `403` as `ce`. The registry exposes its edition for this.
|
|
||||||
|
|
||||||
### Parameters
|
|
||||||
|
|
||||||
- **Shared pieces in `query.ts`:**
|
|
||||||
- Shared parameters carry their descriptions after `.default()`, so zod-openapi lifts them to the parameter: `from`,
|
|
||||||
`to`, `tz`, `limit`, `order`, `cursor` and `q`.
|
|
||||||
- `to` gets its own example (`now`).
|
|
||||||
- **`csvParam(values)`:** adds `param: { style: 'form', explode: false }` and
|
|
||||||
`override: { type: 'array', items: { type: 'string', enum: values } }`. The route adds the description. The other
|
|
||||||
comma-separated parameters do the same: `features`, the egress `status` with its `all`, and the analytics `metrics`
|
|
||||||
and `dimensions`.
|
|
||||||
- **`If-None-Match`:** the generator adds this header parameter to every operation whose responses include `304`.
|
|
||||||
- **Integration test:** it asserts that every parameter of the booted document has a description. A new route without
|
|
||||||
one fails CI instead of silently degrading the document.
|
|
||||||
|
|
||||||
Alternatives rejected for the list parameters:
|
|
||||||
- Changing their schemas to `z.array()` with `explode`: Express parses `a,b` as one string, so validation would change.
|
|
||||||
- A document-wide `override` hook keyed on the transform: too implicit.
|
|
||||||
|
|
||||||
### Refresh cookie and `Set-Cookie`
|
|
||||||
|
|
||||||
- `RouteDefinition` gains an optional `refreshCookie: 'required' | 'optional'`. It only affects the document;
|
|
||||||
authentication is unchanged, and the auth routes stay `public`.
|
|
||||||
- The generator adds the `refreshCookie` security scheme (`apiKey`, `in: cookie`, name `REFRESH_COOKIE`). It sets
|
|
||||||
`security: [{ refreshCookie: [] }]` for refresh, and `[{}, { refreshCookie: [] }]` for logout, which also answers
|
|
||||||
without a cookie.
|
|
||||||
- The auth responses that set or clear the cookie declare a `Set-Cookie` header with its attributes: login `200`,
|
|
||||||
refresh `200` and `401`, logout `204`.
|
|
||||||
|
|
||||||
### Metadata
|
|
||||||
|
|
||||||
- `openApiDocument(registry, { version, basePath, license? })`:
|
|
||||||
- `servers: [{ url: basePath without its trailing slash, or '/' }]`;
|
|
||||||
- `info.description`: one Markdown text of the conventions, edition-neutral;
|
|
||||||
- `info.license`: the given one, or `{ name: 'Apache 2.0', identifier: 'Apache-2.0' }`.
|
|
||||||
- `DashboardOptions` gains an optional `apiLicense`, which an extending edition passes. CE passes nothing.
|
|
||||||
|
|
||||||
### Schema with required keys never defined
|
|
||||||
|
|
||||||
`byInputType: z.record(inputType, count)` renders `required` without `properties`. It becomes
|
|
||||||
`z.object({ rtmp: count, whip: count, url: count })`, which validates the same values.
|
|
||||||
|
|
||||||
### Unknown-route message
|
|
||||||
|
|
||||||
`apiNotFound` is mounted on `/api`, so `req.path` lacks the prefix. The message uses the path of `req.originalUrl`
|
|
||||||
(without the query string), which is the path the client sent: `${req.baseUrl}${req.path}` would turn `/api` into
|
|
||||||
`/api/`.
|
|
||||||
|
|
||||||
## Risks / Trade-offs
|
|
||||||
|
|
||||||
- **[Breaking for PRO at compile time]** `operationId` becomes required, and undeclared tags throw. → PRO adds them in
|
|
||||||
the same move of `ce.ref`, with its own document test.
|
|
||||||
- **[`override` drifting from the parser]** The array rendering duplicates the allowed values. → Both come from the same
|
|
||||||
`values` argument of `csvParam()`. The other list parameters pass their own enum constant to both the parser and the
|
|
||||||
override.
|
|
||||||
|
|
||||||
## Migration Plan
|
|
||||||
|
|
||||||
This changes the document only; deploy as usual. Clients that generated code from the old document get stable method
|
|
||||||
names from now on.
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
# Proposal
|
|
||||||
|
|
||||||
## Why
|
|
||||||
|
|
||||||
The OpenAPI document (`GET /api/v1/docs/openapi.json`) lists every route, but it does not describe the API well enough
|
|
||||||
to use it without reading the code:
|
|
||||||
- No operation has an `operationId`.
|
|
||||||
- Most parameters have no description.
|
|
||||||
- Comma-separated lists are typed as bare strings.
|
|
||||||
- Several error responses are missing or have no body schema.
|
|
||||||
- Some responses are declared that the edition never answers.
|
|
||||||
- There is no `servers`, `license` or tag list.
|
|
||||||
|
|
||||||
The unknown-route `404` also names a path without its `/api` prefix.
|
|
||||||
|
|
||||||
## What Changes
|
|
||||||
|
|
||||||
- **Document metadata:**
|
|
||||||
- `info` gains a description of the API conventions (authentication, time ranges, pagination, errors, conditional
|
|
||||||
requests, request IDs, versioning) and the license.
|
|
||||||
- `servers` gains the public base path.
|
|
||||||
- A `tags` list, with one description per tag, in a stable order.
|
|
||||||
- **Operations:**
|
|
||||||
- Every operation has a unique `operationId` and tags from that list.
|
|
||||||
- Every parameter has a description.
|
|
||||||
- Comma-separated list parameters are documented as arrays of their allowed values (`style: form`, `explode: false`).
|
|
||||||
- Routes answered conditionally declare the `If-None-Match` request header next to their `ETag` and `304`.
|
|
||||||
- The auth routes declare the refresh cookie they read (as a cookie security scheme) and the `Set-Cookie` header they
|
|
||||||
send.
|
|
||||||
- **Error responses:**
|
|
||||||
- Every error response an operation can answer is declared with the `ApiError` schema.
|
|
||||||
- `400` is declared only for operations that take input.
|
|
||||||
- A response that only one edition answers appears only in that edition's document. This covers the automatic
|
|
||||||
`PRO_FEATURE` `403`, which no listed route answers, and the `403` of PRO metrics on the CE analytics routes.
|
|
||||||
- **Schemas:** the ingress summary's `byInputType` is a plain object with its three keys. It was a record that
|
|
||||||
required keys it never defined.
|
|
||||||
- **404 message:** the unknown-route `404` message names the full request path, `No API route for GET
|
|
||||||
/api/v1/does-not-exist`.
|
|
||||||
|
|
||||||
Scope excludes the following:
|
|
||||||
- No route, parameter or response body changes.
|
|
||||||
- The PRO routes are documented by the PRO workspace on top of the generic pieces this change adds, so CE needs no
|
|
||||||
PRO-specific code, and the seams stay generic: an optional license for the document and the per-route fields.
|
|
||||||
|
|
||||||
Edition: CE, and the generic pieces PRO builds on. Repository: `OpenVidu/openvidu` (dashboard) only.
|
|
||||||
|
|
||||||
## Capabilities
|
|
||||||
|
|
||||||
### New Capabilities
|
|
||||||
|
|
||||||
None.
|
|
||||||
|
|
||||||
### Modified Capabilities
|
|
||||||
|
|
||||||
- `api-conventions`:
|
|
||||||
- The OpenAPI document gains requirements on its metadata, operations, parameters and error responses.
|
|
||||||
- The unknown-route scenario fixes its message.
|
|
||||||
|
|
||||||
## Impact
|
|
||||||
|
|
||||||
- Backend:
|
|
||||||
- `src/api/openapi.ts` (generator).
|
|
||||||
- `src/api/route-registry.ts` (`operationId`, tag declarations, per-response edition, refresh cookie field).
|
|
||||||
- `src/api/query.ts` (shared parameter descriptions and the array form of `csvParam`).
|
|
||||||
- Every `src/*/*-routes.ts` (operation IDs, tags, parameter descriptions, error responses).
|
|
||||||
- `src/ingress/ingress-responses.ts`.
|
|
||||||
- `src/http/errors.ts`.
|
|
||||||
- `src/lifecycle/startup.ts` (base path, and an optional license from `DashboardOptions`).
|
|
||||||
- Tests: unit tests of the generator and the registry, and an integration test that checks the invariants above on the
|
|
||||||
document of the booted CE dashboard.
|
|
||||||
- PRO: its own routes must supply `operationId`, declared tags and parameter descriptions. That is a compile-time
|
|
||||||
requirement once it moves `ce.ref`.
|
|
||||||
- CHANGELOG (`CHANGELOG.md`, 3.10.0, Added › Data), a new line: "API reference outside production: an OpenAPI 3.1
|
|
||||||
document at `/api/v1/docs/openapi.json` and a browsable page at `/api/v1/docs`, with operation IDs, described
|
|
||||||
parameters and every error response."
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
## MODIFIED Requirements
|
|
||||||
|
|
||||||
### Requirement: Base path and resource naming
|
|
||||||
|
|
||||||
Edition: CE. Every dashboard API route SHALL live under `/api/v1`, use lower-case plural nouns for resources
|
|
||||||
(`rooms`, `participants`, `agents`, `tracks`, `egresses`, `ingresses`, `analytics`, `overview`, `status`, `auth`,
|
|
||||||
`info`), nest
|
|
||||||
sub-resources at most one level under the parent identifier, and answer every unknown `/api` path with a JSON
|
|
||||||
`404` instead of the SPA fallback.
|
|
||||||
|
|
||||||
#### Scenario: Sub-resource path
|
|
||||||
|
|
||||||
- **GIVEN** a room with SID `RM_abc`
|
|
||||||
- **WHEN** the client requests its participants
|
|
||||||
- **THEN** the path is `GET /api/v1/rooms/RM_abc/participants`
|
|
||||||
|
|
||||||
#### Scenario: Unknown API path
|
|
||||||
|
|
||||||
- **WHEN** the client requests `GET /api/v1/does-not-exist` or `GET /api/anything`
|
|
||||||
- **THEN** the response is `404` with body `{ error: { code: 'NOT_FOUND', message } }` and
|
|
||||||
`Content-Type: application/json`, never the SPA `index.html`, and the message names the full path, such as
|
|
||||||
`No API route for GET /api/v1/does-not-exist`
|
|
||||||
|
|
||||||
## ADDED Requirements
|
|
||||||
|
|
||||||
### Requirement: OpenAPI document metadata
|
|
||||||
|
|
||||||
Edition: CE. The OpenAPI document SHALL carry the following:
|
|
||||||
- an `info.description` of the API conventions;
|
|
||||||
- `info.license`: Apache 2.0 unless the edition provides another;
|
|
||||||
- one `servers` entry with the dashboard's public base path;
|
|
||||||
- a `tags` list with a description per tag, holding only the tags of the listed operations.
|
|
||||||
|
|
||||||
#### Scenario: Behind a base path
|
|
||||||
|
|
||||||
- **GIVEN** the server runs with `DASHBOARD_BASE_PATH=/dashboard/` and `NODE_ENV=development`
|
|
||||||
- **WHEN** the client reads the document
|
|
||||||
- **THEN** `servers[0].url` is `/dashboard`, and the paths start with `/api/v1`
|
|
||||||
|
|
||||||
#### Scenario: Conventions described
|
|
||||||
|
|
||||||
- **WHEN** the client reads `info.description`
|
|
||||||
- **THEN** it explains the following:
|
|
||||||
- the bearer access token and the refresh cookie;
|
|
||||||
- the `from`, `to` and `tz` time expressions;
|
|
||||||
- cursor pagination with `limit` and `total`;
|
|
||||||
- the `ApiError` envelope;
|
|
||||||
- `ETag` and `If-None-Match`;
|
|
||||||
- the `X-Request-Id` header;
|
|
||||||
- the versioning policy.
|
|
||||||
|
|
||||||
#### Scenario: Tags
|
|
||||||
|
|
||||||
- **WHEN** the client reads `tags`
|
|
||||||
- **THEN** every operation's tags are listed there with a description, and no listed tag lacks an operation
|
|
||||||
|
|
||||||
### Requirement: OpenAPI operations
|
|
||||||
|
|
||||||
Edition: CE. Every operation of the OpenAPI document SHALL have the following:
|
|
||||||
- a unique `operationId`, a summary and tags;
|
|
||||||
- a description for every parameter, with comma-separated list parameters as arrays of their allowed values
|
|
||||||
(`style: form`, `explode: false`);
|
|
||||||
- an `If-None-Match` header parameter when it can answer `304`;
|
|
||||||
- on the auth routes, the refresh cookie as a cookie security scheme, and the `Set-Cookie` header of the responses that
|
|
||||||
set or clear it.
|
|
||||||
|
|
||||||
#### Scenario: Operation identifiers
|
|
||||||
|
|
||||||
- **WHEN** the client reads every operation of the document
|
|
||||||
- **THEN** each has an `operationId` that no other operation shares, such as `listRooms` for `GET /api/v1/rooms`
|
|
||||||
|
|
||||||
#### Scenario: List parameter
|
|
||||||
|
|
||||||
- **WHEN** the client reads the `kind` parameter of `GET /api/v1/participants`
|
|
||||||
- **THEN** it has a description, `style: form`, `explode: false`, and an array schema whose items enumerate the
|
|
||||||
participant kinds
|
|
||||||
|
|
||||||
#### Scenario: Conditional request
|
|
||||||
|
|
||||||
- **WHEN** the client reads `GET /api/v1/overview`
|
|
||||||
- **THEN** it declares the `If-None-Match` header parameter, the `ETag` header of its `200`, and its `304`
|
|
||||||
|
|
||||||
#### Scenario: Refresh cookie
|
|
||||||
|
|
||||||
- **WHEN** the client reads `POST /api/v1/auth/refresh`
|
|
||||||
- **THEN** its security names the refresh cookie scheme (`in: cookie`, `name: ov_dashboard_refresh`), and its `200` and
|
|
||||||
`401` declare `Set-Cookie`
|
|
||||||
|
|
||||||
### Requirement: OpenAPI error responses
|
|
||||||
|
|
||||||
Edition: CE. Every error response an operation can answer SHALL be declared with the `ApiError` schema. This means
|
|
||||||
`400` only for operations that take parameters or a body, `401` for operations that need or accept a token, and the
|
|
||||||
route's own errors such as `404`, `429` and `503`. A response that only one edition answers SHALL appear only in that
|
|
||||||
edition's document.
|
|
||||||
|
|
||||||
#### Scenario: Operation without input
|
|
||||||
|
|
||||||
- **WHEN** the client reads `GET /api/v1/health`
|
|
||||||
- **THEN** it declares no `400`
|
|
||||||
|
|
||||||
#### Scenario: Error body
|
|
||||||
|
|
||||||
- **WHEN** the client reads the `429` of `POST /api/v1/auth/login`
|
|
||||||
- **THEN** its content is `application/json` with the `ApiError` schema
|
|
||||||
|
|
||||||
#### Scenario: Edition-specific error
|
|
||||||
|
|
||||||
- **WHEN** the client reads `GET /api/v1/analytics/series` in the CE document
|
|
||||||
- **THEN** it declares the `403` of a PRO metric, dimension or node scope; and no CE operation declares a `403`
|
|
||||||
`PRO_FEATURE` for being a PRO route
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
# Tasks
|
|
||||||
|
|
||||||
## 1. Backend: registry and generator
|
|
||||||
|
|
||||||
- [x] 1.1 In `src/api/route-registry.ts`, add:
|
|
||||||
- a required `operationId` and an optional `refreshCookie: 'required' | 'optional'` to `RouteDefinition`;
|
|
||||||
- `edition?: Edition` to `RouteResponse`;
|
|
||||||
- `tag(name, description)` and the `edition` getter to `RouteRegistry`.
|
|
||||||
`register()` and `replace()` reject a repeated `operationId` and an undeclared tag. Verify with
|
|
||||||
`route-registry.test.ts` cases for both rejections.
|
|
||||||
- [x] 1.2 In `src/api/openapi.ts`, change `openApiDocument(registry, { version, basePath, license? })` to emit:
|
|
||||||
- `servers`, `info.description`, `info.license` and `tags` (declared and used, in declaration order);
|
|
||||||
- `operationId`;
|
|
||||||
- `400` only with input, `401` for `bearer` and `optional-bearer`, no automatic `403`;
|
|
||||||
- `ApiError` on every error without a schema;
|
|
||||||
- responses filtered by `edition`;
|
|
||||||
- the `If-None-Match` parameter on operations with `304`;
|
|
||||||
- the `refreshCookie` security scheme;
|
|
||||||
and drop the dead locked-route description. Verify with `route-registry.test.ts` cases for each rule.
|
|
||||||
- [x] 1.3 In `src/lifecycle/startup.ts` and `src/index.ts`, add the optional `DashboardOptions.apiLicense` and pass it,
|
|
||||||
with the bound base path, to the document. Verify with the typecheck and the `servers` case of 1.2.
|
|
||||||
|
|
||||||
## 2. Backend: parameters and routes
|
|
||||||
|
|
||||||
- [x] 2.1 In `src/api/query.ts`, describe `from`, `to`, `tz`, `limit`, `order`, `cursor` and `q` at the parameter
|
|
||||||
level (after `.default()`), with `to` getting its own example. Make `csvParam()` render as an array of its values
|
|
||||||
with `style: form`, `explode: false`. Verify with a generator unit case on a ranged list query.
|
|
||||||
- [x] 2.2 In every `src/*/*-routes.ts` and `src/api/probes.ts`:
|
|
||||||
- declare the module's tags;
|
|
||||||
- give each route its `operationId`;
|
|
||||||
- describe every route-specific parameter;
|
|
||||||
- render the other comma-separated parameters as arrays (`features`, egress `status`, analytics `metrics` and
|
|
||||||
`dimensions`);
|
|
||||||
- mark the analytics `403` as `edition: 'ce'`;
|
|
||||||
- add `refreshCookie` and the `Set-Cookie` headers to the auth routes.
|
|
||||||
Verify with the integration test of 3.1.
|
|
||||||
- [x] 2.3 In `src/ingress/ingress-responses.ts`, make `byInputType` a `z.object` with `rtmp`, `whip` and `url`. Verify
|
|
||||||
with the ingress response unit tests and the mapper checks.
|
|
||||||
- [x] 2.4 In `src/http/errors.ts`, make `apiNotFound` name the path of `req.originalUrl`. Verify with the app unit test
|
|
||||||
of the unknown API path, which asserts the message.
|
|
||||||
|
|
||||||
## 3. Tests and docs
|
|
||||||
|
|
||||||
- [x] 3.1 Add the integration test `test/integration/openapi-document.test.ts`. It boots the CE dashboard in
|
|
||||||
development and reads `/api/v1/docs/openapi.json`, then asserts:
|
|
||||||
- unique `operationId`s;
|
|
||||||
- every tag is declared with a description;
|
|
||||||
- every parameter has a description;
|
|
||||||
- every response ≥ 400 has `ApiError`;
|
|
||||||
- no `400` on operations without input;
|
|
||||||
- no `403` `PRO_FEATURE` outside the analytics routes;
|
|
||||||
- `If-None-Match` wherever there is a `304`;
|
|
||||||
- the refresh cookie scheme on refresh and logout;
|
|
||||||
- `servers` and `license`.
|
|
||||||
Verify that `./dashboard.sh test-integration` passes.
|
|
||||||
- [x] 3.2 Lint both a development document and the generated one with Redocly CLI (`recommended`, scratch install,
|
|
||||||
not a dependency). Expect no errors, and no warnings other than intentional ones recorded in this file. Verify by
|
|
||||||
running it by hand once.
|
|
||||||
Result: valid. The only warnings are `operation-4xx-response` on `getHealth`, `getReadiness` and `logout`, which
|
|
||||||
answer no 4xx by design.
|
|
||||||
- [x] 3.3 Update `dashboard-ce/backend/AGENTS.md` (route declaration: `operationId`, tags through `registry.tag`,
|
|
||||||
parameter descriptions, response `edition`, `refreshCookie`), and add the `CHANGELOG.md` line of the proposal.
|
|
||||||
Verify that `./dashboard.sh check` passes.
|
|
||||||
|
|
||||||
## Workflow follow-up
|
|
||||||
|
|
||||||
- Move `ce.ref` in openvidu-pro and document the PRO routes there (PRO change).
|
|
||||||
- Archive when the owner asks.
|
|
||||||
|
|
@ -25,7 +25,8 @@ sub-resources at most one level under the parent identifier, and answer every un
|
||||||
|
|
||||||
- **WHEN** the client requests `GET /api/v1/does-not-exist` or `GET /api/anything`
|
- **WHEN** the client requests `GET /api/v1/does-not-exist` or `GET /api/anything`
|
||||||
- **THEN** the response is `404` with body `{ error: { code: 'NOT_FOUND', message } }` and
|
- **THEN** the response is `404` with body `{ error: { code: 'NOT_FOUND', message } }` and
|
||||||
`Content-Type: application/json`, never the SPA `index.html`
|
`Content-Type: application/json`, never the SPA `index.html`, and the message names the full path, such as
|
||||||
|
`No API route for GET /api/v1/does-not-exist`
|
||||||
|
|
||||||
### Requirement: JSON field conventions
|
### Requirement: JSON field conventions
|
||||||
|
|
||||||
|
|
@ -583,3 +584,89 @@ omit `stale` otherwise, also when it has no `lastStatsAt`.
|
||||||
- **GIVEN** a closed room whose `lastStatsAt` is 2 hours old
|
- **GIVEN** a closed room whose `lastStatsAt` is 2 hours old
|
||||||
- **WHEN** it is returned
|
- **WHEN** it is returned
|
||||||
- **THEN** it has no `stale` property
|
- **THEN** it has no `stale` property
|
||||||
|
|
||||||
|
### Requirement: OpenAPI document metadata
|
||||||
|
|
||||||
|
Edition: CE. The OpenAPI document SHALL carry the following:
|
||||||
|
- an `info.description` of the API conventions;
|
||||||
|
- `info.license`: Apache 2.0 unless the edition provides another;
|
||||||
|
- one `servers` entry with the dashboard's public base path;
|
||||||
|
- a `tags` list with a description per tag, holding only the tags of the listed operations.
|
||||||
|
|
||||||
|
#### Scenario: Behind a base path
|
||||||
|
|
||||||
|
- **GIVEN** the server runs with `DASHBOARD_BASE_PATH=/dashboard/` and `NODE_ENV=development`
|
||||||
|
- **WHEN** the client reads the document
|
||||||
|
- **THEN** `servers[0].url` is `/dashboard`, and the paths start with `/api/v1`
|
||||||
|
|
||||||
|
#### Scenario: Conventions described
|
||||||
|
|
||||||
|
- **WHEN** the client reads `info.description`
|
||||||
|
- **THEN** it explains the following:
|
||||||
|
- the bearer access token and the refresh cookie;
|
||||||
|
- the `from`, `to` and `tz` time expressions;
|
||||||
|
- cursor pagination with `limit` and `total`;
|
||||||
|
- the `ApiError` envelope;
|
||||||
|
- `ETag` and `If-None-Match`;
|
||||||
|
- the `X-Request-Id` header;
|
||||||
|
- the versioning policy.
|
||||||
|
|
||||||
|
#### Scenario: Tags
|
||||||
|
|
||||||
|
- **WHEN** the client reads `tags`
|
||||||
|
- **THEN** every operation's tags are listed there with a description, and no listed tag lacks an operation
|
||||||
|
|
||||||
|
### Requirement: OpenAPI operations
|
||||||
|
|
||||||
|
Edition: CE. Every operation of the OpenAPI document SHALL have the following:
|
||||||
|
- a unique `operationId`, a summary and tags;
|
||||||
|
- a description for every parameter, with comma-separated list parameters as arrays of their allowed values
|
||||||
|
(`style: form`, `explode: false`);
|
||||||
|
- an `If-None-Match` header parameter when it can answer `304`;
|
||||||
|
- on the auth routes, the refresh cookie as a cookie security scheme, and the `Set-Cookie` header of the responses that
|
||||||
|
set or clear it.
|
||||||
|
|
||||||
|
#### Scenario: Operation identifiers
|
||||||
|
|
||||||
|
- **WHEN** the client reads every operation of the document
|
||||||
|
- **THEN** each has an `operationId` that no other operation shares, such as `listRooms` for `GET /api/v1/rooms`
|
||||||
|
|
||||||
|
#### Scenario: List parameter
|
||||||
|
|
||||||
|
- **WHEN** the client reads the `kind` parameter of `GET /api/v1/participants`
|
||||||
|
- **THEN** it has a description, `style: form`, `explode: false`, and an array schema whose items enumerate the
|
||||||
|
participant kinds
|
||||||
|
|
||||||
|
#### Scenario: Conditional request
|
||||||
|
|
||||||
|
- **WHEN** the client reads `GET /api/v1/overview`
|
||||||
|
- **THEN** it declares the `If-None-Match` header parameter, the `ETag` header of its `200`, and its `304`
|
||||||
|
|
||||||
|
#### Scenario: Refresh cookie
|
||||||
|
|
||||||
|
- **WHEN** the client reads `POST /api/v1/auth/refresh`
|
||||||
|
- **THEN** its security names the refresh cookie scheme (`in: cookie`, `name: ov_dashboard_refresh`), and its `200` and
|
||||||
|
`401` declare `Set-Cookie`
|
||||||
|
|
||||||
|
### Requirement: OpenAPI error responses
|
||||||
|
|
||||||
|
Edition: CE. Every error response an operation can answer SHALL be declared with the `ApiError` schema. This means
|
||||||
|
`400` only for operations that take parameters or a body, `401` for operations that need or accept a token, and the
|
||||||
|
route's own errors such as `404`, `429` and `503`. A response that only one edition answers SHALL appear only in that
|
||||||
|
edition's document.
|
||||||
|
|
||||||
|
#### Scenario: Operation without input
|
||||||
|
|
||||||
|
- **WHEN** the client reads `GET /api/v1/health`
|
||||||
|
- **THEN** it declares no `400`
|
||||||
|
|
||||||
|
#### Scenario: Error body
|
||||||
|
|
||||||
|
- **WHEN** the client reads the `429` of `POST /api/v1/auth/login`
|
||||||
|
- **THEN** its content is `application/json` with the `ApiError` schema
|
||||||
|
|
||||||
|
#### Scenario: Edition-specific error
|
||||||
|
|
||||||
|
- **WHEN** the client reads `GET /api/v1/analytics/series` in the CE document
|
||||||
|
- **THEN** it declares the `403` of a PRO metric, dimension or node scope; and no CE operation declares a `403`
|
||||||
|
`PRO_FEATURE` for being a PRO route
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue