Skip to content

Server features#

Some pages are switched on or off per environment: the switch is stored in the CommandCenter database and holds for everyone using that CommandCenter. You find the switches under Configuration → Features (/config/features).

These are not the debug flags a developer toggles in the browser console (cc.enableAIChat() and the like). Those live in one browser's local storage and change nothing for anyone else.

What there is#

Feature Default What it switches
exceptions-sql — Exceptions (database) on The Exceptions page, reading the exception log table (TblExceptionLog).
exceptions-log-analytics — Exceptions (Log Analytics) off A second Exceptions page over Azure Log Analytics. Needs LogAnalytics:WorkspaceId on WebApi; without it the switch shows Not configured on this host.

What a switch does#

  • Off: the page leaves the menu and the configuration cards. Someone who opens its address anyway (a bookmark) sees a short notice with a link to the switches, and the page's API endpoints answer 404 with a problem document naming the feature, before they do any work.
  • Not configured: the switch is replaced by a badge, and the endpoints answer 503. Setting the missing configuration makes the switch appear.
  • On: everything as usual.

A switch applies at once, on every server behind the load balancer: each request reads the setting from the database, and the browser refreshes its copy within a minute (or when the tab regains focus).

Two people switching at once. Each switch carries a revision. A switch sent with an older revision than the one stored is refused ("Someone else changed this switch meanwhile"), and the page shows the current setting. Switch again if you still want to.

Who changed it is shown next to each switch. Until the gateway forwards the signed-in user to WebApi it reads unknown.

Before the first switch#

The switches live in the table TblFeature, created by the WebApi migration AddFeatures. Like every WebApi migration it is applied by calling GET /api/maintenance/migrate on the environment; check that the response lists AddFeatures.

Until then nothing breaks: every feature simply has its default, and the page lists them without a "last changed". The same holds when the list cannot be read at all — the browser falls back to the defaults, so the Exceptions page never disappears because of a failed request.

How it fits together#

flowchart LR
    operator([Operator]) -->|/config/features| spa[SPA]
    user([User]) -->|menu, pages| spa
    spa -->|GET/PUT /api/features| bff[BFF]
    spa -->|page API calls| bff
    bff -->|/v2/api/...| webapi[WebApi]
    webapi -->|TblFeature| db[(CommandCenter database)]
    webapi -. catalog: names, defaults,<br/>configured? .- code[FeatureCatalog]
Hold "Alt" / "Option" to enable pan & zoom
sequenceDiagram
    autonumber
    participant Op as Operator (tab A)
    participant Other as Operator (tab B)
    participant API as WebApi (any replica)
    participant DB as TblFeature
    Op->>API: GET /api/features
    API->>DB: read rows
    API-->>Op: catalog merged with rows (revision 3)
    Other->>API: PUT /api/features/exceptions-sql {enabled:false, revision:3}
    API->>DB: UPDATE … WHERE Name AND Revision = 3
    API-->>Other: 200 (revision 4)
    Op->>API: PUT /api/features/exceptions-sql {enabled:true, revision:3}
    API->>DB: UPDATE … WHERE Revision = 3 → 0 rows
    API-->>Op: 409 "changed by someone else"
    Note over API: a page endpoint with RequireFeature<br/>reads the row per request: 404 when off
Hold "Alt" / "Option" to enable pan & zoom
journey
    title Switching the Log Analytics page on for an environment
    section Prepare
      Set LogAnalytics:WorkspaceId on WebApi: 3: Operator
      Grant WebApi Log Analytics Reader: 3: Operator
    section Switch
      Open Configuration, Features: 5: Operator
      Switch "Exceptions (Log Analytics)" on: 5: Operator
    section Use
      The page appears in the menu: 5: User
Hold "Alt" / "Option" to enable pan & zoom

For developers: adding a feature#

  1. A constant in FeatureNames and an entry in FeatureCatalog.All (backend/CommandCenter.WebApi/Features/FeatureCatalog.cs): name, default, English description, and, when it depends on configuration, an IsConfigured probe.
  2. Guard its endpoints with .RequireFeature(FeatureNames.X).
  3. In the SPA: add the name to ServerFeatureName and SERVER_FEATURE_DEFAULTS (src/services/web-api/features.ts), labels in pages/config/features/FeaturesPage.tsx (LABELS) and in all five translation files, and feature: 'x' plus a ServerFeatureGate around the element of its route.
  4. A seed row is optional: a missing row means the default. If you add one, use idempotent SQL in a migration, never HasData, so a later edit never overwrites what an operator switched.