Files
dashboardAPI/wiki/Reference-Limitations.md

157 lines
11 KiB
Markdown
Raw Normal View History

# Reference — Limitations
![code-ref](https://img.shields.io/badge/code--ref-a6f09d8-blue)
> [!NOTE]
> What `dashboardAPI` does not do, current rough edges, and open questions. Open items live in `.agents/improvements/IMPROVEMENTS_BACKLOG.md` and `.claude/refactor/OPEN_QUESTIONS.md` in the superproject.
>
> Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only.
---
## When you would not use this node
| Scenario | Use instead |
|:---|:---|
| You maintain Grafana dashboards by hand | Skip dashboardAPI — it will overwrite your customisations on every `child.register` (upsert is `overwrite: true`). |
| You need arbitrary Grafana API calls (annotations, alerts, data sources, folders) | A plain `http request` node. dashboardAPI only emits `POST /api/dashboards/db` envelopes. |
| You want to forward tick / measurement data to Grafana | This is not what dashboardAPI does. Wire telemetry through Port 1 of an EVOLV process node directly into InfluxDB; Grafana queries InfluxDB. |
| You want to use dashboardAPI as a BaseDomain-capable child of something else | Not supported — dashboardAPI does not extend `BaseDomain` and cannot register as a child of `machineGroupControl` / `pumpingStation` / similar. See [No BaseNodeAdapter / BaseDomain](#no-basenodeadapter--basedomain) below. |
| You expect EVOLV nodes to auto-discover dashboardAPI | They don't. Port 2 of the emitter must be wired into dashboardAPI's input explicitly. |
---
## Known limitations
### Legacy filename drift
The entry file and editor HTML are currently lowercase — `dashboardapi.js` and `dashboardapi.html` — rather than `dashboardAPI.js` / `dashboardAPI.html` per the canonical folder-name convention in `.claude/rules/node-architecture.md`.
The convention rule explicitly calls this out as legacy drift to fix when the file is next touched. A rename is a four-touch change:
1. `dashboardapi.js` → `dashboardAPI.js`
2. `dashboardapi.html` → `dashboardAPI.html`
3. `package.json#node-red.nodes` — key remains `dashboardapi` (the Node-RED type id is independent of the filename) but the value becomes `dashboardAPI.js`.
4. Superproject submodule references and any `require()` paths.
The Node-RED **type id** (`dashboardapi`, lowercase, registered via `RED.nodes.registerType('dashboardapi', …)`) must stay `dashboardapi` to avoid breaking existing flows in the wild. The rename is purely the source-file path. Tracked.
### Example flow stubs
The three shipped flows (`basic.flow.json`, `integration.flow.json`, `edge.flow.json`) are placeholders. Their inject nodes don't fire a payload that matches the `child.register` resolver:
| File | Current behaviour | What's wrong |
|:---|:---|:---|
| `basic.flow.json` | Inject `topic: 'ping'` | Not `child.register`; registry silently drops. |
| `integration.flow.json` | Inject `topic: 'registerChild'` with `payload: 'example-child-id'` (string) | The string id has no live Node-RED node behind it; `RED.nodes.getNode('example-child-id')` returns null; throws `'Missing or invalid child node'`. |
| `edge.flow.json` | Inject `topic: 'doesNotExist'` | Works as a registry-coverage probe (silent drop is correct) but exercises nothing. |
Working wiring patterns are documented inline in [Reference — Examples](Reference-Examples#working-wiring-patterns). Replacement of the stubs is tracked in `IMPROVEMENTS_BACKLOG.md` (P9 wiki cleanup follow-up).
### No BaseNodeAdapter / BaseDomain
Most EVOLV nodes inherit a common adapter / domain base class. dashboardAPI does not. The decision is recorded in `OPEN_QUESTIONS.md` (2026-05-10) — four blockers (no platform config JSON, no periodic output, no parent registration, no status badge / tick / measurements). Until `BaseNodeAdapter` grows passive-mode flags (skip-registration + skip-output-stream), the bespoke adapter shape is the correct compromise.
Consequence: dashboardAPI cannot be introspected via the standard `getOutput()` channel. Debugging relies on watching Port 0 in a debug node.
### No domain output / no manifest
Per `.claude/rules/output-coverage.md`, every node should ship a `test/_output-manifest.md` enumerating every Port-0/1/2 key in populated and degraded states. dashboardAPI's output surface is **one envelope shape**, emitted only when a dashboard is successfully generated — there is no degraded "partial envelope" state to test. The manifest collapses to:
| Port | Output | Populated state | Degraded state |
|:---|:---|:---|:---|
| 0 | `{topic, url, method, headers, payload, meta}` envelope | Emitted once per generated dashboard | **Not emitted** — on resolution failure the handler throws and nodeClass sets a red status badge instead |
| 1 | (unused) | — | — |
| 2 | (unused) | — | — |
The full output-coverage rule applies prospectively; no backfill manifest exists yet. Tracked.
### Template discovery is filename-based
The template lookup is `softwareType` ↔ filename. Renaming a node's `softwareType` (e.g. `rotatingmachine` → `rotatingMachine`) requires either renaming the template file or adding an alias arm in `_templateFileForSoftwareType`. The `machineGroupControl → machineGroup.json` mapping is a one-off alias because the historical filename was abbreviated.
> [!NOTE]
> Verify in full review: which softwareType does the current `rotatingMachine` emit? The shipped template is `config/machine.json` — if `rotatingMachine`'s `functionality.softwareType` is `'rotatingmachine'` (lowercase), the case-insensitive fallback won't find it and dashboard generation will warn-and-skip. Flagged.
### No retry / circuit-breaker on downstream HTTP
dashboardAPI emits the upsert envelope and is done. If the downstream `http request` node fails (Grafana down, 5xx, network timeout), the dashboard upsert is silently dropped — no retry, no DLQ, no status badge propagation back to dashboardAPI. The caller is responsible for wiring retry logic into the http-request path.
### `oneditsave` doesn't read all editor fields uniformly
`dashboardapi.html` `oneditsave` reads `['name', 'protocol', 'host', 'port', 'bearerToken', 'defaultBucket']` via direct DOM lookups, separately from the logger menu's `saveEditor`. Adding a new editor field requires touching both the form HTML and the `oneditsave` whitelist. Mild; not load-bearing.
### Config default mismatch
The runtime `_buildConfig` defaults `general.logging.enabled` to `Boolean(config?.general?.logging?.enabled)` — effectively `false` when the editor doesn't set it. But `dependencies/dashboardapi/dashboardapiConfig.json` declares the default as `true`. The editor menu (`loggerMenu`) bridges these via the standard EVOLV logger pattern, but the divergence is worth confirming — logger enabled vs disabled changes whether `Skipping dashboard generation: no template …` warns appear at all.
> [!NOTE]
> Confirm in full review which side wins by default for a freshly-dropped node. Flagged.
### `bucket` resolution priority is global-then-per-position
`buildDashboard` reads `this.config.defaultBucket || this.config.bucketMap[position] || defaultBucketForPosition(position)`. The global override fires **before** the per-position map. If you want per-position buckets to win over a global default, the current code doesn't do that — you'd need to leave `defaultBucket` empty and rely solely on `bucketMap` + the position fallback.
Open question whether the "global beats per-position" priority is the intended semantics. Flagged.
### No InfluxDB bucket validation
The bucket name is templated into the Grafana dashboard JSON without any check that the bucket exists in InfluxDB. A typo produces a dashboard that renders panels saying "no data" with no upstream warning. Tracked.
---
## Open questions (tracked)
| Question | Where it lives |
|:---|:---|
| Should `BaseNodeAdapter` grow a passive / HTTP-only mode (skip-registration + skip-output-stream) so dashboardAPI can extend it? | `.claude/refactor/OPEN_QUESTIONS.md` (2026-05-10) — "dashboardAPI skipped BaseNodeAdapter + BaseDomain" |
| Confirm `rotatingMachine` softwareType ↔ `config/machine.json` mapping | Internal — flag during full review |
| Bucket priority: should per-position `bucketMap` beat global `defaultBucket`? | Internal |
| Should dashboardAPI emit a Port-2 status / health pulse so other EVOLV nodes can detect it? | Internal |
| Should `child.register` aliases include older topic names (e.g. `RegisterChild`, `register-child`) for legacy compat? | Internal |
| Add an explicit `child.unregister` / `dashboard.delete` topic to remove orphaned Grafana dashboards | Internal |
| Provide a programmatic way to bulk-regenerate all dashboards for an existing deployment (e.g. `cmd.regenerate-all`) | Internal |
| Retry / DLQ for failed Grafana upserts | TBD |
---
## Migration notes
### From the `registerChild` alias
The canonical topic since 2026-Q1 is `child.register`. The `registerChild` alias still works but logs a one-time deprecation warning on first use. Migrate callers when convenient:
```diff
- msg.topic = 'registerChild';
+ msg.topic = 'child.register';
```
Both topics accept identical payloads.
### From bare-string node-id payloads
The handler resolves bare-string payloads via `RED.nodes.getNode(id) → node._flow.getNode(id) → null`. This works at runtime but is brittle for tests and for flows where the emitter and dashboardAPI live on different `_flow` instances. Prefer the inline `{source: {config: {...}}}` or `{config: {...}}` shapes for tests and for any flow that imports both sides as JSON (no `RED.nodes` registry at compile time).
### From hand-curated Grafana dashboards
If you're moving from hand-curated dashboards to dashboardAPI-generated ones:
1. Export your existing dashboard JSON from Grafana.
2. Replace the templating-var values for `measurement` and `bucket` with placeholders.
3. Save as `nodes/dashboardAPI/config/<softwareType>.json`.
4. The next `child.register` for that softwareType will upsert (overwrite) the existing dashboard, preserving the UID if you set it to match `stableUid(softwareType:nodeId)`.
If you want to **preserve the UID** of an existing hand-curated dashboard, compute `sha1(softwareType:nodeId).slice(0, 12)` and check it matches your existing UID. If not, either rename the node id, or accept that the first upsert will create a new dashboard alongside the old one.
---
## 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, "no BaseNodeAdapter" rationale |
| [Reference &mdash; Examples](Reference-Examples) | Shipped flows + debug recipes + working wiring patterns |
| [EVOLV &mdash; Open Questions](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/.claude/refactor/OPEN_QUESTIONS.md) | Cross-node open questions and decisions log |