Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
150 lines
8.3 KiB
Markdown
150 lines
8.3 KiB
Markdown
# dashboardAPI
|
|
|
|
  
|
|
|
|
A `dashboardAPI` node converts EVOLV node topology into Grafana dashboards. On each inbound `child.register` event it resolves the child source, walks its direct children, loads per-`softwareType` Grafana JSON templates from `config/`, and emits one HTTP upsert request per dashboard on Port 0 to a downstream `http request` node. Sits adjacent to the S88 hierarchy as a passive HTTP emitter — **no measurements, no tick loop, no parent registration**.
|
|
|
|
> [!NOTE]
|
|
> Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only.
|
|
|
|
---
|
|
|
|
## At a glance
|
|
|
|
| Thing | Value |
|
|
|:---|:---|
|
|
| What it represents | Utility bridge between EVOLV topology and Grafana — auto-generates dashboards from `child.register` events |
|
|
| S88 level | **Utility** — not in the S88 hierarchy; sits adjacent to it |
|
|
| Use it when | You want Grafana dashboards to materialise automatically when an EVOLV node graph is deployed |
|
|
| Don't use it for | Maintaining hand-curated Grafana dashboards (will overwrite); arbitrary Grafana API calls; tick / measurement data plumbing |
|
|
| Children it accepts | Any EVOLV node whose `nodeSource.config` carries `functionality.softwareType` |
|
|
| Parents it talks to | None — dashboardAPI is a passive sink; it does not register with a parent |
|
|
|
|
---
|
|
|
|
## How it fits
|
|
|
|
```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"| http[http request<br/>node-red core]:::neutral
|
|
http --> grafana[(Grafana<br/>HTTP API)]
|
|
grafana -.renders dashboards for.-> ff[FlowFuse / Browser]
|
|
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
|
|
```
|
|
|
|
Dashed arrows = inbound `child.register` events from any EVOLV process node. The solid arrow is the outbound HTTP upsert envelope on Port 0 — emitted **once per generated dashboard** in the walked graph. S88 colours and the utility-neutral `#dddddd` are anchored in `.claude/rules/node-red-flow-layout.md`.
|
|
|
|
---
|
|
|
|
## Try it — 3-minute demo
|
|
|
|
Import the basic example flow, deploy, and watch a `child.register` payload turn into a Grafana dashboard upsert request.
|
|
|
|
```bash
|
|
curl -X POST -H 'Content-Type: application/json' \
|
|
--data @nodes/dashboardAPI/examples/basic.flow.json \
|
|
http://localhost:1880/flow
|
|
```
|
|
|
|
What to click after deploy:
|
|
|
|
1. Open the inject node (`basic trigger`) and edit the payload to a `{source: {config: {...}}}` shape — see [Reference — Examples](Reference-Examples#wiring-pattern) for the minimal inline-payload shape.
|
|
2. Fire the inject. Watch the debug pane: one `topic: 'create'` HTTP envelope appears per dashboard in the walked graph (root + direct children).
|
|
3. Wire a downstream `http request` node (method `POST`) to the dashboardAPI output to actually POST the envelope to Grafana.
|
|
|
|
> [!IMPORTANT]
|
|
> **GIF needed.** Demo recording of the inject → Port-0 envelope → Grafana dashboard upsert path. Save as `wiki/_partial-gifs/dashboardAPI/01-basic-demo.gif`, target ≤ 1 MB after `gifsicle -O3 --lossy=80`.
|
|
|
|
> [!WARNING]
|
|
> The shipped `basic.flow.json` / `integration.flow.json` / `edge.flow.json` are stubs — the inject payloads do not yet conform to the `child.register` resolver's expected shape. They will trigger `Missing or invalid child node` errors until updated. Tracked in [Limitations — Example flow stubs](Reference-Limitations#example-flow-stubs).
|
|
|
|
---
|
|
|
|
## The one thing you'll send
|
|
|
|
| Topic | Aliases | Payload | What it does |
|
|
|:---|:---|:---|:---|
|
|
| `child.register` | `registerChild` | `string` (child node id) **or** `{source: {...}}` **or** `{config: {...}}` (optionally `msg.includeChildren: boolean`, default `true`) | Resolves the child source (`RED.nodes.getNode` → `node._flow.getNode` → inline payload), calls `source.generateDashboardsForGraph(child, {includeChildren})`, then emits one `topic: 'create'` HTTP-upsert message on Port 0 per generated dashboard. |
|
|
|
|
That's it. There is no `set.*`, no `cmd.*`, no `query.*` — the registry has a single canonical topic (alias-with-deprecation). The legacy `registerChild` alias logs a one-time deprecation warning on first use.
|
|
|
|
---
|
|
|
|
## What you'll see come out
|
|
|
|
Sample Port 0 message after a `child.register` for a `pumpingStation` node with two direct children:
|
|
|
|
```json
|
|
{
|
|
"topic": "create",
|
|
"url": "http://grafana:3000/api/dashboards/db",
|
|
"method": "POST",
|
|
"headers": {
|
|
"Accept": "application/json",
|
|
"Content-Type": "application/json",
|
|
"Authorization": "Bearer eyJ..."
|
|
},
|
|
"payload": {
|
|
"dashboard": { "uid": "a1b2c3d4e5f6", "title": "Pumping Station Demo", "templating": {...} },
|
|
"folderId": 0,
|
|
"overwrite": true
|
|
},
|
|
"meta": {
|
|
"nodeId": "ps_demo",
|
|
"softwareType": "pumpingStation",
|
|
"uid": "a1b2c3d4e5f6",
|
|
"title": "Pumping Station Demo"
|
|
}
|
|
}
|
|
```
|
|
|
|
| Field | Meaning |
|
|
|:---|:---|
|
|
| `topic` | Always `'create'` — signals a dashboard-upsert HTTP envelope. |
|
|
| `url` | `grafanaUpsertUrl()` = `<protocol>://<host>:<port>/api/dashboards/db`. |
|
|
| `method` | Always `POST`. |
|
|
| `headers.Authorization` | Present only when `bearerToken` is configured; omitted otherwise. |
|
|
| `payload.dashboard` | The composed Grafana dashboard JSON (template + templating vars filled in). |
|
|
| `payload.dashboard.uid` | `sha1(softwareType:nodeId).slice(0, 12)` — stable across re-deploys. |
|
|
| `meta.*` | Correlation fields for the downstream consumer (nodeId, softwareType, uid, title). |
|
|
|
|
Inbound `msg` fields propagate via spread (`{...msg, ...envelope}`) so any caller-supplied correlation / trace fields survive.
|
|
|
|
> Port 1 (InfluxDB telemetry) and Port 2 (registration / control plumbing) are **unused** — dashboardAPI has no measurements and does not register with a parent. See [Reference — Architecture](Reference-Architecture#output-ports).
|
|
|
|
---
|
|
|
|
## The new bit — no BaseNodeAdapter / BaseDomain
|
|
|
|
Most EVOLV nodes extend `BaseNodeAdapter` + `BaseDomain` from `generalFunctions/`. `dashboardAPI` does **not** — per `OPEN_QUESTIONS.md` (2026-05-10) the decision is to keep a bespoke adapter until `BaseNodeAdapter` grows passive / HTTP-only flags.
|
|
|
|
Reasons:
|
|
|
|
- No `generalFunctions/src/configs/dashboardapi.json` — `BaseDomain`'s constructor unconditionally calls `configManager.getConfig(ctor.name)` and would throw. The local `dependencies/dashboardapi/dashboardapiConfig.json` is for the editor menu endpoint, not the runtime config pipeline.
|
|
- No periodic output — `BaseNodeAdapter._emitOutputs()` / `outputUtils.formatMsg` assumes a delta-compressed Port 0 / 1 stream; dashboardAPI emits HTTP-shaped messages instead.
|
|
- No registration to a parent — `BaseNodeAdapter._scheduleRegistration` would emit a spurious `child.register` of its own.
|
|
- No status badge / tick / measurements / children of its own.
|
|
|
|
dashboardAPI uses the shared `commandRegistry` (canonical-topic naming + alias-with-deprecation) and stops there. See [Reference — Architecture](Reference-Architecture#why-no-basenodeadapter--basedomain) for the full rationale.
|
|
|
|
---
|
|
|
|
## Need more?
|
|
|
|
| Page | What you'll find |
|
|
|:---|:---|
|
|
| [Reference — Contracts](Reference-Contracts) | Full topic contract, config schema, child resolution rules, template alias table |
|
|
| [Reference — Architecture](Reference-Architecture) | Code map, HTTP-endpoint lifecycle, template loader, UID stability, graph walk |
|
|
| [Reference — Examples](Reference-Examples) | Shipped example flows + debug recipes |
|
|
| [Reference — Limitations](Reference-Limitations) | Legacy filename drift, stub flows, missing template handling, open questions |
|
|
|
|
[EVOLV master wiki](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Home) · [Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) · [Topic Conventions](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions)
|