# 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=` warn | `config/.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 |