172 lines
9.6 KiB
Markdown
172 lines
9.6 KiB
Markdown
|
|
# Reference — Examples
|
||
|
|
|
||
|
|

|
||
|
|
|
||
|
|
> [!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 — add a template JSON, or fix the emitting node's `functionality.softwareType`. |
|
||
|
|
| `machineGroupControl` produces no dashboard | The alias maps to `machineGroup.json` — verify that file exists in `config/`. | `_templateFileForSoftwareType` in `src/specificClass.js`. |
|
||
|
|
| Empty `Authorization` header | `bearerToken` not set in editor form — the header is omitted entirely when the token is empty, not set to `'Bearer '`. | Editor → Bearer Token field. |
|
||
|
|
| Wrong InfluxDB bucket in Grafana template variables | `defaultBucket` config (or `INFLUXDB_BUCKET` env) overrides the position-based default. Priority order: `defaultBucket` → `bucketMap[position]` → `defaultBucketForPosition`. | `_buildConfig` in `nodeClass.js` + `defaultBucketForPosition` in `specificClass.js`. |
|
||
|
|
| Dashboard UID changes between deploys | Node id or `softwareType` changed — UID is `sha1(softwareType:nodeId).slice(0, 12)`. Stable only if both are stable. | `stableUid` in `specificClass.js`. |
|
||
|
|
| `registerChild` alias warns once | Expected — 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–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 → Administration → 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 → Network → `menu.js`; check the entry file's logger menu endpoint. |
|
||
|
|
|
||
|
|
> Never ship `enableLog: 'debug'` in a demo — 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 — that's correct behaviour.
|
||
|
|
|
||
|
|
The downstream `http request` node is **optional** for the smoke test — the dashboardAPI emits regardless of whether anything POSTs the envelope to Grafana.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Related pages
|
||
|
|
|
||
|
|
| Page | Why |
|
||
|
|
|:---|:---|
|
||
|
|
| [Home](Home) | Intuitive overview |
|
||
|
|
| [Reference — Contracts](Reference-Contracts) | Topic + payload resolution + envelope shape |
|
||
|
|
| [Reference — Architecture](Reference-Architecture) | Code map, lifecycle, graph walk |
|
||
|
|
| [Reference — Limitations](Reference-Limitations) | Stub flows, filename drift, open questions |
|
||
|
|
| [EVOLV — Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where dashboardAPI fits in a larger plant |
|