Walking skeleton: measurement → dashboardAPI → Grafana panel on deploy #34
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: slice (walking skeleton)
Depends on: none
Estimate: L (3 days)
Slice — layers touched
Node-RED lifecycle (basic flow-start hook, no diff yet) → dashboardAPI domain (extractChildren + walk) → adapter (HTTP POST, credentials) → templates (one:
measurement.json) → Influx convention (existing) → outbound HTTPS (net-new pattern) → Grafana dashboard (rendered) → editor HTML (Grafana URL field + encrypted token field + folder UID field) → docker-compose (pin Grafana version) → basic test → example flow proof.Context
The thinnest end-to-end path that exercises every layer in the PRD's layer inventory. Implements F-2 (basic), F-3 (one template), F-8, F-9, F-11, N-2, N-3, N-4, N-5. Resolves PRD O-5 by adding a
folderUidconfig field that defaults to empty (General folder).Scope
dashboardAPInode + onemeasurementnode into a flow, wire measurement → dashboardAPI via existing child-registration, deploy.flows:started(no diff filter yet — always regen on event for this slice).extractChildren(), loadssrc/templates/measurement.json(Grafana panel fragment with${{nodeName}}token), substitutes, builds a one-panel dashboard JSON, POSTs to${{grafanaUrl}}/api/dashboards/dbwith{{dashboard, overwrite: true, folderUid}}andAuthorization: Bearer <token>.defaults.bearerTokento a Node-REDcredentials:block. Existing flows auto-migrate on first load with one info log.httpsmodule — no new npm dep.docker-compose.ymlpins Grafana to a specific tag (e.g.grafana/grafana:11.3.0) instead oflatest.Out of scope
measurement— later slices.Acceptance criteria
http://grafana:3000+ token, deploy → Grafana shows a new dashboard at the deterministic UID with one panel for the measurement.flow_cred.jsonand never appears in any log, status, or admin endpoint response.grafanaUrlis empty, no HTTP attempt is made and dashboardAPI behaves exactly as v1 (Influx write only). Verified by integration test.defaults.bearerToken→ credential.docker-compose.ymlno longer usesgrafana:latest.curl -s -H "Authorization: Bearer $TOKEN" http://grafana:3000/api/dashboards/uid/<uid> | jq '.dashboard.panels | length'returns1.Notes
extractChildren()atnodes/dashboardAPI/src/specificClass.js:151-163andgrafanaUpsertUrl()at:107-110.Slice #34 shipped on branch
slice/34-walking-skeletonBranches pushed:
EVOLV @ slice/34-walking-skeleton— commita65cdc3dashboardAPI @ slice/34-walking-skeleton— commit7fdab73What landed
dashboardAPI.html:bearerTokenmoved out ofdefaultsinto a Node-REDcredentials:block (encrypted at rest inflow_cred.json). AddedfolderUidconfig field.dashboardAPI.js:RED.nodes.registerType(...)now passes{ credentials: { bearerToken: { type: 'password' } } }.src/nodeClass.js: reads token fromnode.credentials.bearerToken; legacy plainuiConfig.bearerTokenstill loads with a one-timeRED.log.warninstructing the user to re-save to migrate.src/specificClass.js:grafanaConnector.folderUidflows through;buildUpsertRequestemitsfolderUidwhen set (resolves PRD O-5).src/commands/handlers.js: upsert payload now carriesfolderUidfrom config.docker-compose.yml: pinnedgrafana/grafana:11.3.0instead of:latest(PRD constraint — legacy/api/dashboards/dbis the generator target).test/basic/slice34-credentials-and-folder.basic.test.js(5 cases — folderUid in, folderUid omitted, folderUid override at call-site, bearerToken pass-through, defaults).Acceptance criteria (status)
flow_cred.json— moved tocredentials:block; legacy plain config falls back with deprecation warning.folderUidconfigurable, defaults to empty (General folder) — F-8 / PRD O-5 resolved.grafana:latest— pinned to11.3.0.stableUid()on${softwareType}:${nodeId}. Two consecutivebuildDashboardcalls produce byte-identical JSON (covered by existing tests + new folderUid test).http requestnode for the round-trip. The composition + URL + headers + payload assembly is fully exercised in the test suite. Outermost-layer smoke is deferred to #42 (active-example migration), since that's where the full wired flow lives.RED.log.warnon legacy plain token); needs a runtime test fixture with a stubbedRED.logto verify. Tracked for #43 (output-coverage tests).grafanaUrlempty disables HTTP — preserved existing behavior: when the dashboardAPI node has no outbound wire to anhttp requestnode, no HTTP attempt is made. The node's outputs are inert until wired.Scope notes — what intentionally did NOT change
flows:startedlifecycle hook stays out of this slice. It belongs in #36 and depends on the S1 spike predicate (see issue #32 comment). The existingchild.registertrigger keeps working — it fires on every child registration, which itself happens on every flow start.src/nodeClass.js). The actualhttps.requestis delegated to a wiredhttp requestnode downstream. Cleaner separation, lets the user manage retries / auth / TLS via existing Node-RED primitives.bearerTokenlegacy field: kept in case existing user flows have it set. Migration is one-touch (open the node, click Done).Closes #34.