Files
dashboardAPI/wiki/Home.md
znetsixe 67a374ff4f P9.3: wiki/Home.md following 14-section visual-first template + wiki:* scripts
Auto-generated topic-contract + data-model sections via shared wikiGen
script. Hand-written Mermaid diagrams for position-in-platform, code
map, child registration, lifecycle, configuration, state chart (where
applicable).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:17:46 +02:00

227 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dashboardAPI
> **Reflects code as of `92d7eba` · regenerated `2026-05-11` via `npm run wiki:all`**
> If this banner is stale, the page may be out of date. Treat as informative, not authoritative.
## 1. What this node is
**dashboardAPI** is an HTTP-emitter utility node — it listens for `child.register` events from other EVOLV nodes and emits one Grafana dashboard upsert HTTP request per node on Port 0. It is not a domain in the `BaseDomain` sense: no measurements, no tick loop, no children of its own, no parent registration.
## 2. Position in the platform
```mermaid
flowchart LR
ps[pumpingStation<br/>Process Cell]:::pc -.child.register.-> dash
mgc[machineGroupControl<br/>Unit]:::unit -.child.register.-> dash
rm[rotatingMachine<br/>Equipment]:::equip -.child.register.-> dash
meas[measurement<br/>Control Module]:::ctrl -.child.register.-> dash
dash[dashboardAPI<br/>Utility]:::neutral -->|POST /api/dashboards/db| grafana[(Grafana<br/>HTTP API)]
classDef pc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
classDef neutral fill:#dddddd,color:#000
```
dashboardAPI has **no S88 level** — it's a utility node. Dashed arrows = inbound `child.register` events; the solid arrow is the outbound HTTP upsert on Port 0 to a downstream `http request` node.
## 3. Capability matrix
| Capability | Status | Notes |
|---|---|---|
| Accept `child.register` from any EVOLV node | ✅ | Resolves via `RED.nodes.getNode``node._flow.getNode` → inline payload. |
| Emit Grafana dashboard upsert (Port 0) | ✅ | One msg per generated dashboard, shaped for `http request`. |
| Walk child graph + emit per-child dashboards | ✅ | `includeChildren: true` by default; opt-out per call. |
| Add root → child dashboard `links[]` | ✅ | Each direct child appears as a link on the root dashboard. |
| Template selection by `softwareType` | ✅ | Reads from `config/<softwareType>.json`; case-insensitive fallback. |
| Bearer-token auth | ✅ | Set via editor or `INFLUXDB_BUCKET` env (bucket only). |
| Domain output on Port 0 | ❌ | Never. Port 0 carries HTTP request envelopes, not measurements. |
| Port 1 telemetry / Port 2 registration | ❌ | Both unused — see Section 8. |
| Status badge / tick loop / FSM | ❌ | Stateless; no periodic emission. |
## 4. Code map
```mermaid
flowchart TB
subgraph nodeRED["nodeClass.js — passive adapter"]
nc["createRegistry(commands)<br/>dispatch on msg input<br/>NO BaseNodeAdapter"]
end
subgraph domain["specificClass.js — DashboardApi service"]
sc["buildDashboard()<br/>generateDashboardsForGraph()<br/>extractChildren()"]
end
subgraph cmd["src/commands/"]
h["child.register handler<br/>+ registerChild alias"]
end
subgraph tpl["config/ (templates)"]
t["<softwareType>.json<br/>Grafana JSON skeletons"]
end
nc --> sc
nc --> cmd
sc --> tpl
```
| Module | Owns | Read first if you're changing… |
|---|---|---|
| `src/nodeClass.js` | Input wiring, command dispatch, config build | Topic dispatching, configuration mapping. |
| `src/specificClass.js` | Template loading, dashboard composition, child graph walk | UID stability, links, templating-var substitution. |
| `src/commands/` | `child.register` handler + legacy alias | Adding / renaming inbound topics. |
| `config/` | Per-softwareType Grafana templates | Adding support for new node types. |
dashboardAPI deliberately does NOT split into `concerns/` — its surface is too narrow. See CONTRACT.md → "Why no BaseNodeAdapter / BaseDomain" for the rationale.
## 5. Topic contract
> **Auto-generated** from `src/commands/index.js`. Do NOT hand-edit between the markers. Re-run `npm run wiki:contract`.
<!-- BEGIN AUTOGEN: topic-contract -->
| Canonical topic | Aliases | Payload | Effect |
|---|---|---|---|
| `child.register` | `registerChild` | `any` | Parent/child plumbing — registers or unregisters a child node. |
<!-- END AUTOGEN: topic-contract -->
The legacy `registerChild` alias logs a one-time deprecation warning on first use. The payload can be a string (child id), `{ source: {...} }`, or `{ config: {...} }`; `msg.includeChildren` (default `true`) controls graph-walk depth.
## 6. Child registration
dashboardAPI does **not** maintain a child registry of its own. Instead, every inbound `child.register` triggers a one-shot resolution + dashboard emission. No state is held between calls.
```mermaid
flowchart LR
src["any EVOLV node<br/>(has functionality.softwareType)"]:::other -->|child.register| dash[dashboardAPI<br/>Utility]:::neutral
dash --> resolve[resolve child source<br/>RED.nodes.getNode → _flow → inline]
resolve --> walk["source.generateDashboardsForGraph(child)<br/>(includes children if flag set)"]
walk --> emit[emit one msg per dashboard<br/>topic='create']
emit --> http[(downstream<br/>http request node)]
classDef neutral fill:#dddddd,color:#000
classDef other fill:#ffffff,stroke:#666
```
| Inbound softwareType | Filter | Side effect |
|---|---|---|
| any | child has `functionality.softwareType` | Loads `config/<softwareType>.json`; emits one upsert msg per dashboard in the graph walk. |
| (template missing) | no matching `config/*.json` | Warns and skips that dashboard. No error. |
## 7. Lifecycle — what one event does
```mermaid
sequenceDiagram
participant emitter as any EVOLV node
participant dash as dashboardAPI
participant api as DashboardApi
participant out as Port-0 output
participant grafana as Grafana HTTP API
emitter->>dash: child.register {source / config / id}
dash->>dash: commandRegistry dispatch
dash->>api: generateDashboardsForGraph(child, {includeChildren})
api->>api: loadTemplate(softwareType)
api->>api: stableUid + updateTemplatingVar
api->>api: walk children via childRegistrationUtils
api-->>dash: [{dashboard, uid, title}, ...]
loop per dashboard
dash->>out: msg{topic:'create', url, method, headers, payload}
out->>grafana: POST /api/dashboards/db
end
```
One inbound event yields N outbound HTTP messages (N = 1 + direct child count when `includeChildren=true`).
## 8. Data model — `getOutput()`
> **dashboardAPI has no domain output.** It does not extend `BaseDomain` and does not implement `getOutput()`. The `wiki:datamodel` script falls back to the placeholder below.
<!-- BEGIN AUTOGEN: data-model -->
No domain output. dashboardAPI emits **HTTP request envelopes on Port 0**, shaped for a downstream `http request` node:
```js
{
topic: 'create',
url: 'http://<grafana>:<port>/api/dashboards/db',
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer …' // only when bearerToken is set
},
payload: { dashboard: {}, folderId: 0, overwrite: true },
meta: { nodeId, softwareType, uid, title }
}
```
Port 1 (InfluxDB telemetry) and Port 2 (registration / control plumbing) are unused — dashboardAPI has no measurements and does not register with a parent.
<!-- END AUTOGEN: data-model -->
See CONTRACT.md for the full envelope spec.
## 9. Configuration — editor form ↔ config keys
```mermaid
flowchart TB
subgraph editor["Node-RED editor form"]
f1[Protocol]
f2[Host]
f3[Port]
f4[Bearer token]
f5[Default bucket]
end
subgraph config["Domain config slice"]
c1[grafanaConnector.protocol]
c2[grafanaConnector.host]
c3[grafanaConnector.port]
c4[grafanaConnector.bearerToken]
c5[defaultBucket]
end
f1 --> c1
f2 --> c2
f3 --> c3
f4 --> c4
f5 --> c5
```
| Form field | Config key | Default | Range | Where used |
|---|---|---|---|---|
| Protocol | `grafanaConnector.protocol` | `http` | `http`\|`https` | `grafanaUpsertUrl()` |
| Host | `grafanaConnector.host` | `localhost` | hostname | `grafanaUpsertUrl()` |
| Port | `grafanaConnector.port` | `3000` | 165535 | `grafanaUpsertUrl()` |
| Bearer token | `grafanaConnector.bearerToken` | `''` | string | `Authorization` header |
| Default bucket | `defaultBucket` | `''` / `INFLUXDB_BUCKET` env | string | `updateTemplatingVar(bucket)` |
## 11. Examples
| Tier | File | What it shows | Status |
|---|---|---|---|
| Basic | `examples/01-Basic.flow.json` | Inject `child.register` payload + a downstream `http request` → mock Grafana | ⏳ TBD |
| Integration | `examples/02-Integration.flow.json` | Real EVOLV node (e.g. pumpingStation) wired into dashboardAPI | ⏳ TBD |
| Dashboard | _n/a_ | dashboardAPI **is** the dashboard plumbing — no FlowFuse tier | — |
## 12. Debug recipes
| Symptom | First thing to check | Where to look |
|---|---|---|
| No HTTP request emitted | Did the `child.register` resolve a source? `source.generateDashboardsForGraph` returns `[]` when child has no `config`. | `node_redlog` for "generateDashboardsForGraph skipped" warning. |
| `Skipping dashboard generation: no template` | `config/<softwareType>.json` missing for this node type. | `config/` directory; add a template. |
| Empty `Authorization` header | `bearerToken` not set in editor. | Editor form → Bearer token field. |
| Wrong bucket in Grafana | `defaultBucket` overrides position-based default. Check `INFLUXDB_BUCKET` env. | `_buildConfig` in nodeClass.js. |
| `registerChild` alias warns once | Expected — migrate callers to `child.register`. | Caller's `msg.topic`. |
> Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors.
## 13. When you would NOT use this node
- Use dashboardAPI when you want **auto-generated Grafana dashboards** from EVOLV node topology. If you maintain dashboards by hand in Grafana, skip it.
- Don't use dashboardAPI as a generic HTTP client — it only emits Grafana dashboard upserts. For arbitrary HTTP, use a plain `http request` node.
- Don't put dashboardAPI in a hot data path. It fires on registration events, not on each tick — wiring tick data to it is a misuse.
## 14. Known limitations / current issues
| # | Issue | Tracked in |
|---|---|---|
| 1 | No domain output — cannot be introspected through the standard `getOutput()` channel; debugging relies on Port 0 HTTP envelopes. | CONTRACT.md → "Why no BaseNodeAdapter / BaseDomain" |
| 2 | Template discovery is filename-based; renaming a node's `softwareType` requires renaming the template file (with the `machineGroupControl → machineGroup.json` alias being a one-off). | `_templateFileForSoftwareType` in specificClass.js |
| 3 | No retry / circuit-breaker on the downstream `http request` — Grafana outages drop dashboards silently. | TBD |
| 4 | Tier 1/2 example flows not yet written. | P9 wiki cleanup follow-up |