Files
dashboardAPI/wiki/Reference-Examples.md

172 lines
9.6 KiB
Markdown
Raw Permalink Normal View History

# Reference — Examples
![code-ref](https://img.shields.io/badge/code--ref-a6f09d8-blue)
> [!NOTE]
> Every example flow shipped under `nodes/dashboardAPI/examples/`, plus how to load them, what they show, and the debug recipes that go with them. Live source: `nodes/dashboardAPI/examples/`.
>
> Pending full node review (2026-05). The shipped example flows are **stubs** — they wire up the node but the inject payloads do not yet match the `child.register` resolver's expected shape. Working wiring patterns are documented inline below.
---
## Shipped examples
| File | Tier | Dependencies | What it shows | Status |
|:---|:---:|:---|:---|:---|
| `basic.flow.json` | 1 | EVOLV only | Inject a `ping` topic into a stand-alone dashboardAPI node + debug tap on Port 0. | ⏳ **Stub** — inject topic is `ping`, not `child.register`; the registry will silently drop the msg. |
| `integration.flow.json` | 2 | EVOLV only | Inject a `registerChild` alias topic with a bare-string node id (`'example-child-id'`) + debug tap. | ⏳ **Stub** — the bare-string id resolves to `null` via `RED.nodes.getNode`; throws `'Missing or invalid child node'`. |
| `edge.flow.json` | 3 | EVOLV only | Inject an unknown topic to confirm the dispatcher silently drops it. | ✓ Works as a registry-coverage probe. |
All three are tracked for replacement in the next wiki-cleanup pass — see [Limitations — Example flow stubs](Reference-Limitations#example-flow-stubs).
---
## Loading a flow
### Via the editor
1. Open the Node-RED editor at `http://localhost:1880`.
2. Menu → Import → drag the JSON file.
3. Click Deploy.
### Via the Admin API
```bash
curl -X POST -H 'Content-Type: application/json' \
--data @nodes/dashboardAPI/examples/basic.flow.json \
http://localhost:1880/flows
```
---
## Working wiring patterns
These are the shapes that actually exercise the resolver. Use them as the basis for any new example flow until the stubs above are replaced.
### Wiring pattern A — inline `source` payload (no real EVOLV node needed)
```json
[
{
"type": "inject",
"topic": "child.register",
"props": [
{"p": "topic", "vt": "str"},
{"p": "payload", "v": "{\"source\":{\"config\":{\"functionality\":{\"softwareType\":\"measurement\"},\"general\":{\"id\":\"pump-a-flow\",\"name\":\"Pump A flow\"}}}}", "vt": "json"}
]
},
{ "type": "dashboardapi" },
{ "type": "http request", "method": "POST" },
{ "type": "debug", "complete": "true" }
]
```
What happens:
1. The inject fires a msg with `topic: 'child.register'` and `payload.source.config.functionality.softwareType = 'measurement'`.
2. `resolveChildSource` matches the `payload.source.config` branch and returns `payload.source` directly.
3. `loadTemplate('measurement')` reads `config/measurement.json`.
4. `stableUid('measurement:pump-a-flow')` → deterministic 12-char hex.
5. The Port-0 envelope flows to the debug node AND to the `http request` node which POSTs to Grafana.
### Wiring pattern B — bare `config` payload
Same as pattern A but with the outer `source` wrapper dropped:
```json
"payload": "{\"config\":{\"functionality\":{\"softwareType\":\"pumpingStation\"},\"general\":{\"id\":\"ps_demo\",\"name\":\"Pumping Station Demo\"}}}"
```
`resolveChildSource` falls through to the `payload.config` branch and wraps as `{config: payload.config}`. No `childRegistrationUtils` is present, so the graph walk emits only the root dashboard (no children even if `includeChildren=true`).
### Wiring pattern C — real EVOLV node via Port 2
The canonical production wiring: any EVOLV node's Port 2 (`registerChild` emission) wired into dashboardAPI's input.
```text
[rotatingMachine] Port 2 ──► [dashboardAPI] Port 0 ──► [http request] ──► Grafana
└─► [debug]
```
The emitting node's `child.register` payload is the bare node id (a string). `resolveChildNode` then runs `RED.nodes.getNode(id)` to fetch the live runtime node and reads `node.source.config`. Walks `node.source.childRegistrationUtils.registeredChildren` so direct children also get dashboards.
> [!IMPORTANT]
> **Example needed.** A Tier-2 example that wires a real `rotatingMachine` or `pumpingStation` Port 2 to dashboardAPI input is the missing canonical demo. Save as `nodes/dashboardAPI/examples/02-Integration-with-EVOLV-node.json`. Track in `IMPROVEMENTS_BACKLOG.md`.
---
## Docker compose snippet
To bring up Node-RED + Grafana (+ optional InfluxDB) for end-to-end testing:
```yaml
services:
nodered:
build: ./docker/nodered
ports: ['1880:1880']
volumes:
- ./docker/nodered/data:/data/evolv
environment:
INFLUXDB_BUCKET: lvl2
grafana:
image: grafana/grafana:11.0.0
ports: ['3000:3000']
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
influxdb:
image: influxdb:2.7
ports: ['8086:8086']
```
A Grafana service account token (created via Grafana UI → Administration → Service accounts) goes into the dashboardAPI's Bearer Token editor field.
Full file: [EVOLV/docker-compose.yml](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/docker-compose.yml).
---
## Debug recipes
| Symptom | First thing to check | Where to look |
|:---|:---|:---|
| No HTTP message emitted on Port 0; node shows red `dashboardapi error` status | `resolveChildSource` returned `null`. Check payload shape against [Payload resolution rules](Reference-Contracts#payload-resolution-rules). The most common cause: bare-string id that doesn't match a live Node-RED node. | `src/commands/handlers.js` `resolveChildSource` + `resolveChildNode`. |
| Dispatch silently drops msg (no error, no output) | Topic is not `child.register` and not the `registerChild` alias. The registry's catch-all is "no match → ignore". | `src/commands/index.js` + `createRegistry` source in `generalFunctions/`. |
| `Skipping dashboard generation: no template for softwareType=<st>` warn | `config/<softwareType>.json` (or its lowercase variant or alias) doesn't exist. | `config/` directory &mdash; add a template JSON, or fix the emitting node's `functionality.softwareType`. |
| `machineGroupControl` produces no dashboard | The alias maps to `machineGroup.json` &mdash; verify that file exists in `config/`. | `_templateFileForSoftwareType` in `src/specificClass.js`. |
| Empty `Authorization` header | `bearerToken` not set in editor form &mdash; the header is omitted entirely when the token is empty, not set to `'Bearer '`. | Editor &rarr; Bearer Token field. |
| Wrong InfluxDB bucket in Grafana template variables | `defaultBucket` config (or `INFLUXDB_BUCKET` env) overrides the position-based default. Priority order: `defaultBucket` &rarr; `bucketMap[position]` &rarr; `defaultBucketForPosition`. | `_buildConfig` in `nodeClass.js` + `defaultBucketForPosition` in `specificClass.js`. |
| Dashboard UID changes between deploys | Node id or `softwareType` changed &mdash; UID is `sha1(softwareType:nodeId).slice(0, 12)`. Stable only if both are stable. | `stableUid` in `specificClass.js`. |
| `registerChild` alias warns once | Expected &mdash; deprecation warning on first use only. Migrate caller to `child.register`. | Caller `msg.topic`. |
| Grafana 404 on `POST /api/dashboards/db` | Wrong path = check Grafana version. The `/api/dashboards/db` endpoint exists in Grafana 7&ndash;11. For newer Grafana with org-scoped endpoints, the upsert URL may differ. | `grafanaUpsertUrl` in `specificClass.js`. |
| Grafana 401 / 403 | Bearer token missing, expired, or insufficient permissions. The service account needs at least `Editor` role on the target folder. | Grafana UI &rarr; Administration &rarr; Service accounts. |
| Root dashboard has no `links[]` to children | `includeChildren=false` was passed, OR the root source's `childRegistrationUtils.registeredChildren` is empty / absent. | `generateDashboardsForGraph` + `extractChildren`. |
| Editor form shows blank fields after re-open | `oneditprepare` waits for `window.EVOLV.nodes.dashboardapi.loggerMenu` which is loaded by `/dashboardapi/menu.js`. If the menu endpoint 500s, the editor stays blank. | Browser devtools &rarr; Network &rarr; `menu.js`; check the entry file's logger menu endpoint. |
> Never ship `enableLog: 'debug'` in a demo &mdash; fills the container log within seconds and obscures real errors. Use only for live debugging sessions.
---
## Quick smoke test (no Grafana required)
To verify the node loads and the registry dispatches correctly without standing up Grafana:
1. Import `examples/basic.flow.json` (or any of the stubs).
2. Edit the inject node: set topic to `child.register` and payload to a JSON object matching wiring pattern A above.
3. Deploy.
4. Fire the inject. The debug pane should show a `topic: 'create'` envelope with a populated `payload.dashboard`.
5. If `headers.Authorization` is absent, the editor's Bearer Token field is empty &mdash; that's correct behaviour.
The downstream `http request` node is **optional** for the smoke test &mdash; the dashboardAPI emits regardless of whether anything POSTs the envelope to Grafana.
---
## Related pages
| Page | Why |
|:---|:---|
| [Home](Home) | Intuitive overview |
| [Reference &mdash; Contracts](Reference-Contracts) | Topic + payload resolution + envelope shape |
| [Reference &mdash; Architecture](Reference-Architecture) | Code map, lifecycle, graph walk |
| [Reference &mdash; Limitations](Reference-Limitations) | Stub flows, filename drift, open questions |
| [EVOLV &mdash; Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where dashboardAPI fits in a larger plant |