feat(dashboardAPI): resolve Grafana folder by name (fixes stale folderUid 400s)

A pinned folderUid goes stale whenever Grafana is rebuilt — the same-named
folder returns with a fresh uid and every dashboard upsert then 400s
"folder not found", silently dropping all generated dashboards.

Add a folderTitle config field: when set, resolveFolderUid() looks the folder
up by name (GET /api/folders), creates it if absent (POST /api/folders),
caches the uid for the process, and falls back to the configured folderUid on
any failure (never worse than the pinned behavior). The emit handlers
(registerChild/regenerateDashboard/emitDashboardsFor) are now async and await
the resolution. folderUid retained as an explicit override/fallback.

Locked by slice48-folder-resolve-by-name; existing emit tests made async.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-05-27 21:02:38 +02:00
parent 5533293647
commit 5d651b59ef
8 changed files with 217 additions and 27 deletions

View File

@@ -13,6 +13,7 @@
protocol: { value: 'http' },
host: { value: 'localhost' },
port: { value: 3000 },
folderTitle: { value: '' },
folderUid: { value: '' },
defaultBucket: { value: '' },
},
@@ -47,7 +48,7 @@
window.EVOLV.nodes.dashboardapi.loggerMenu.saveEditor(node);
}
['name', 'protocol', 'host', 'port', 'folderUid', 'defaultBucket'].forEach((field) => {
['name', 'protocol', 'host', 'port', 'folderTitle', 'folderUid', 'defaultBucket'].forEach((field) => {
const element = document.getElementById(`node-input-${field}`);
if (!element) return;
node[field] = field === 'port' ? parseInt(element.value, 10) || 3000 : element.value || '';
@@ -87,9 +88,14 @@
<input type="password" id="node-input-bearerToken" placeholder="encrypted at rest" style="width:70%;" />
</div>
<div class="form-row">
<label for="node-input-folderTitle"><i class="fa fa-folder"></i> Grafana Folder</label>
<input type="text" id="node-input-folderTitle" placeholder="folder name e.g. EVOLV — resolved/created by name" style="width:70%;" />
</div>
<div class="form-row">
<label for="node-input-folderUid"><i class="fa fa-folder-open"></i> Grafana Folder UID</label>
<input type="text" id="node-input-folderUid" placeholder="optional — empty = General folder" style="width:70%;" />
<input type="text" id="node-input-folderUid" placeholder="optional fallback — leave empty when Folder name is set" style="width:70%;" />
</div>
<div class="form-row">