// Runs every panel of every dashboard (or the uids given) through
// contents/ui/Grafana.js against a real instance, the same way the widget
// does, and prints what each panel would show.
//
// node tools/check.mjs # reads GRAFANA_URL / GRAFANA_TOKEN from .env
// node tools/check.mjs git-gud waxx
import { readFileSync } from "node:fs"
import vm from "node:vm"
const root = new URL("..", import.meta.url).pathname
const env = Object.fromEntries(
readFileSync(root + ".env", "utf8").split("\n").filter(l => l.includes("="))
.map(l => [l.slice(0, l.indexOf("=")).trim(), l.slice(l.indexOf("=") + 1).trim()]))
const base = (env.GRAFANA_URL || "").replace(/\/+$/, "")
const headers = { Authorization: "Bearer " + env.GRAFANA_TOKEN, "Content-Type": "application/json", Accept: "application/json" }
const src = readFileSync(root + "contents/ui/Grafana.js", "utf8").replace(/^\.pragma library\s*$/m, "")
const sandbox = { module: { exports: {} } }
vm.runInNewContext(src, sandbox)
const G = sandbox.module.exports
async function get(path) {
const r = await fetch(base + path, { headers })
if (!r.ok) throw new Error(path + ": HTTP " + r.status)
return r.json()
}
const settings = await get("/api/frontend/settings")
const uids = process.argv.slice(2).length ? process.argv.slice(2)
: (await get("/api/search?type=dash-db&limit=500")).map(d => d.uid)
let failures = 0
for (const uid of uids) {
const { dashboard } = await get("/api/dashboards/uid/" + uid)
const range = G.resolveRange(dashboard.time || {}, Date.now())
// evaluate query variables in order, each seeing the ones before it
const fetched = {}
let vars = G.variables(dashboard, settings.datasources, {}, fetched)
for (const v of dashboard.templating?.list || []) {
const req = G.varRequest(v, vars, settings.datasources, range)
if (!req) continue
fetched[v.name] = G.varOptions(v, req, await get(req.path))
vars = G.variables(dashboard, settings.datasources, {}, fetched)
console.log(` $${v.name}: ${fetched[v.name].length} options, selected ${G.selectionText(vars[v.name])}`)
}
// every row open, so folded panels get checked too
const open = Object.fromEntries((dashboard.panels || []).filter(p => p.type === "row").map(p => [p.id, false]))
const items = G.layout(dashboard, open, vars)
console.log(`\n== ${dashboard.title} (${uid}) — ${items.length} items, ${G.rangeLabel(dashboard.time || {})}`)
for (const it of items) {
const p = it.panel
if (it.kind === "row") { console.log(` ▸ row "${p.title}" ${it.collapsed ? "(folded, " + it.count + ")" : ""}`); continue }
const where = `[${it.x},${it.y} ${it.w}x${it.h}]`
if (!G.supported(p.type)) { console.log(` ? ${where} ${p.type} "${p.title}" unsupported`); continue }
if (p.type === "text") { console.log(` T ${where} text "${p.title}"`); continue }
const q = G.buildQuery(p, dashboard, vars, settings.datasources, range, 300)
if (q.error) { console.log(` ! ${where} ${p.type} "${p.title}": ${q.error}`); failures++; continue }
const r = await fetch(base + "/api/ds/query", { method: "POST", headers, body: JSON.stringify(q.body) })
const data = await r.json()
if (!r.ok) { console.log(` ! ${where} ${p.type} "${p.title}": HTTP ${r.status} ${data.message || ""}`); failures++; continue }
const errs = G.resultErrors(data.results)
const series = G.seriesFromResults(data.results, p)
let shown
if (p.type === "table") {
const t = G.tableFromResults(data.results, p)
shown = `${t.columns.length} cols × ${t.rows.length} rows: ${t.columns.map(c => c.name).join(", ")}`
} else {
const calc = (p.options?.reduceOptions?.calcs || ["lastNotNull"])[0]
shown = series.slice(0, 3).map((s, i) => {
const cfg = G.fieldConfig(p, s)
const v = G.reduce(s.values, calc)
return `${G.displayName(s, cfg)}=${G.format(v, cfg.unit, cfg.decimals)} ${G.seriesColor(cfg, i, v, null, "text")}`
}).join(" | ") + (series.length > 3 ? ` (+${series.length - 3})` : "")
}
console.log(` ${errs.length ? "!" : "✓"} ${where} ${p.type} "${G.panelTitle(p, vars)}" ${series.length} series ${shown}${errs.length ? " ERR " + errs.join("; ") : ""}`)
if (errs.length) failures++
}
}
console.log(`\n${failures} panel(s) failed`)
neiam
/grafana-plasmoid
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
4.4 KiB · text
History
35c3541