Files
dashboardAPI/wiki/Reference-Examples.md
znetsixe a9fc51d6f0 docs(wiki): full 5-page wiki matching the rotatingMachine reference format
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>
2026-05-19 09:42:14 +02:00

9.6 KiB
Raw Permalink Blame History

Reference — Examples

code-ref

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.


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

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)

[
  {
    "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:

"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.

[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:

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.


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. 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: defaultBucketbucketMap[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 711. 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.


Page Why
Home Intuitive overview
Reference — Contracts Topic + payload resolution + envelope shape
Reference — Architecture Code map, lifecycle, graph walk
Reference — Limitations Stub flows, filename drift, open questions
EVOLV — Topology Patterns Where dashboardAPI fits in a larger plant