@intentius/chant-lexicon-grafana 0.97.0 → 0.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/README.md +15 -5
  2. package/dist/actions/grafana.d.ts +24 -0
  3. package/dist/actions/grafana.d.ts.map +1 -0
  4. package/dist/actions/index.d.ts +2 -0
  5. package/dist/actions/index.d.ts.map +1 -0
  6. package/dist/alerting-build.d.ts +128 -0
  7. package/dist/alerting-build.d.ts.map +1 -0
  8. package/dist/alerting.d.ts +273 -0
  9. package/dist/alerting.d.ts.map +1 -0
  10. package/dist/api/apply.d.ts +156 -0
  11. package/dist/api/apply.d.ts.map +1 -0
  12. package/dist/api/bind.d.ts +32 -0
  13. package/dist/api/bind.d.ts.map +1 -0
  14. package/dist/api/client.d.ts +76 -0
  15. package/dist/api/client.d.ts.map +1 -0
  16. package/dist/api/converge.d.ts +33 -0
  17. package/dist/api/converge.d.ts.map +1 -0
  18. package/dist/api/dashboards.d.ts +102 -0
  19. package/dist/api/dashboards.d.ts.map +1 -0
  20. package/dist/api/datasources.d.ts +26 -0
  21. package/dist/api/datasources.d.ts.map +1 -0
  22. package/dist/api/fake-grafana-writes.d.ts +41 -0
  23. package/dist/api/fake-grafana-writes.d.ts.map +1 -0
  24. package/dist/api/fake-grafana.d.ts +61 -0
  25. package/dist/api/fake-grafana.d.ts.map +1 -0
  26. package/dist/api/folders.d.ts +110 -0
  27. package/dist/api/folders.d.ts.map +1 -0
  28. package/dist/api/library-panels.d.ts +52 -0
  29. package/dist/api/library-panels.d.ts.map +1 -0
  30. package/dist/build.d.ts +30 -2
  31. package/dist/build.d.ts.map +1 -1
  32. package/dist/catalog.d.ts +1 -1
  33. package/dist/catalog.d.ts.map +1 -1
  34. package/dist/codegen/docs.d.ts.map +1 -1
  35. package/dist/codegen/schema-types.d.ts +3 -3
  36. package/dist/codegen/schema-types.d.ts.map +1 -1
  37. package/dist/composites/agent-dashboard.d.ts.map +1 -1
  38. package/dist/composites/catalog.d.ts.map +1 -1
  39. package/dist/composites/index.d.ts +4 -2
  40. package/dist/composites/index.d.ts.map +1 -1
  41. package/dist/composites/red-dashboard.d.ts +21 -2
  42. package/dist/composites/red-dashboard.d.ts.map +1 -1
  43. package/dist/composites/shared.d.ts +7 -0
  44. package/dist/composites/shared.d.ts.map +1 -1
  45. package/dist/composites/slo-alert-rules.d.ts +92 -0
  46. package/dist/composites/slo-alert-rules.d.ts.map +1 -0
  47. package/dist/config.d.ts +111 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/contact-point-secrets.d.ts +17 -0
  50. package/dist/contact-point-secrets.d.ts.map +1 -0
  51. package/dist/dashboard.d.ts +16 -2
  52. package/dist/dashboard.d.ts.map +1 -1
  53. package/dist/datasource-refs.d.ts +95 -0
  54. package/dist/datasource-refs.d.ts.map +1 -0
  55. package/dist/datasource.d.ts +29 -0
  56. package/dist/datasource.d.ts.map +1 -1
  57. package/dist/deep-observe-hooks.d.ts +64 -0
  58. package/dist/deep-observe-hooks.d.ts.map +1 -0
  59. package/dist/deep-observe.d.ts +42 -0
  60. package/dist/deep-observe.d.ts.map +1 -0
  61. package/dist/describe-resources.d.ts +49 -0
  62. package/dist/describe-resources.d.ts.map +1 -0
  63. package/dist/detect.d.ts +28 -1
  64. package/dist/detect.d.ts.map +1 -1
  65. package/dist/export-resources.d.ts +38 -0
  66. package/dist/export-resources.d.ts.map +1 -0
  67. package/dist/import/alerting.d.ts +40 -0
  68. package/dist/import/alerting.d.ts.map +1 -0
  69. package/dist/import/edits.d.ts +47 -0
  70. package/dist/import/edits.d.ts.map +1 -0
  71. package/dist/import/embedded.d.ts +14 -0
  72. package/dist/import/embedded.d.ts.map +1 -0
  73. package/dist/import/generator.d.ts +60 -0
  74. package/dist/import/generator.d.ts.map +1 -0
  75. package/dist/import/live-export.d.ts +108 -0
  76. package/dist/import/live-export.d.ts.map +1 -0
  77. package/dist/import/mappings.d.ts +83 -0
  78. package/dist/import/mappings.d.ts.map +1 -0
  79. package/dist/import/model.d.ts +98 -0
  80. package/dist/import/model.d.ts.map +1 -0
  81. package/dist/import/normalize-alerting.d.ts +26 -0
  82. package/dist/import/normalize-alerting.d.ts.map +1 -0
  83. package/dist/import/normalize.d.ts +74 -0
  84. package/dist/import/normalize.d.ts.map +1 -0
  85. package/dist/import/parser.d.ts +110 -0
  86. package/dist/import/parser.d.ts.map +1 -0
  87. package/dist/import/report.d.ts +26 -0
  88. package/dist/import/report.d.ts.map +1 -0
  89. package/dist/import/testdata/fixtures.d.ts +35 -0
  90. package/dist/import/testdata/fixtures.d.ts.map +1 -0
  91. package/dist/import/v2.d.ts +72 -0
  92. package/dist/import/v2.d.ts.map +1 -0
  93. package/dist/index.d.ts +26 -9
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/integrity.json +18 -10
  96. package/dist/k8s.d.ts +107 -0
  97. package/dist/k8s.d.ts.map +1 -0
  98. package/dist/lint/audit-catalog.d.ts +2 -1
  99. package/dist/lint/audit-catalog.d.ts.map +1 -1
  100. package/dist/lint/post-synth/graf101.d.ts +2 -2
  101. package/dist/lint/post-synth/graf102.d.ts +1 -1
  102. package/dist/lint/post-synth/graf107.d.ts +1 -1
  103. package/dist/lint/post-synth/graf108.d.ts +8 -0
  104. package/dist/lint/post-synth/graf108.d.ts.map +1 -0
  105. package/dist/lint/post-synth/graf109.d.ts +8 -0
  106. package/dist/lint/post-synth/graf109.d.ts.map +1 -0
  107. package/dist/lint/post-synth/graf111.d.ts +8 -0
  108. package/dist/lint/post-synth/graf111.d.ts.map +1 -0
  109. package/dist/lint/post-synth/graf112.d.ts +8 -0
  110. package/dist/lint/post-synth/graf112.d.ts.map +1 -0
  111. package/dist/lint/post-synth/graf113.d.ts +8 -0
  112. package/dist/lint/post-synth/graf113.d.ts.map +1 -0
  113. package/dist/lint/post-synth/graf114.d.ts +8 -0
  114. package/dist/lint/post-synth/graf114.d.ts.map +1 -0
  115. package/dist/lint/post-synth/graf115.d.ts +8 -0
  116. package/dist/lint/post-synth/graf115.d.ts.map +1 -0
  117. package/dist/lint/post-synth/grafana-helpers.d.ts +7 -5
  118. package/dist/lint/post-synth/grafana-helpers.d.ts.map +1 -1
  119. package/dist/lint/post-synth/index.d.ts.map +1 -1
  120. package/dist/lint/rules/literal-secret.d.ts +7 -3
  121. package/dist/lint/rules/literal-secret.d.ts.map +1 -1
  122. package/dist/lsp/hover.d.ts.map +1 -1
  123. package/dist/manifest.json +1 -1
  124. package/dist/members.d.ts +27 -0
  125. package/dist/members.d.ts.map +1 -0
  126. package/dist/meta.json +135 -0
  127. package/dist/okf/index.md +36 -2
  128. package/dist/okf/rules/GRAF002.md +3 -2
  129. package/dist/okf/rules/GRAF101.md +3 -2
  130. package/dist/okf/rules/GRAF102.md +1 -0
  131. package/dist/okf/rules/GRAF108.md +11 -0
  132. package/dist/okf/rules/GRAF109.md +15 -0
  133. package/dist/okf/rules/GRAF111.md +11 -0
  134. package/dist/okf/rules/GRAF112.md +16 -0
  135. package/dist/okf/rules/GRAF113.md +16 -0
  136. package/dist/okf/rules/GRAF114.md +11 -0
  137. package/dist/okf/rules/GRAF115.md +11 -0
  138. package/dist/okf/types/AlertListPanel.md +9 -0
  139. package/dist/okf/types/AlertQuery.md +9 -0
  140. package/dist/okf/types/AlertRule.md +9 -0
  141. package/dist/okf/types/AlertRuleGroup.md +9 -0
  142. package/dist/okf/types/BarChartPanel.md +9 -0
  143. package/dist/okf/types/BarGaugePanel.md +9 -0
  144. package/dist/okf/types/CanvasPanel.md +9 -0
  145. package/dist/okf/types/ClassicConditionsExpression.md +9 -0
  146. package/dist/okf/types/ContactPoint.md +14 -0
  147. package/dist/okf/types/Dashboard.md +1 -0
  148. package/dist/okf/types/Datasource.md +3 -2
  149. package/dist/okf/types/ExternalDatasource.md +15 -0
  150. package/dist/okf/types/FlameGraphPanel.md +9 -0
  151. package/dist/okf/types/GeomapPanel.md +9 -0
  152. package/dist/okf/types/HistogramPanel.md +9 -0
  153. package/dist/okf/types/MathExpression.md +9 -0
  154. package/dist/okf/types/MuteTiming.md +13 -0
  155. package/dist/okf/types/NodeGraphPanel.md +9 -0
  156. package/dist/okf/types/NotificationPolicy.md +9 -0
  157. package/dist/okf/types/NotificationTemplate.md +9 -0
  158. package/dist/okf/types/PieChartPanel.md +9 -0
  159. package/dist/okf/types/ReduceExpression.md +9 -0
  160. package/dist/okf/types/ResampleExpression.md +9 -0
  161. package/dist/okf/types/SqlExpression.md +9 -0
  162. package/dist/okf/types/StateTimelinePanel.md +9 -0
  163. package/dist/okf/types/StatusHistoryPanel.md +9 -0
  164. package/dist/okf/types/ThresholdExpression.md +9 -0
  165. package/dist/okf/types/TrendPanel.md +9 -0
  166. package/dist/okf/types/XYChartPanel.md +9 -0
  167. package/dist/op/activities/grafana-apply.d.ts +75 -0
  168. package/dist/op/activities/grafana-apply.d.ts.map +1 -0
  169. package/dist/op/activities/index.d.ts +9 -0
  170. package/dist/op/activities/index.d.ts.map +1 -0
  171. package/dist/op/builders.d.ts +24 -0
  172. package/dist/op/builders.d.ts.map +1 -0
  173. package/dist/ownership.d.ts +67 -0
  174. package/dist/ownership.d.ts.map +1 -0
  175. package/dist/panel-options.d.ts +65 -0
  176. package/dist/panel-options.d.ts.map +1 -0
  177. package/dist/panels.d.ts +46 -9
  178. package/dist/panels.d.ts.map +1 -1
  179. package/dist/pin.d.ts +29 -7
  180. package/dist/pin.d.ts.map +1 -1
  181. package/dist/plugin.d.ts.map +1 -1
  182. package/dist/promql-check.d.ts +51 -0
  183. package/dist/promql-check.d.ts.map +1 -0
  184. package/dist/query.d.ts +9 -8
  185. package/dist/query.d.ts.map +1 -1
  186. package/dist/rules/graf101.ts +3 -3
  187. package/dist/rules/graf102.ts +1 -1
  188. package/dist/rules/graf107.ts +1 -1
  189. package/dist/rules/graf108.ts +17 -0
  190. package/dist/rules/graf109.ts +17 -0
  191. package/dist/rules/graf111.ts +17 -0
  192. package/dist/rules/graf112.ts +17 -0
  193. package/dist/rules/graf113.ts +17 -0
  194. package/dist/rules/graf114.ts +17 -0
  195. package/dist/rules/graf115.ts +17 -0
  196. package/dist/rules/grafana-helpers.ts +15 -8
  197. package/dist/rules/literal-secret.ts +60 -6
  198. package/dist/schema/barchart.gen.d.ts +198 -0
  199. package/dist/schema/barchart.gen.d.ts.map +1 -0
  200. package/dist/schema/bargauge.gen.d.ts +97 -0
  201. package/dist/schema/bargauge.gen.d.ts.map +1 -0
  202. package/dist/schema/canvas.gen.d.ts +152 -0
  203. package/dist/schema/canvas.gen.d.ts.map +1 -0
  204. package/dist/schema/dashboard.gen.d.ts +196 -21
  205. package/dist/schema/dashboard.gen.d.ts.map +1 -1
  206. package/dist/schema/expr.gen.d.ts +606 -0
  207. package/dist/schema/expr.gen.d.ts.map +1 -0
  208. package/dist/schema/gauge.gen.d.ts.map +1 -1
  209. package/dist/schema/geomap.gen.d.ts +100 -0
  210. package/dist/schema/geomap.gen.d.ts.map +1 -0
  211. package/dist/schema/heatmap.gen.d.ts.map +1 -1
  212. package/dist/schema/histogram.gen.d.ts +132 -0
  213. package/dist/schema/histogram.gen.d.ts.map +1 -0
  214. package/dist/schema/index.d.ts +12 -0
  215. package/dist/schema/index.d.ts.map +1 -1
  216. package/dist/schema/logs.gen.d.ts +1 -0
  217. package/dist/schema/logs.gen.d.ts.map +1 -1
  218. package/dist/schema/loki.gen.d.ts.map +1 -1
  219. package/dist/schema/nodegraph.gen.d.ts +49 -0
  220. package/dist/schema/nodegraph.gen.d.ts.map +1 -0
  221. package/dist/schema/piechart.gen.d.ts +127 -0
  222. package/dist/schema/piechart.gen.d.ts.map +1 -0
  223. package/dist/schema/prometheus.gen.d.ts.map +1 -1
  224. package/dist/schema/stat.gen.d.ts.map +1 -1
  225. package/dist/schema/statetimeline.gen.d.ts +124 -0
  226. package/dist/schema/statetimeline.gen.d.ts.map +1 -0
  227. package/dist/schema/statushistory.gen.d.ts +114 -0
  228. package/dist/schema/statushistory.gen.d.ts.map +1 -0
  229. package/dist/schema/table.gen.d.ts +22 -3
  230. package/dist/schema/table.gen.d.ts.map +1 -1
  231. package/dist/schema/tempo.gen.d.ts.map +1 -1
  232. package/dist/schema/text.gen.d.ts.map +1 -1
  233. package/dist/schema/timeseries.gen.d.ts +5 -1
  234. package/dist/schema/timeseries.gen.d.ts.map +1 -1
  235. package/dist/schema/trend.gen.d.ts +180 -0
  236. package/dist/schema/trend.gen.d.ts.map +1 -0
  237. package/dist/schema/xychart.gen.d.ts +145 -0
  238. package/dist/schema/xychart.gen.d.ts.map +1 -0
  239. package/dist/schema-validate.d.ts +30 -4
  240. package/dist/schema-validate.d.ts.map +1 -1
  241. package/dist/serializer.d.ts +11 -3
  242. package/dist/serializer.d.ts.map +1 -1
  243. package/dist/skill-defs.d.ts.map +1 -1
  244. package/dist/skills/chant-grafana-alerting.md +49 -0
  245. package/dist/skills/chant-grafana-provisioning.md +3 -3
  246. package/dist/skills/chant-grafana.md +12 -6
  247. package/dist/spec/fetch-units-cli.d.ts +3 -0
  248. package/dist/spec/fetch-units-cli.d.ts.map +1 -0
  249. package/dist/spec/overlay.d.ts +43 -0
  250. package/dist/spec/overlay.d.ts.map +1 -0
  251. package/dist/spec/schemas.d.ts +13 -7
  252. package/dist/spec/schemas.d.ts.map +1 -1
  253. package/dist/spec/units.d.ts +35 -0
  254. package/dist/spec/units.d.ts.map +1 -0
  255. package/dist/spec/units.gen.d.ts +2 -0
  256. package/dist/spec/units.gen.d.ts.map +1 -0
  257. package/dist/transformations.d.ts +459 -0
  258. package/dist/transformations.d.ts.map +1 -0
  259. package/dist/util.d.ts +6 -0
  260. package/dist/util.d.ts.map +1 -1
  261. package/dist/validate-alerting.d.ts +65 -0
  262. package/dist/validate-alerting.d.ts.map +1 -0
  263. package/dist/validate-output.d.ts +49 -9
  264. package/dist/validate-output.d.ts.map +1 -1
  265. package/dist/validate.d.ts.map +1 -1
  266. package/dist/variables.d.ts +7 -2
  267. package/dist/variables.d.ts.map +1 -1
  268. package/package.json +12 -4
  269. package/src/actions/actions.test.ts +24 -0
  270. package/src/actions/grafana.ts +40 -0
  271. package/src/actions/index.ts +1 -0
  272. package/src/alerting-build.ts +376 -0
  273. package/src/alerting.e2e.test.ts +212 -0
  274. package/src/alerting.test.ts +272 -0
  275. package/src/alerting.ts +435 -0
  276. package/src/api/apply.test.ts +238 -0
  277. package/src/api/apply.ts +384 -0
  278. package/src/api/bind.ts +58 -0
  279. package/src/api/client.ts +148 -0
  280. package/src/api/converge.ts +63 -0
  281. package/src/api/dashboards.ts +247 -0
  282. package/src/api/datasources.ts +33 -0
  283. package/src/api/fake-grafana-writes.ts +201 -0
  284. package/src/api/fake-grafana.ts +186 -0
  285. package/src/api/folders.ts +265 -0
  286. package/src/api/library-panels.ts +101 -0
  287. package/src/build.test.ts +210 -5
  288. package/src/build.ts +180 -43
  289. package/src/catalog.ts +86 -2
  290. package/src/codegen/docs.ts +42 -9
  291. package/src/codegen/schema-types.ts +16 -6
  292. package/src/composites/agent-dashboard.ts +6 -3
  293. package/src/composites/catalog.test.ts +2 -2
  294. package/src/composites/catalog.ts +82 -0
  295. package/src/composites/composites.test.ts +73 -9
  296. package/src/composites/index.ts +4 -2
  297. package/src/composites/queries.e2e.test.ts +374 -0
  298. package/src/composites/red-dashboard.ts +83 -12
  299. package/src/composites/shared.ts +11 -0
  300. package/src/composites/slo-alert-rules.ts +149 -0
  301. package/src/config.test.ts +61 -0
  302. package/src/config.ts +138 -0
  303. package/src/contact-point-secrets.ts +63 -0
  304. package/src/dashboard.ts +16 -2
  305. package/src/datasource-refs.test.ts +56 -0
  306. package/src/datasource-refs.ts +189 -0
  307. package/src/datasource.ts +52 -0
  308. package/src/deep-observe-hooks.ts +252 -0
  309. package/src/deep-observe.test.ts +265 -0
  310. package/src/deep-observe.ts +164 -0
  311. package/src/describe-resources.test.ts +255 -0
  312. package/src/describe-resources.ts +209 -0
  313. package/src/detect.ts +79 -2
  314. package/src/export-resources.test.ts +129 -0
  315. package/src/export-resources.ts +84 -0
  316. package/src/generated/lexicon-grafana.json +135 -0
  317. package/src/import/alerting-roundtrip.test.ts +161 -0
  318. package/src/import/alerting.ts +500 -0
  319. package/src/import/edits.ts +95 -0
  320. package/src/import/embedded.test.ts +45 -0
  321. package/src/import/embedded.ts +47 -0
  322. package/src/import/generated-types.e2e.test.ts +118 -0
  323. package/src/import/generator.test.ts +210 -0
  324. package/src/import/generator.ts +462 -0
  325. package/src/import/live-export.ts +264 -0
  326. package/src/import/mappings.ts +264 -0
  327. package/src/import/model.ts +110 -0
  328. package/src/import/normalize-alerting.ts +101 -0
  329. package/src/import/normalize.test.ts +117 -0
  330. package/src/import/normalize.ts +364 -0
  331. package/src/import/parser.test.ts +440 -0
  332. package/src/import/parser.ts +1236 -0
  333. package/src/import/report.ts +62 -0
  334. package/src/import/roundtrip.test.ts +468 -0
  335. package/src/import/testdata/fixtures.ts +129 -0
  336. package/src/import/v2.test.ts +292 -0
  337. package/src/import/v2.ts +803 -0
  338. package/src/import.e2e.test.ts +31 -84
  339. package/src/import.test.ts +9 -3
  340. package/src/index.ts +190 -5
  341. package/src/k8s.test.ts +98 -0
  342. package/src/k8s.ts +197 -0
  343. package/src/lint/audit-catalog.ts +60 -3
  344. package/src/lint/core-lint.test.ts +43 -0
  345. package/src/lint/post-synth/graf101.ts +3 -3
  346. package/src/lint/post-synth/graf102.ts +1 -1
  347. package/src/lint/post-synth/graf107.ts +1 -1
  348. package/src/lint/post-synth/graf108.ts +17 -0
  349. package/src/lint/post-synth/graf109.ts +17 -0
  350. package/src/lint/post-synth/graf111.ts +17 -0
  351. package/src/lint/post-synth/graf112.ts +17 -0
  352. package/src/lint/post-synth/graf113.ts +17 -0
  353. package/src/lint/post-synth/graf114.ts +17 -0
  354. package/src/lint/post-synth/graf115.ts +17 -0
  355. package/src/lint/post-synth/grafana-helpers.ts +15 -8
  356. package/src/lint/post-synth/index.ts +14 -0
  357. package/src/lint/post-synth/post-synth.test.ts +235 -11
  358. package/src/lint/rules/literal-secret.ts +60 -6
  359. package/src/lint/rules/rules.test.ts +27 -0
  360. package/src/lsp/completions.test.ts +5 -0
  361. package/src/lsp/hover.test.ts +7 -0
  362. package/src/lsp/hover.ts +8 -1
  363. package/src/members.ts +68 -0
  364. package/src/observe.e2e.test.ts +255 -0
  365. package/src/op/activities/grafana-apply.e2e.test.ts +175 -0
  366. package/src/op/activities/grafana-apply.test.ts +229 -0
  367. package/src/op/activities/grafana-apply.ts +171 -0
  368. package/src/op/activities/index.ts +8 -0
  369. package/src/op/builders.test.ts +40 -0
  370. package/src/op/builders.ts +34 -0
  371. package/src/ownership.ts +103 -0
  372. package/src/panel-options.ts +66 -0
  373. package/src/panels.test.ts +51 -0
  374. package/src/panels.ts +148 -10
  375. package/src/pin.ts +57 -6
  376. package/src/plugin.test.ts +5 -2
  377. package/src/plugin.ts +66 -0
  378. package/src/promql-check.test.ts +108 -0
  379. package/src/promql-check.ts +189 -0
  380. package/src/query.ts +13 -8
  381. package/src/schema/barchart.gen.ts +221 -0
  382. package/src/schema/bargauge.gen.ts +111 -0
  383. package/src/schema/canvas.gen.ts +176 -0
  384. package/src/schema/dashboard.gen.ts +206 -22
  385. package/src/schema/expr.gen.ts +618 -0
  386. package/src/schema/gauge.gen.ts +2 -1
  387. package/src/schema/geomap.gen.ts +112 -0
  388. package/src/schema/heatmap.gen.ts +2 -1
  389. package/src/schema/histogram.gen.ts +151 -0
  390. package/src/schema/index.ts +12 -0
  391. package/src/schema/logs.gen.ts +3 -1
  392. package/src/schema/loki.gen.ts +2 -1
  393. package/src/schema/nodegraph.gen.ts +58 -0
  394. package/src/schema/piechart.gen.ts +146 -0
  395. package/src/schema/prometheus.gen.ts +2 -1
  396. package/src/schema/stat.gen.ts +2 -1
  397. package/src/schema/statetimeline.gen.ts +143 -0
  398. package/src/schema/statushistory.gen.ts +132 -0
  399. package/src/schema/table.gen.ts +27 -4
  400. package/src/schema/tempo.gen.ts +2 -1
  401. package/src/schema/text.gen.ts +2 -1
  402. package/src/schema/timeseries.gen.ts +7 -2
  403. package/src/schema/trend.gen.ts +208 -0
  404. package/src/schema/xychart.gen.ts +167 -0
  405. package/src/schema-overlay.test.ts +282 -0
  406. package/src/schema-validate.ts +141 -50
  407. package/src/schema.test.ts +4 -2
  408. package/src/serializer.test.ts +13 -1
  409. package/src/serializer.ts +11 -3
  410. package/src/skill-defs.ts +17 -0
  411. package/src/skills/chant-grafana-alerting.md +49 -0
  412. package/src/skills/chant-grafana-provisioning.md +3 -3
  413. package/src/skills/chant-grafana.md +12 -6
  414. package/src/spec/fetch-cli.ts +3 -3
  415. package/src/spec/fetch-units-cli.ts +37 -0
  416. package/src/spec/overlay/canvas.overlay.json +142 -0
  417. package/src/spec/overlay/dashboard.overlay.json +378 -0
  418. package/src/spec/overlay/expr.overlay.json +14 -0
  419. package/src/spec/overlay/geomap.overlay.json +35 -0
  420. package/src/spec/overlay/logs.overlay.json +14 -0
  421. package/src/spec/overlay/nodegraph.overlay.json +31 -0
  422. package/src/spec/overlay/piechart.overlay.json +17 -0
  423. package/src/spec/overlay/table.overlay.json +84 -0
  424. package/src/spec/overlay/timeseries.overlay.json +21 -0
  425. package/src/spec/overlay/trend.overlay.json +17 -0
  426. package/src/spec/overlay/xychart.overlay.json +16 -0
  427. package/src/spec/overlay.ts +105 -0
  428. package/src/spec/schemas/barchart.jsonschema.json +419 -0
  429. package/src/spec/schemas/bargauge.jsonschema.json +224 -0
  430. package/src/spec/schemas/canvas.jsonschema.json +356 -0
  431. package/src/spec/schemas/dashboardv2.jsonschema.json +3387 -0
  432. package/src/spec/schemas/expr.jsonschema.json +987 -0
  433. package/src/spec/schemas/geomap.jsonschema.json +220 -0
  434. package/src/spec/schemas/histogram.jsonschema.json +308 -0
  435. package/src/spec/schemas/nodegraph.jsonschema.json +77 -0
  436. package/src/spec/schemas/piechart.jsonschema.json +275 -0
  437. package/src/spec/schemas/statetimeline.jsonschema.json +324 -0
  438. package/src/spec/schemas/statushistory.jsonschema.json +292 -0
  439. package/src/spec/schemas/trend.jsonschema.json +449 -0
  440. package/src/spec/schemas/xychart.jsonschema.json +428 -0
  441. package/src/spec/schemas.ts +22 -10
  442. package/src/spec/units.gen.ts +281 -0
  443. package/src/spec/units.ts +77 -0
  444. package/src/transformations.test.ts +65 -0
  445. package/src/transformations.ts +574 -0
  446. package/src/util.ts +8 -0
  447. package/src/validate-alerting.test.ts +255 -0
  448. package/src/validate-alerting.ts +428 -0
  449. package/src/validate-output.ts +332 -101
  450. package/src/validate.ts +14 -0
  451. package/src/variables.ts +7 -2
@@ -1,24 +1,22 @@
1
1
  /**
2
2
  * "A dashboard builds to JSON that Grafana imports without edits", against
3
- * Grafana itself.
3
+ * Grafana itself, on every release in `GRAFANA_IMAGES` (12.4 and 13.2).
4
4
  *
5
5
  * Builds each example (getting-started, and dashboards-from-declarations,
6
6
  * whose dashboards the RED, SLO and agent composites build), mounts its
7
7
  * provisioning files and dashboards into a pinned `grafana/grafana`
8
- * container, and asserts that
9
- * Grafana provisioned every datasource and dashboard and hands each
10
- * dashboard back with the panels that were built. It then imports the same
11
- * JSON a second time through `POST /api/dashboards/db`, the path the UI's
12
- * import uses, under another uid.
8
+ * container, and asserts that Grafana provisioned every datasource and
9
+ * dashboard, and that each stored dashboard model is the built JSON, key
10
+ * for key, once the keys Grafana adds (`GRAFANA_INJECTED`: `id` and
11
+ * `version`) are taken out. It then imports the same JSON a second time
12
+ * through `POST /api/dashboards/db`, the path the UI's import uses, under
13
+ * another uid, and compares that stored model the same way.
13
14
  *
14
15
  * Skipped, with the reason in the test name, when Docker is not running.
15
- * On demand: `npx vitest run --project e2e lexicons/grafana`.
16
+ * On demand: `npx vitest run --project e2e lexicons/grafana/src/import.e2e.test.ts`.
16
17
  */
17
- import { execSync } from "node:child_process";
18
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
19
- import { tmpdir } from "node:os";
18
+ import { mkdirSync, writeFileSync } from "node:fs";
20
19
  import { dirname, join } from "node:path";
21
- import { fileURLToPath } from "node:url";
22
20
  import { afterAll, describe, expect, it } from "vitest";
23
21
  import { build } from "@intentius/chant/build";
24
22
  import type { SerializerResult } from "@intentius/chant/serializer";
@@ -26,23 +24,12 @@ import type { Serializer } from "@intentius/chant/serializer";
26
24
  import { otelSerializer } from "@intentius/chant-lexicon-otel/serializer";
27
25
  import { prometheusSerializer } from "@intentius/chant-lexicon-prometheus/serializer";
28
26
  import { grafanaSerializer } from "./serializer";
27
+ import { DockerScope, GRAFANA_IMAGES, dockerAvailable, waitFor } from "../test/e2e/containers";
28
+ import { withoutInjected } from "../test/e2e/stored-model";
29
29
 
30
- /** The Grafana release the import test runs against; `CHANT_GRAFANA_IMAGE` overrides it. */
31
- export const GRAFANA_IMAGE = process.env.CHANT_GRAFANA_IMAGE ?? "grafana/grafana:12.4.11";
32
-
33
- function available(cmd: string): boolean {
34
- try {
35
- execSync(cmd, { stdio: "ignore" });
36
- return true;
37
- } catch {
38
- return false;
39
- }
40
- }
41
-
42
- const hasDocker = available("docker info");
30
+ const hasDocker = dockerAvailable();
43
31
  const skipReason = hasDocker ? "" : "Docker is not running";
44
- const AUTH = `Basic ${Buffer.from("admin:admin").toString("base64")}`;
45
- const examples = join(dirname(dirname(fileURLToPath(import.meta.url))), "examples");
32
+ const examples = join(import.meta.dirname, "..", "examples");
46
33
 
47
34
  /** Each example, and the serializers its build root needs. */
48
35
  const EXAMPLES: Array<{ name: string; serializers: Serializer[] }> = [
@@ -50,98 +37,58 @@ const EXAMPLES: Array<{ name: string; serializers: Serializer[] }> = [
50
37
  { name: "dashboards-from-declarations", serializers: [otelSerializer, prometheusSerializer, grafanaSerializer] },
51
38
  ];
52
39
 
53
- async function waitFor<T>(what: string, fn: () => Promise<T | undefined>, timeoutMs = 120_000): Promise<T> {
54
- const deadline = Date.now() + timeoutMs;
55
- let last: unknown;
56
- while (Date.now() < deadline) {
57
- try {
58
- const v = await fn();
59
- if (v !== undefined) return v;
60
- } catch (err) {
61
- last = err;
62
- }
63
- await new Promise((r) => setTimeout(r, 1000));
64
- }
65
- throw new Error(`timed out waiting for ${what}${last ? `: ${String(last)}` : ""}`);
66
- }
40
+ const MATRIX = GRAFANA_IMAGES.flatMap((image) => EXAMPLES.map((e) => ({ ...e, image })));
67
41
 
68
- describe.skipIf(!hasDocker).each(EXAMPLES)(`Grafana imports the $name dashboards without edits${skipReason ? ` (skipped: ${skipReason})` : ""}`, ({ name, serializers }) => {
69
- const CONTAINER = `chant-grafana-import-${name}-${process.pid}`;
70
- const dir = mkdtempSync(join(tmpdir(), "chant-grafana-import-"));
42
+ describe.skipIf(!hasDocker).each(MATRIX)(`$image imports the $name dashboards without edits${skipReason ? ` (skipped: ${skipReason})` : ""}`, ({ name, serializers, image }) => {
43
+ const scope = new DockerScope(`grafana-import-${name}`);
71
44
 
72
- afterAll(() => {
73
- try {
74
- execSync(`docker rm -f ${CONTAINER}`, { stdio: "ignore" });
75
- } catch {
76
- // already gone
77
- }
78
- rmSync(dir, { recursive: true, force: true });
79
- });
45
+ afterAll(() => scope.cleanup());
80
46
 
81
- it("provisions every datasource and dashboard, and accepts the JSON through the import API", { timeout: 300_000 }, async () => {
47
+ it("provisions every datasource and dashboard as built, and accepts the JSON through the import API", { timeout: 300_000 }, async () => {
82
48
  const result = await build(join(examples, name, "src"), serializers);
83
49
  expect(result.errors).toEqual([]);
84
50
  const out = result.outputs.get("grafana") as SerializerResult;
51
+ const dir = scope.tempDir();
85
52
  for (const [file, content] of Object.entries(out.files ?? {})) {
86
53
  mkdirSync(dirname(join(dir, file)), { recursive: true });
87
54
  writeFileSync(join(dir, file), content);
88
55
  }
89
56
  const index = JSON.parse(out.primary) as { dashboards: Array<{ uid: string; title: string; folder?: string; file: string }>; datasources: Array<{ uid: string; type: string; name: string }> };
90
57
 
91
- execSync(
92
- [
93
- "docker run -d",
94
- `--name ${CONTAINER}`,
95
- "-p 127.0.0.1::3000",
96
- "-e GF_SECURITY_ADMIN_PASSWORD=admin",
97
- "-e GF_ANALYTICS_REPORTING_ENABLED=false",
98
- "-e GF_ANALYTICS_CHECK_FOR_UPDATES=false",
99
- `-v ${join(dir, "provisioning")}:/etc/grafana/provisioning:ro`,
100
- `-v ${join(dir, "dashboards")}:/var/lib/grafana/dashboards:ro`,
101
- GRAFANA_IMAGE,
102
- ].join(" "),
103
- { stdio: "ignore" },
104
- );
105
- const port = execSync(`docker port ${CONTAINER} 3000/tcp`).toString().trim().split("\n")[0].split(":").pop();
106
- const base = `http://127.0.0.1:${port}`;
107
- const api = async (path: string, init?: RequestInit) => {
108
- const res = await fetch(`${base}${path}`, { ...init, headers: { authorization: AUTH, "content-type": "application/json", ...(init?.headers ?? {}) } });
109
- return { status: res.status, body: (await res.json()) as Record<string, unknown> & unknown[] };
110
- };
111
-
112
- await waitFor("Grafana to answer", async () => ((await fetch(`${base}/api/health`)).ok ? true : undefined));
58
+ const grafana = await scope.grafana({ image, provisioningDir: join(dir, "provisioning"), dashboardsDir: join(dir, "dashboards") });
59
+ const { api } = grafana;
113
60
 
114
61
  // Datasources: every declared one, with the uid and type chant gave it.
115
62
  const datasources = await waitFor("datasources", async () => {
116
63
  const { status, body } = await api("/api/datasources");
117
- return status === 200 && (body as unknown[]).length >= index.datasources.length ? (body as unknown as Array<{ uid: string; type: string; name: string }>) : undefined;
64
+ return status === 200 && (body as unknown[]).length >= index.datasources.length ? (body as Array<{ uid: string; type: string; name: string }>) : undefined;
118
65
  });
119
66
  expect(datasources.map((d) => [d.uid, d.type, d.name]).sort()).toEqual(index.datasources.map((d) => [d.uid, d.type, d.name]).sort());
120
67
 
121
- // Dashboards: provisioned, in their folder, with every panel as built.
68
+ // Dashboards: provisioned, in their folder, and stored exactly as built.
122
69
  for (const d of index.dashboards) {
123
70
  const { body } = await waitFor(`dashboard ${d.uid}`, async () => {
124
71
  const r = await api(`/api/dashboards/uid/${d.uid}`);
125
72
  return r.status === 200 ? r : undefined;
126
73
  });
127
74
  const meta = body.meta as { provisioned: boolean; folderTitle?: string };
128
- const stored = body.dashboard as { title: string; panels: Array<Record<string, unknown>>; templating: { list: unknown[] } };
129
- const built = JSON.parse(out.files![d.file]) as typeof stored;
75
+ const stored = body.dashboard as Record<string, unknown>;
76
+ const built = JSON.parse(out.files![d.file]) as Record<string, unknown>;
130
77
  expect(meta.provisioned).toBe(true);
131
78
  if (d.folder) expect(meta.folderTitle).toBe(d.folder);
132
- expect(stored.title).toBe(d.title);
133
- const shape = (panels: Array<Record<string, unknown>>) => panels.map((p) => [p.id, p.type, p.title, p.gridPos]);
134
- expect(shape(stored.panels)).toEqual(shape(built.panels));
135
- expect(stored.templating.list.length).toBe(built.templating.list.length);
79
+ expect(typeof stored.id, `${d.uid}: Grafana assigns the id`).toBe("number");
80
+ expect(stored.version, `${d.uid}: Grafana's first version`).toBe(1);
81
+ expect(withoutInjected(stored), `${image} stored ${d.uid} differently from the build`).toStrictEqual(built);
136
82
  }
137
83
 
138
84
  // The same JSON through the import API, under a new uid.
139
85
  const first = index.dashboards[0];
140
- const copy = { ...JSON.parse(out.files![first.file]), uid: `${first.uid}-api`, title: `${first.title} (API import)` };
86
+ const copy = { ...(JSON.parse(out.files![first.file]) as Record<string, unknown>), uid: `${first.uid}-api`, title: `${first.title} (API import)` };
141
87
  const imported = await api("/api/dashboards/db", { method: "POST", body: JSON.stringify({ dashboard: copy, overwrite: false }) });
142
88
  expect(imported.status).toBe(200);
143
89
  expect(imported.body.status).toBe("success");
144
90
  const back = await api(`/api/dashboards/uid/${first.uid}-api`);
145
- expect((back.body.dashboard as { panels: unknown[] }).panels.length).toBe(copy.panels.length);
91
+ expect(back.status).toBe(200);
92
+ expect(withoutInjected(back.body.dashboard as Record<string, unknown>), `${image} stored the API import differently from the JSON sent`).toStrictEqual(copy);
146
93
  });
147
94
  });
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Builds every example and validates each dashboard against Grafana's
5
5
  * dashboard schema at `GRAFANA_SCHEMA_PIN`, each panel's options and each
6
- * query against their plugin's schema, and runs GRAF101-GRAF107 over the
6
+ * query against their plugin's schema, and runs the GRAF1xx checks over the
7
7
  * output. `import.e2e.test.ts` does the same against a real Grafana when
8
8
  * Docker is available.
9
9
  */
@@ -18,6 +18,7 @@ import { grafanaSerializer } from "./serializer";
18
18
  import { validateDashboardSchema } from "./schema-validate";
19
19
  import { validateGrafanaOutput } from "./validate-output";
20
20
  import type { ProvisionedDatasource } from "./build";
21
+ import { ALERTING_FILE } from "./alerting-build";
21
22
 
22
23
  const examplesDir = join(dirname(dirname(fileURLToPath(import.meta.url))), "examples");
23
24
  const examples = readdirSync(examplesDir).filter((e) => {
@@ -34,12 +35,17 @@ describe.each(examples)("example %s", (example) => {
34
35
  expect(result.errors).toEqual([]);
35
36
  const out = result.outputs.get("grafana") as SerializerResult;
36
37
  const dashboards = Object.entries(out.files ?? {}).filter(([f]) => f.startsWith("dashboards/"));
37
- expect(dashboards.length).toBeGreaterThan(0);
38
+ const alerting = out.files?.[ALERTING_FILE];
39
+ expect(dashboards.length + (alerting ? 1 : 0)).toBeGreaterThan(0);
38
40
  for (const [file, text] of dashboards) {
39
41
  expect({ file, problems: validateDashboardSchema(JSON.parse(text)) }).toEqual({ file, problems: [] });
40
42
  }
41
43
  const ds = load(out.files?.["provisioning/datasources/chant.yaml"] ?? "datasources: []") as { datasources: ProvisionedDatasource[] };
42
- const issues = validateGrafanaOutput({ dashboards: dashboards.map(([source, t]) => ({ source, json: JSON.parse(t) })), datasources: ds.datasources });
44
+ const issues = validateGrafanaOutput({
45
+ dashboards: dashboards.map(([source, t]) => ({ source, json: JSON.parse(t) })),
46
+ datasources: ds.datasources,
47
+ alerting: alerting ? [{ source: ALERTING_FILE, json: load(alerting) as Record<string, unknown> }] : [],
48
+ });
43
49
  expect(issues).toEqual([]);
44
50
  });
45
51
  });
package/src/index.ts CHANGED
@@ -8,8 +8,15 @@ export { grafanaSerializer } from "./serializer";
8
8
  export {
9
9
  Datasource,
10
10
  DATASOURCE_TYPE,
11
+ ExternalDatasource,
12
+ EXTERNAL_DATASOURCE_TYPE,
11
13
  BUILTIN_DATASOURCE_UIDS,
12
14
  isDatasourceEntity,
15
+ isExternalDatasource,
16
+ isDatasourceDeclaration,
17
+ type ExternalDatasourceProps,
18
+ type ExternalDatasourceEntity,
19
+ type ExternalDatasourceConstructor,
13
20
  type DatasourceProps,
14
21
  type DatasourceEntity,
15
22
  type DatasourceConstructor,
@@ -34,6 +41,98 @@ export {
34
41
  type DashboardProviderEntity,
35
42
  } from "./dashboard";
36
43
 
44
+ // Grafana-managed alerting
45
+ export {
46
+ AlertRuleGroup,
47
+ AlertRule,
48
+ AlertQuery,
49
+ ReduceExpression,
50
+ MathExpression,
51
+ ThresholdExpression,
52
+ ResampleExpression,
53
+ ClassicConditionsExpression,
54
+ SqlExpression,
55
+ ContactPoint,
56
+ NotificationPolicy,
57
+ MuteTiming,
58
+ NotificationTemplate,
59
+ ALERT_RULE_GROUP_TYPE,
60
+ ALERT_RULE_TYPE,
61
+ ALERT_QUERY_TYPE,
62
+ EXPRESSION_TYPE_PREFIX,
63
+ EXPRESSION_DATASOURCE_UID,
64
+ CONTACT_POINT_TYPE,
65
+ NOTIFICATION_POLICY_TYPE,
66
+ MUTE_TIMING_TYPE,
67
+ NOTIFICATION_TEMPLATE_TYPE,
68
+ isAlertRuleGroupEntity,
69
+ isAlertRuleEntity,
70
+ isAlertQueryEntity,
71
+ isExpressionEntity,
72
+ isContactPointEntity,
73
+ isNotificationPolicyEntity,
74
+ isMuteTimingEntity,
75
+ isNotificationTemplateEntity,
76
+ type AlertRuleGroupProps,
77
+ type AlertRuleGroupEntity,
78
+ type AlertRuleProps,
79
+ type AlertRuleEntity,
80
+ type AlertRuleData,
81
+ type AlertRuleNotificationSettings,
82
+ type AlertRuleRecord,
83
+ type AlertQueryProps,
84
+ type AlertQueryEntity,
85
+ type AlertDuration,
86
+ type RelativeTimeRange,
87
+ type ExpressionEntity,
88
+ type ExpressionKind,
89
+ type ReduceExpressionProps,
90
+ type MathExpressionProps,
91
+ type ThresholdExpressionProps,
92
+ type ResampleExpressionProps,
93
+ type ClassicConditionsExpressionProps,
94
+ type SqlExpressionProps,
95
+ type NoDataState,
96
+ type ExecErrState,
97
+ type ContactPointProps,
98
+ type ContactPointEntity,
99
+ type ContactPointReceiver,
100
+ type ContactPointIntegrationType,
101
+ type NotificationPolicyProps,
102
+ type NotificationPolicyEntity,
103
+ type PolicyRoute,
104
+ type ObjectMatcher,
105
+ type MuteTimingProps,
106
+ type MuteTimingEntity,
107
+ type NotificationTemplateProps,
108
+ type NotificationTemplateEntity,
109
+ } from "./alerting";
110
+ export {
111
+ buildAlerting,
112
+ alertingYaml,
113
+ alertRuleJson,
114
+ alertQueryJson,
115
+ ruleGroupJson,
116
+ contactPointJson,
117
+ notificationPolicyJson,
118
+ muteTimingJson,
119
+ notificationTemplateJson,
120
+ durationSeconds,
121
+ ALERTING_FILE,
122
+ ALERTING_FILE_KEYS,
123
+ DEFAULT_RELATIVE_TIME_RANGE,
124
+ DEFAULT_GROUP_INTERVAL,
125
+ type AlertingFile,
126
+ type AlertingIndex,
127
+ type ProvisionedRuleGroup,
128
+ type ProvisionedAlertRule,
129
+ type ProvisionedAlertQuery,
130
+ type ProvisionedContactPoint,
131
+ type ProvisionedMuteTiming,
132
+ type ProvisionedTemplate,
133
+ } from "./alerting-build";
134
+ export { CONTACT_POINT_SECRET_SETTINGS } from "./contact-point-secrets";
135
+
37
136
  // Panels and rows, and the extension point for other panel plugins
38
137
  export {
39
138
  TimeSeriesPanel,
@@ -44,6 +143,19 @@ export {
44
143
  TracesPanel,
45
144
  HeatmapPanel,
46
145
  TextPanel,
146
+ BarChartPanel,
147
+ BarGaugePanel,
148
+ PieChartPanel,
149
+ StateTimelinePanel,
150
+ StatusHistoryPanel,
151
+ HistogramPanel,
152
+ NodeGraphPanel,
153
+ XYChartPanel,
154
+ TrendPanel,
155
+ CanvasPanel,
156
+ GeomapPanel,
157
+ FlameGraphPanel,
158
+ AlertListPanel,
47
159
  Row,
48
160
  definePanel,
49
161
  registeredPanels,
@@ -112,7 +224,32 @@ export {
112
224
  // The generated schema types and their pin
113
225
  export * as schema from "./schema";
114
226
  export { DASHBOARD_SCHEMA_VERSION } from "./schema";
115
- export { GRAFANA_SCHEMA_PIN, SCHEMA_NAMES, type GrafanaSchemaPin, type SchemaName } from "./pin";
227
+ export {
228
+ GRAFANA_SCHEMA_PIN,
229
+ SCHEMA_NAMES,
230
+ IMPORT_ONLY_SCHEMA_NAMES,
231
+ VENDORED_SCHEMA_NAMES,
232
+ type GrafanaSchemaPin,
233
+ type SchemaName,
234
+ type ImportOnlySchemaName,
235
+ type VendoredSchemaName,
236
+ } from "./pin";
237
+ // Hand-written options for the built-in panels with no schema
238
+ export type { AlertListOptions, AlertListSortOrder, AlertListStateFilter, FlameGraphOptions } from "./panel-options";
239
+
240
+ // Panel transformations, typed per transformer (Grafana v13.2.2)
241
+ export {
242
+ transformation,
243
+ customTransformation,
244
+ TRANSFORMATION_IDS,
245
+ type Transformation,
246
+ type KnownTransformation,
247
+ type CustomTransformation,
248
+ type TransformationCommon,
249
+ type TransformationId,
250
+ type TransformationOptionsById,
251
+ type ReducerId,
252
+ } from "./transformations";
116
253
 
117
254
  // Plain-data API: render, check and detect without a build
118
255
  export {
@@ -124,8 +261,10 @@ export {
124
261
  panelsJson,
125
262
  targetJson,
126
263
  variableModel,
264
+ customVariableOptions,
127
265
  datasourceRef,
128
266
  provisionedDatasource,
267
+ externalDatasourceRecord,
129
268
  provisionedProvider,
130
269
  datasourcesYaml,
131
270
  dashboardProvidersYaml,
@@ -137,6 +276,7 @@ export {
137
276
  type BuiltDashboard,
138
277
  type GrafanaIndex,
139
278
  type ProvisionedDatasource,
279
+ type ExternalDatasourceRecord,
140
280
  type ProvisionedProvider,
141
281
  type DashboardJson,
142
282
  type PanelJson,
@@ -147,20 +287,31 @@ export {
147
287
  export {
148
288
  validateGrafanaOutput,
149
289
  issuesFor,
290
+ knownDatasourcesOf,
150
291
  variableReferences,
151
292
  type GrafanaIssue,
152
293
  type GrafanaIssueCode,
153
294
  type GrafanaArtifacts,
154
295
  type DashboardDoc,
155
296
  } from "./validate-output";
156
- export { validateDashboardSchema, type SchemaProblem } from "./schema-validate";
157
- export { looksLikeDashboard, looksLikeDatasourceProvisioning, looksLikeDashboardProvisioning } from "./detect";
158
- export { slugUid, isValidUid, UID_PATTERN, type DeepPartial } from "./util";
297
+ export {
298
+ datasourceUses,
299
+ resolveDatasourceRef,
300
+ knownDatasources,
301
+ type DatasourceUse,
302
+ type ResolvedDatasource,
303
+ type KnownDatasource,
304
+ type DatasourceRefJson,
305
+ } from "./datasource-refs";
306
+ export { validateDashboardSchema, validateExpressionSchema, type SchemaProblem } from "./schema-validate";
307
+ export { looksLikeDashboard, looksLikeDatasourceProvisioning, looksLikeDashboardProvisioning, looksLikeAlertingProvisioning } from "./detect";
308
+ export { slugUid, isValidUid, UID_PATTERN, type DeepPartial, type PropsOf } from "./util";
159
309
 
160
- // Composites: dashboards built from a spanmetrics connector, an Slo and the GenAI preset
310
+ // Composites: dashboards built from a spanmetrics connector, an Slo and the GenAI preset, and an Slo's burn-rate alerts as Grafana rules
161
311
  export {
162
312
  RedDashboard,
163
313
  redQueries,
314
+ RED_DEFAULT_SPAN_KINDS,
164
315
  SloDashboard,
165
316
  sloQueries,
166
317
  AgentDashboard,
@@ -168,6 +319,7 @@ export {
168
319
  type RedDashboardProps,
169
320
  type RedDashboardMembers,
170
321
  type RedDashboardInstance,
322
+ type SpanKind,
171
323
  type SloDashboardProps,
172
324
  type SloDashboardMembers,
173
325
  type SloDashboardInstance,
@@ -175,4 +327,37 @@ export {
175
327
  type AgentDashboardMembers,
176
328
  type AgentDashboardInstance,
177
329
  type DashboardOptions,
330
+ SloAlertRules,
331
+ sloAlertQueries,
332
+ type SloAlertRulesProps,
333
+ type SloAlertRulesMembers,
334
+ type SloAlertRulesInstance,
178
335
  } from "./composites";
336
+
337
+ // Config namespace (#2946): `grafana.profiles.<env>` in chant.config.ts, the
338
+ // Grafana each environment is observed in and exported from.
339
+ export { grafanaConfigSchema, resolveGrafanaTarget } from "./config";
340
+ export type { GrafanaConfig, GrafanaProfile } from "./config";
341
+
342
+ // Ownership (#2946): chant's marker on a dashboard.grafana.app resource.
343
+ export {
344
+ GRAFANA_OWNERSHIP_KEYS,
345
+ DEFAULT_PROVIDER_NAME,
346
+ grafanaOwnershipLabels,
347
+ dashboardOwnership,
348
+ } from "./ownership";
349
+
350
+ // The HTTP client observe and export share, for the API applier (#2948).
351
+ export { GrafanaClient, GrafanaApiError, grafanaHttp, namespaceOf, statusVerdict } from "./api/client";
352
+ export type { GrafanaAuth, GrafanaHttp, GrafanaResponse, GrafanaTarget, StatusVerdict } from "./api/client";
353
+ export { bindGrafana, classifyGrafanaFailure, GrafanaBindingError } from "./api/bind";
354
+
355
+ // The API applier (#2948): the typed Op step, and the pieces an embedding
356
+ // caller composes (the activity itself is `./op/activities`).
357
+ export { grafanaApply } from "./op/builders";
358
+ export { applyGrafana, planFromDashboards, GRAFANA_APPLY_KINDS } from "./api/apply";
359
+ export type { GrafanaApplyPlan, GrafanaApplyOutcome, DashboardPlan, BuiltDashboardInput } from "./api/apply";
360
+ export { folderUidFor, foldersForDashboards, type FolderPlan } from "./api/folders";
361
+
362
+ // The Grafana RBAC actions a service account needs to observe, apply and prune.
363
+ export { GrafanaActions, grafanaActionsFor, type GrafanaAccessLevel } from "./actions/index";
@@ -0,0 +1,98 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { expandComposite } from "@intentius/chant/composite";
3
+ import { k8sSerializer } from "@intentius/chant-lexicon-k8s";
4
+ import { loadAll } from "js-yaml";
5
+ import { Dashboard } from "./dashboard";
6
+ import { Datasource } from "./datasource";
7
+ import { StatPanel } from "./panels";
8
+ import { PromQuery } from "./query";
9
+ import { dashboardJson } from "./build";
10
+ import { GrafanaConfigMaps, grafanaConfigMapLayout, grafanaVolumes } from "./k8s";
11
+
12
+ const prometheus = new Datasource({ name: "Prometheus", type: "prometheus", url: "http://prometheus:9090" });
13
+ const stat = () => new StatPanel({ title: "Up", datasource: prometheus, targets: [new PromQuery({ expr: "sum(up)" })] });
14
+ const top = new Dashboard({ title: "Top", uid: "Top_Level", panels: [stat()] });
15
+ const red = new Dashboard({ title: "RED", uid: "red", folder: "Services", panels: [stat()] });
16
+ const slo = new Dashboard({ title: "SLO", uid: "slo", folder: "Services", panels: [stat()] });
17
+ const entities = [prometheus, top, red, slo];
18
+
19
+ type Manifest = { kind: string; metadata: { name: string; namespace?: string; labels?: Record<string, string>; annotations?: Record<string, string> }; data: Record<string, string> };
20
+
21
+ function manifests(instance: ReturnType<typeof GrafanaConfigMaps>): Manifest[] {
22
+ const out = k8sSerializer.serialize(expandComposite("grafana", instance));
23
+ return (loadAll(typeof out === "string" ? out : out.primary) as Manifest[]).filter(Boolean);
24
+ }
25
+
26
+ describe("GrafanaConfigMaps", () => {
27
+ test("one ConfigMap per dashboard, labelled for the sidecar, holding dashboardJson(dashboard)", () => {
28
+ const docs = manifests(GrafanaConfigMaps({ entities, namespace: "obs", labels: { team: "sre" } }));
29
+ expect(docs.map((d) => d.metadata.name)).toEqual([
30
+ "grafana-dashboard-top-level",
31
+ "grafana-dashboard-red",
32
+ "grafana-dashboard-slo",
33
+ "grafana-datasources",
34
+ "grafana-dashboard-providers",
35
+ ]);
36
+ const [topCm, redCm] = docs;
37
+ expect(topCm.metadata).toEqual({ name: "grafana-dashboard-top-level", namespace: "obs", labels: { team: "sre", grafana_dashboard: "1" } });
38
+ expect(topCm.data).toEqual({ "Top_Level.json": dashboardJson(top) });
39
+ expect(redCm.metadata.annotations).toEqual({ "k8s-sidecar-target-directory": "Services" });
40
+ expect(docs[3].metadata.labels).toEqual({ team: "sre", grafana_datasource: "1" });
41
+ expect(Object.keys(docs[3].data)).toEqual(["chant.yaml"]);
42
+ expect(docs[3].data["chant.yaml"]).toContain("url: http://prometheus:9090");
43
+ expect(docs[4].metadata.labels).toEqual({ team: "sre" });
44
+ });
45
+
46
+ test("labels and the folder annotation can be changed or left off", () => {
47
+ const docs = manifests(GrafanaConfigMaps({ entities: [red], name: "Obs", dashboardLabel: ["dashboards", "obs"], folderAnnotation: false }));
48
+ expect(docs[0].metadata).toEqual({ name: "obs-dashboard-red", labels: { dashboards: "obs" } });
49
+ const bare = manifests(GrafanaConfigMaps({ entities: [prometheus], datasourceLabel: false }));
50
+ expect(bare.map((d) => [d.metadata.name, d.metadata.labels])).toEqual([["grafana-datasources", {}]]);
51
+ });
52
+
53
+ test("names stay unique and DNS-1123 when uids collide after lower-casing", () => {
54
+ const a = new Dashboard({ title: "A", uid: "Same" });
55
+ const b = new Dashboard({ title: "B", uid: "same" });
56
+ expect(grafanaConfigMapLayout({ entities: [a, b] }).dashboards.map((d) => d.configMap)).toEqual(["grafana-dashboard-same", "grafana-dashboard-same-2"]);
57
+ });
58
+ });
59
+
60
+ describe("grafanaVolumes", () => {
61
+ test("provisioning under /etc/grafana/provisioning, each dashboard folder a projected volume of its own", () => {
62
+ const { volumes, volumeMounts } = grafanaVolumes({ entities });
63
+ expect(volumeMounts).toEqual([
64
+ { name: "grafana-provisioning", mountPath: "/etc/grafana/provisioning", readOnly: true },
65
+ { name: "grafana-dashboards-0", mountPath: "/var/lib/grafana/dashboards", readOnly: true },
66
+ { name: "grafana-dashboards-1", mountPath: "/var/lib/grafana/dashboards/Services", readOnly: true },
67
+ ]);
68
+ expect(volumes).toEqual([
69
+ {
70
+ name: "grafana-provisioning",
71
+ projected: {
72
+ sources: [
73
+ { configMap: { name: "grafana-datasources", items: [{ key: "chant.yaml", path: "datasources/chant.yaml" }] } },
74
+ { configMap: { name: "grafana-dashboard-providers", items: [{ key: "chant.yaml", path: "dashboards/chant.yaml" }] } },
75
+ ],
76
+ },
77
+ },
78
+ { name: "grafana-dashboards-0", projected: { sources: [{ configMap: { name: "grafana-dashboard-top-level", items: [{ key: "Top_Level.json", path: "Top_Level.json" }] } }] } },
79
+ {
80
+ name: "grafana-dashboards-1",
81
+ projected: {
82
+ sources: [
83
+ { configMap: { name: "grafana-dashboard-red", items: [{ key: "red.json", path: "red.json" }] } },
84
+ { configMap: { name: "grafana-dashboard-slo", items: [{ key: "slo.json", path: "slo.json" }] } },
85
+ ],
86
+ },
87
+ },
88
+ ]);
89
+ });
90
+
91
+ test("a different dashboards path or volume name", () => {
92
+ const { volumeMounts } = grafanaVolumes({ entities: [red] }, { dashboardsPath: "/dash/", volumeName: "g" });
93
+ expect(volumeMounts.map((m) => [m.name, m.mountPath])).toEqual([
94
+ ["g-provisioning", "/etc/grafana/provisioning"],
95
+ ["g-dashboards-0", "/dash/Services"],
96
+ ]);
97
+ });
98
+ });