@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.
- package/README.md +15 -5
- package/dist/actions/grafana.d.ts +24 -0
- package/dist/actions/grafana.d.ts.map +1 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/alerting-build.d.ts +128 -0
- package/dist/alerting-build.d.ts.map +1 -0
- package/dist/alerting.d.ts +273 -0
- package/dist/alerting.d.ts.map +1 -0
- package/dist/api/apply.d.ts +156 -0
- package/dist/api/apply.d.ts.map +1 -0
- package/dist/api/bind.d.ts +32 -0
- package/dist/api/bind.d.ts.map +1 -0
- package/dist/api/client.d.ts +76 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/converge.d.ts +33 -0
- package/dist/api/converge.d.ts.map +1 -0
- package/dist/api/dashboards.d.ts +102 -0
- package/dist/api/dashboards.d.ts.map +1 -0
- package/dist/api/datasources.d.ts +26 -0
- package/dist/api/datasources.d.ts.map +1 -0
- package/dist/api/fake-grafana-writes.d.ts +41 -0
- package/dist/api/fake-grafana-writes.d.ts.map +1 -0
- package/dist/api/fake-grafana.d.ts +61 -0
- package/dist/api/fake-grafana.d.ts.map +1 -0
- package/dist/api/folders.d.ts +110 -0
- package/dist/api/folders.d.ts.map +1 -0
- package/dist/api/library-panels.d.ts +52 -0
- package/dist/api/library-panels.d.ts.map +1 -0
- package/dist/build.d.ts +30 -2
- package/dist/build.d.ts.map +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/schema-types.d.ts +3 -3
- package/dist/codegen/schema-types.d.ts.map +1 -1
- package/dist/composites/agent-dashboard.d.ts.map +1 -1
- package/dist/composites/catalog.d.ts.map +1 -1
- package/dist/composites/index.d.ts +4 -2
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/red-dashboard.d.ts +21 -2
- package/dist/composites/red-dashboard.d.ts.map +1 -1
- package/dist/composites/shared.d.ts +7 -0
- package/dist/composites/shared.d.ts.map +1 -1
- package/dist/composites/slo-alert-rules.d.ts +92 -0
- package/dist/composites/slo-alert-rules.d.ts.map +1 -0
- package/dist/config.d.ts +111 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/contact-point-secrets.d.ts +17 -0
- package/dist/contact-point-secrets.d.ts.map +1 -0
- package/dist/dashboard.d.ts +16 -2
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/datasource-refs.d.ts +95 -0
- package/dist/datasource-refs.d.ts.map +1 -0
- package/dist/datasource.d.ts +29 -0
- package/dist/datasource.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts +64 -0
- package/dist/deep-observe-hooks.d.ts.map +1 -0
- package/dist/deep-observe.d.ts +42 -0
- package/dist/deep-observe.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +49 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/detect.d.ts +28 -1
- package/dist/detect.d.ts.map +1 -1
- package/dist/export-resources.d.ts +38 -0
- package/dist/export-resources.d.ts.map +1 -0
- package/dist/import/alerting.d.ts +40 -0
- package/dist/import/alerting.d.ts.map +1 -0
- package/dist/import/edits.d.ts +47 -0
- package/dist/import/edits.d.ts.map +1 -0
- package/dist/import/embedded.d.ts +14 -0
- package/dist/import/embedded.d.ts.map +1 -0
- package/dist/import/generator.d.ts +60 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/live-export.d.ts +108 -0
- package/dist/import/live-export.d.ts.map +1 -0
- package/dist/import/mappings.d.ts +83 -0
- package/dist/import/mappings.d.ts.map +1 -0
- package/dist/import/model.d.ts +98 -0
- package/dist/import/model.d.ts.map +1 -0
- package/dist/import/normalize-alerting.d.ts +26 -0
- package/dist/import/normalize-alerting.d.ts.map +1 -0
- package/dist/import/normalize.d.ts +74 -0
- package/dist/import/normalize.d.ts.map +1 -0
- package/dist/import/parser.d.ts +110 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/import/report.d.ts +26 -0
- package/dist/import/report.d.ts.map +1 -0
- package/dist/import/testdata/fixtures.d.ts +35 -0
- package/dist/import/testdata/fixtures.d.ts.map +1 -0
- package/dist/import/v2.d.ts +72 -0
- package/dist/import/v2.d.ts.map +1 -0
- package/dist/index.d.ts +26 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +18 -10
- package/dist/k8s.d.ts +107 -0
- package/dist/k8s.d.ts.map +1 -0
- package/dist/lint/audit-catalog.d.ts +2 -1
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/graf101.d.ts +2 -2
- package/dist/lint/post-synth/graf102.d.ts +1 -1
- package/dist/lint/post-synth/graf107.d.ts +1 -1
- package/dist/lint/post-synth/graf108.d.ts +8 -0
- package/dist/lint/post-synth/graf108.d.ts.map +1 -0
- package/dist/lint/post-synth/graf109.d.ts +8 -0
- package/dist/lint/post-synth/graf109.d.ts.map +1 -0
- package/dist/lint/post-synth/graf111.d.ts +8 -0
- package/dist/lint/post-synth/graf111.d.ts.map +1 -0
- package/dist/lint/post-synth/graf112.d.ts +8 -0
- package/dist/lint/post-synth/graf112.d.ts.map +1 -0
- package/dist/lint/post-synth/graf113.d.ts +8 -0
- package/dist/lint/post-synth/graf113.d.ts.map +1 -0
- package/dist/lint/post-synth/graf114.d.ts +8 -0
- package/dist/lint/post-synth/graf114.d.ts.map +1 -0
- package/dist/lint/post-synth/graf115.d.ts +8 -0
- package/dist/lint/post-synth/graf115.d.ts.map +1 -0
- package/dist/lint/post-synth/grafana-helpers.d.ts +7 -5
- package/dist/lint/post-synth/grafana-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/rules/literal-secret.d.ts +7 -3
- package/dist/lint/rules/literal-secret.d.ts.map +1 -1
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/members.d.ts +27 -0
- package/dist/members.d.ts.map +1 -0
- package/dist/meta.json +135 -0
- package/dist/okf/index.md +36 -2
- package/dist/okf/rules/GRAF002.md +3 -2
- package/dist/okf/rules/GRAF101.md +3 -2
- package/dist/okf/rules/GRAF102.md +1 -0
- package/dist/okf/rules/GRAF108.md +11 -0
- package/dist/okf/rules/GRAF109.md +15 -0
- package/dist/okf/rules/GRAF111.md +11 -0
- package/dist/okf/rules/GRAF112.md +16 -0
- package/dist/okf/rules/GRAF113.md +16 -0
- package/dist/okf/rules/GRAF114.md +11 -0
- package/dist/okf/rules/GRAF115.md +11 -0
- package/dist/okf/types/AlertListPanel.md +9 -0
- package/dist/okf/types/AlertQuery.md +9 -0
- package/dist/okf/types/AlertRule.md +9 -0
- package/dist/okf/types/AlertRuleGroup.md +9 -0
- package/dist/okf/types/BarChartPanel.md +9 -0
- package/dist/okf/types/BarGaugePanel.md +9 -0
- package/dist/okf/types/CanvasPanel.md +9 -0
- package/dist/okf/types/ClassicConditionsExpression.md +9 -0
- package/dist/okf/types/ContactPoint.md +14 -0
- package/dist/okf/types/Dashboard.md +1 -0
- package/dist/okf/types/Datasource.md +3 -2
- package/dist/okf/types/ExternalDatasource.md +15 -0
- package/dist/okf/types/FlameGraphPanel.md +9 -0
- package/dist/okf/types/GeomapPanel.md +9 -0
- package/dist/okf/types/HistogramPanel.md +9 -0
- package/dist/okf/types/MathExpression.md +9 -0
- package/dist/okf/types/MuteTiming.md +13 -0
- package/dist/okf/types/NodeGraphPanel.md +9 -0
- package/dist/okf/types/NotificationPolicy.md +9 -0
- package/dist/okf/types/NotificationTemplate.md +9 -0
- package/dist/okf/types/PieChartPanel.md +9 -0
- package/dist/okf/types/ReduceExpression.md +9 -0
- package/dist/okf/types/ResampleExpression.md +9 -0
- package/dist/okf/types/SqlExpression.md +9 -0
- package/dist/okf/types/StateTimelinePanel.md +9 -0
- package/dist/okf/types/StatusHistoryPanel.md +9 -0
- package/dist/okf/types/ThresholdExpression.md +9 -0
- package/dist/okf/types/TrendPanel.md +9 -0
- package/dist/okf/types/XYChartPanel.md +9 -0
- package/dist/op/activities/grafana-apply.d.ts +75 -0
- package/dist/op/activities/grafana-apply.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +9 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/builders.d.ts +24 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/ownership.d.ts +67 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/panel-options.d.ts +65 -0
- package/dist/panel-options.d.ts.map +1 -0
- package/dist/panels.d.ts +46 -9
- package/dist/panels.d.ts.map +1 -1
- package/dist/pin.d.ts +29 -7
- package/dist/pin.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/promql-check.d.ts +51 -0
- package/dist/promql-check.d.ts.map +1 -0
- package/dist/query.d.ts +9 -8
- package/dist/query.d.ts.map +1 -1
- package/dist/rules/graf101.ts +3 -3
- package/dist/rules/graf102.ts +1 -1
- package/dist/rules/graf107.ts +1 -1
- package/dist/rules/graf108.ts +17 -0
- package/dist/rules/graf109.ts +17 -0
- package/dist/rules/graf111.ts +17 -0
- package/dist/rules/graf112.ts +17 -0
- package/dist/rules/graf113.ts +17 -0
- package/dist/rules/graf114.ts +17 -0
- package/dist/rules/graf115.ts +17 -0
- package/dist/rules/grafana-helpers.ts +15 -8
- package/dist/rules/literal-secret.ts +60 -6
- package/dist/schema/barchart.gen.d.ts +198 -0
- package/dist/schema/barchart.gen.d.ts.map +1 -0
- package/dist/schema/bargauge.gen.d.ts +97 -0
- package/dist/schema/bargauge.gen.d.ts.map +1 -0
- package/dist/schema/canvas.gen.d.ts +152 -0
- package/dist/schema/canvas.gen.d.ts.map +1 -0
- package/dist/schema/dashboard.gen.d.ts +196 -21
- package/dist/schema/dashboard.gen.d.ts.map +1 -1
- package/dist/schema/expr.gen.d.ts +606 -0
- package/dist/schema/expr.gen.d.ts.map +1 -0
- package/dist/schema/gauge.gen.d.ts.map +1 -1
- package/dist/schema/geomap.gen.d.ts +100 -0
- package/dist/schema/geomap.gen.d.ts.map +1 -0
- package/dist/schema/heatmap.gen.d.ts.map +1 -1
- package/dist/schema/histogram.gen.d.ts +132 -0
- package/dist/schema/histogram.gen.d.ts.map +1 -0
- package/dist/schema/index.d.ts +12 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/logs.gen.d.ts +1 -0
- package/dist/schema/logs.gen.d.ts.map +1 -1
- package/dist/schema/loki.gen.d.ts.map +1 -1
- package/dist/schema/nodegraph.gen.d.ts +49 -0
- package/dist/schema/nodegraph.gen.d.ts.map +1 -0
- package/dist/schema/piechart.gen.d.ts +127 -0
- package/dist/schema/piechart.gen.d.ts.map +1 -0
- package/dist/schema/prometheus.gen.d.ts.map +1 -1
- package/dist/schema/stat.gen.d.ts.map +1 -1
- package/dist/schema/statetimeline.gen.d.ts +124 -0
- package/dist/schema/statetimeline.gen.d.ts.map +1 -0
- package/dist/schema/statushistory.gen.d.ts +114 -0
- package/dist/schema/statushistory.gen.d.ts.map +1 -0
- package/dist/schema/table.gen.d.ts +22 -3
- package/dist/schema/table.gen.d.ts.map +1 -1
- package/dist/schema/tempo.gen.d.ts.map +1 -1
- package/dist/schema/text.gen.d.ts.map +1 -1
- package/dist/schema/timeseries.gen.d.ts +5 -1
- package/dist/schema/timeseries.gen.d.ts.map +1 -1
- package/dist/schema/trend.gen.d.ts +180 -0
- package/dist/schema/trend.gen.d.ts.map +1 -0
- package/dist/schema/xychart.gen.d.ts +145 -0
- package/dist/schema/xychart.gen.d.ts.map +1 -0
- package/dist/schema-validate.d.ts +30 -4
- package/dist/schema-validate.d.ts.map +1 -1
- package/dist/serializer.d.ts +11 -3
- package/dist/serializer.d.ts.map +1 -1
- package/dist/skill-defs.d.ts.map +1 -1
- package/dist/skills/chant-grafana-alerting.md +49 -0
- package/dist/skills/chant-grafana-provisioning.md +3 -3
- package/dist/skills/chant-grafana.md +12 -6
- package/dist/spec/fetch-units-cli.d.ts +3 -0
- package/dist/spec/fetch-units-cli.d.ts.map +1 -0
- package/dist/spec/overlay.d.ts +43 -0
- package/dist/spec/overlay.d.ts.map +1 -0
- package/dist/spec/schemas.d.ts +13 -7
- package/dist/spec/schemas.d.ts.map +1 -1
- package/dist/spec/units.d.ts +35 -0
- package/dist/spec/units.d.ts.map +1 -0
- package/dist/spec/units.gen.d.ts +2 -0
- package/dist/spec/units.gen.d.ts.map +1 -0
- package/dist/transformations.d.ts +459 -0
- package/dist/transformations.d.ts.map +1 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/validate-alerting.d.ts +65 -0
- package/dist/validate-alerting.d.ts.map +1 -0
- package/dist/validate-output.d.ts +49 -9
- package/dist/validate-output.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/variables.d.ts +7 -2
- package/dist/variables.d.ts.map +1 -1
- package/package.json +12 -4
- package/src/actions/actions.test.ts +24 -0
- package/src/actions/grafana.ts +40 -0
- package/src/actions/index.ts +1 -0
- package/src/alerting-build.ts +376 -0
- package/src/alerting.e2e.test.ts +212 -0
- package/src/alerting.test.ts +272 -0
- package/src/alerting.ts +435 -0
- package/src/api/apply.test.ts +238 -0
- package/src/api/apply.ts +384 -0
- package/src/api/bind.ts +58 -0
- package/src/api/client.ts +148 -0
- package/src/api/converge.ts +63 -0
- package/src/api/dashboards.ts +247 -0
- package/src/api/datasources.ts +33 -0
- package/src/api/fake-grafana-writes.ts +201 -0
- package/src/api/fake-grafana.ts +186 -0
- package/src/api/folders.ts +265 -0
- package/src/api/library-panels.ts +101 -0
- package/src/build.test.ts +210 -5
- package/src/build.ts +180 -43
- package/src/catalog.ts +86 -2
- package/src/codegen/docs.ts +42 -9
- package/src/codegen/schema-types.ts +16 -6
- package/src/composites/agent-dashboard.ts +6 -3
- package/src/composites/catalog.test.ts +2 -2
- package/src/composites/catalog.ts +82 -0
- package/src/composites/composites.test.ts +73 -9
- package/src/composites/index.ts +4 -2
- package/src/composites/queries.e2e.test.ts +374 -0
- package/src/composites/red-dashboard.ts +83 -12
- package/src/composites/shared.ts +11 -0
- package/src/composites/slo-alert-rules.ts +149 -0
- package/src/config.test.ts +61 -0
- package/src/config.ts +138 -0
- package/src/contact-point-secrets.ts +63 -0
- package/src/dashboard.ts +16 -2
- package/src/datasource-refs.test.ts +56 -0
- package/src/datasource-refs.ts +189 -0
- package/src/datasource.ts +52 -0
- package/src/deep-observe-hooks.ts +252 -0
- package/src/deep-observe.test.ts +265 -0
- package/src/deep-observe.ts +164 -0
- package/src/describe-resources.test.ts +255 -0
- package/src/describe-resources.ts +209 -0
- package/src/detect.ts +79 -2
- package/src/export-resources.test.ts +129 -0
- package/src/export-resources.ts +84 -0
- package/src/generated/lexicon-grafana.json +135 -0
- package/src/import/alerting-roundtrip.test.ts +161 -0
- package/src/import/alerting.ts +500 -0
- package/src/import/edits.ts +95 -0
- package/src/import/embedded.test.ts +45 -0
- package/src/import/embedded.ts +47 -0
- package/src/import/generated-types.e2e.test.ts +118 -0
- package/src/import/generator.test.ts +210 -0
- package/src/import/generator.ts +462 -0
- package/src/import/live-export.ts +264 -0
- package/src/import/mappings.ts +264 -0
- package/src/import/model.ts +110 -0
- package/src/import/normalize-alerting.ts +101 -0
- package/src/import/normalize.test.ts +117 -0
- package/src/import/normalize.ts +364 -0
- package/src/import/parser.test.ts +440 -0
- package/src/import/parser.ts +1236 -0
- package/src/import/report.ts +62 -0
- package/src/import/roundtrip.test.ts +468 -0
- package/src/import/testdata/fixtures.ts +129 -0
- package/src/import/v2.test.ts +292 -0
- package/src/import/v2.ts +803 -0
- package/src/import.e2e.test.ts +31 -84
- package/src/import.test.ts +9 -3
- package/src/index.ts +190 -5
- package/src/k8s.test.ts +98 -0
- package/src/k8s.ts +197 -0
- package/src/lint/audit-catalog.ts +60 -3
- package/src/lint/core-lint.test.ts +43 -0
- package/src/lint/post-synth/graf101.ts +3 -3
- package/src/lint/post-synth/graf102.ts +1 -1
- package/src/lint/post-synth/graf107.ts +1 -1
- package/src/lint/post-synth/graf108.ts +17 -0
- package/src/lint/post-synth/graf109.ts +17 -0
- package/src/lint/post-synth/graf111.ts +17 -0
- package/src/lint/post-synth/graf112.ts +17 -0
- package/src/lint/post-synth/graf113.ts +17 -0
- package/src/lint/post-synth/graf114.ts +17 -0
- package/src/lint/post-synth/graf115.ts +17 -0
- package/src/lint/post-synth/grafana-helpers.ts +15 -8
- package/src/lint/post-synth/index.ts +14 -0
- package/src/lint/post-synth/post-synth.test.ts +235 -11
- package/src/lint/rules/literal-secret.ts +60 -6
- package/src/lint/rules/rules.test.ts +27 -0
- package/src/lsp/completions.test.ts +5 -0
- package/src/lsp/hover.test.ts +7 -0
- package/src/lsp/hover.ts +8 -1
- package/src/members.ts +68 -0
- package/src/observe.e2e.test.ts +255 -0
- package/src/op/activities/grafana-apply.e2e.test.ts +175 -0
- package/src/op/activities/grafana-apply.test.ts +229 -0
- package/src/op/activities/grafana-apply.ts +171 -0
- package/src/op/activities/index.ts +8 -0
- package/src/op/builders.test.ts +40 -0
- package/src/op/builders.ts +34 -0
- package/src/ownership.ts +103 -0
- package/src/panel-options.ts +66 -0
- package/src/panels.test.ts +51 -0
- package/src/panels.ts +148 -10
- package/src/pin.ts +57 -6
- package/src/plugin.test.ts +5 -2
- package/src/plugin.ts +66 -0
- package/src/promql-check.test.ts +108 -0
- package/src/promql-check.ts +189 -0
- package/src/query.ts +13 -8
- package/src/schema/barchart.gen.ts +221 -0
- package/src/schema/bargauge.gen.ts +111 -0
- package/src/schema/canvas.gen.ts +176 -0
- package/src/schema/dashboard.gen.ts +206 -22
- package/src/schema/expr.gen.ts +618 -0
- package/src/schema/gauge.gen.ts +2 -1
- package/src/schema/geomap.gen.ts +112 -0
- package/src/schema/heatmap.gen.ts +2 -1
- package/src/schema/histogram.gen.ts +151 -0
- package/src/schema/index.ts +12 -0
- package/src/schema/logs.gen.ts +3 -1
- package/src/schema/loki.gen.ts +2 -1
- package/src/schema/nodegraph.gen.ts +58 -0
- package/src/schema/piechart.gen.ts +146 -0
- package/src/schema/prometheus.gen.ts +2 -1
- package/src/schema/stat.gen.ts +2 -1
- package/src/schema/statetimeline.gen.ts +143 -0
- package/src/schema/statushistory.gen.ts +132 -0
- package/src/schema/table.gen.ts +27 -4
- package/src/schema/tempo.gen.ts +2 -1
- package/src/schema/text.gen.ts +2 -1
- package/src/schema/timeseries.gen.ts +7 -2
- package/src/schema/trend.gen.ts +208 -0
- package/src/schema/xychart.gen.ts +167 -0
- package/src/schema-overlay.test.ts +282 -0
- package/src/schema-validate.ts +141 -50
- package/src/schema.test.ts +4 -2
- package/src/serializer.test.ts +13 -1
- package/src/serializer.ts +11 -3
- package/src/skill-defs.ts +17 -0
- package/src/skills/chant-grafana-alerting.md +49 -0
- package/src/skills/chant-grafana-provisioning.md +3 -3
- package/src/skills/chant-grafana.md +12 -6
- package/src/spec/fetch-cli.ts +3 -3
- package/src/spec/fetch-units-cli.ts +37 -0
- package/src/spec/overlay/canvas.overlay.json +142 -0
- package/src/spec/overlay/dashboard.overlay.json +378 -0
- package/src/spec/overlay/expr.overlay.json +14 -0
- package/src/spec/overlay/geomap.overlay.json +35 -0
- package/src/spec/overlay/logs.overlay.json +14 -0
- package/src/spec/overlay/nodegraph.overlay.json +31 -0
- package/src/spec/overlay/piechart.overlay.json +17 -0
- package/src/spec/overlay/table.overlay.json +84 -0
- package/src/spec/overlay/timeseries.overlay.json +21 -0
- package/src/spec/overlay/trend.overlay.json +17 -0
- package/src/spec/overlay/xychart.overlay.json +16 -0
- package/src/spec/overlay.ts +105 -0
- package/src/spec/schemas/barchart.jsonschema.json +419 -0
- package/src/spec/schemas/bargauge.jsonschema.json +224 -0
- package/src/spec/schemas/canvas.jsonschema.json +356 -0
- package/src/spec/schemas/dashboardv2.jsonschema.json +3387 -0
- package/src/spec/schemas/expr.jsonschema.json +987 -0
- package/src/spec/schemas/geomap.jsonschema.json +220 -0
- package/src/spec/schemas/histogram.jsonschema.json +308 -0
- package/src/spec/schemas/nodegraph.jsonschema.json +77 -0
- package/src/spec/schemas/piechart.jsonschema.json +275 -0
- package/src/spec/schemas/statetimeline.jsonschema.json +324 -0
- package/src/spec/schemas/statushistory.jsonschema.json +292 -0
- package/src/spec/schemas/trend.jsonschema.json +449 -0
- package/src/spec/schemas/xychart.jsonschema.json +428 -0
- package/src/spec/schemas.ts +22 -10
- package/src/spec/units.gen.ts +281 -0
- package/src/spec/units.ts +77 -0
- package/src/transformations.test.ts +65 -0
- package/src/transformations.ts +574 -0
- package/src/util.ts +8 -0
- package/src/validate-alerting.test.ts +255 -0
- package/src/validate-alerting.ts +428 -0
- package/src/validate-output.ts +332 -101
- package/src/validate.ts +14 -0
- package/src/variables.ts +7 -2
package/src/build.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { dump } from "js-yaml";
|
|
12
12
|
import type { Declarable } from "@intentius/chant/declarable";
|
|
13
|
-
import { isDatasourceEntity, type DatasourceEntity, type DatasourceRef } from "./datasource";
|
|
13
|
+
import { isDatasourceDeclaration, isDatasourceEntity, isExternalDatasource, type DatasourceEntity, type DatasourceRef, type ExternalDatasourceEntity } from "./datasource";
|
|
14
14
|
import {
|
|
15
15
|
isDashboardEntity,
|
|
16
16
|
isDashboardProviderEntity,
|
|
@@ -34,6 +34,8 @@ import type {
|
|
|
34
34
|
import { DASHBOARD_SCHEMA_VERSION } from "./schema/dashboard.gen";
|
|
35
35
|
import { GRAFANA_SCHEMA_PIN } from "./pin";
|
|
36
36
|
import { compact, slugUid } from "./util";
|
|
37
|
+
import { ALERTING_FILE, alertingYaml, buildAlerting, type AlertingFile, type AlertingIndex } from "./alerting-build";
|
|
38
|
+
import { DEFAULT_PROVIDER_NAME } from "./ownership";
|
|
37
39
|
|
|
38
40
|
export type { DashboardJson, PanelJson, RowPanelJson, VariableModel, DataSourceRef };
|
|
39
41
|
|
|
@@ -90,7 +92,11 @@ export interface BuiltDashboard {
|
|
|
90
92
|
export interface BuiltGrafana {
|
|
91
93
|
dashboards: BuiltDashboard[];
|
|
92
94
|
datasources: ProvisionedDatasource[];
|
|
95
|
+
/** `ExternalDatasource` declarations: the checks count them, the provisioning file leaves them out. */
|
|
96
|
+
externalDatasources: ExternalDatasourceRecord[];
|
|
93
97
|
providers: ProvisionedProvider[];
|
|
98
|
+
/** The alerting provisioning file, when the build declares any alerting. */
|
|
99
|
+
alerting?: AlertingFile;
|
|
94
100
|
/** Every output file by path, ready to write. */
|
|
95
101
|
files: Record<string, string>;
|
|
96
102
|
/** A short summary of what was built: the serializer's primary output. */
|
|
@@ -101,9 +107,20 @@ export interface GrafanaIndex {
|
|
|
101
107
|
grafanaSchema: string;
|
|
102
108
|
dashboards: Array<{ uid: string; title: string; folder?: string; file: string }>;
|
|
103
109
|
datasources: Array<{ name: string; type: string; uid: string }>;
|
|
110
|
+
/** Datasources declared with `ExternalDatasource`: referenced, never provisioned. */
|
|
111
|
+
externalDatasources?: ExternalDatasourceRecord[];
|
|
112
|
+
/** Rule groups, contact points, policies, mute timings and templates, when the build declares any. */
|
|
113
|
+
alerting?: AlertingIndex;
|
|
104
114
|
files: string[];
|
|
105
115
|
}
|
|
106
116
|
|
|
117
|
+
/** An `ExternalDatasource` as the checks and the index see it. */
|
|
118
|
+
export interface ExternalDatasourceRecord {
|
|
119
|
+
type: string;
|
|
120
|
+
uid: string;
|
|
121
|
+
name?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
107
124
|
// ── References ──────────────────────────────────────────────────
|
|
108
125
|
|
|
109
126
|
function isRef(value: unknown): value is DatasourceRef {
|
|
@@ -116,13 +133,13 @@ function isRef(value: unknown): value is DatasourceRef {
|
|
|
116
133
|
);
|
|
117
134
|
}
|
|
118
135
|
|
|
119
|
-
/** The `{ type, uid }` Grafana stores for a datasource, a datasource variable, or a ref. */
|
|
136
|
+
/** The `{ type, uid }` Grafana stores for a datasource (declared or external), a datasource variable, or a ref. */
|
|
120
137
|
export function datasourceRef(input: DatasourceInput | undefined): DataSourceRef | undefined {
|
|
121
138
|
if (input === undefined || input === null) return undefined;
|
|
122
|
-
if (
|
|
139
|
+
if (isDatasourceDeclaration(input)) return { type: input.datasourceType, uid: input.uid };
|
|
123
140
|
if (isDatasourceVariable(input)) return { type: input.pluginType, uid: `\${${input.variableName}}` };
|
|
124
141
|
if (isRef(input)) return { type: input.type, uid: input.uid };
|
|
125
|
-
throw new Error("grafana: a datasource must be a Datasource, a DatasourceVariable or a { type, uid } ref");
|
|
142
|
+
throw new Error("grafana: a datasource must be a Datasource, an ExternalDatasource, a DatasourceVariable or a { type, uid } ref");
|
|
126
143
|
}
|
|
127
144
|
|
|
128
145
|
function sameRef(a: DataSourceRef | undefined, b: DataSourceRef | undefined): boolean {
|
|
@@ -154,8 +171,24 @@ function dashboardLink(link: DashboardLinkInput | PanelLink): DashboardLink {
|
|
|
154
171
|
const HIDE: Record<VariableHide, 0 | 1 | 2> = { label: 0, valueOnly: 1, hidden: 2 };
|
|
155
172
|
const REFRESH = { never: 0, onLoad: 1, onTimeRangeChange: 2 } as const;
|
|
156
173
|
|
|
174
|
+
/** A custom variable value in its `query`: a comma is escaped as `\\,`, and one already escaped is left alone. */
|
|
157
175
|
function escapeCustom(v: string): string {
|
|
158
|
-
return v.replace(
|
|
176
|
+
return v.replace(/\\?,/g, "\\,");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The options Grafana makes of a custom variable's `query`: split on commas
|
|
181
|
+
* not escaped as `\\,`, and `text : value` (spaces around the colon) read as
|
|
182
|
+
* an option showing `text` that sets `value`. The same parse as Grafana's
|
|
183
|
+
* custom variable (public/app/features/variables/custom/reducer.ts, and
|
|
184
|
+
* CustomVariable in @grafana/scenes).
|
|
185
|
+
*/
|
|
186
|
+
export function customVariableOptions(query: string): Array<{ text: string; value: string }> {
|
|
187
|
+
return (query.match(/(?:\\,|[^,])+/g) ?? []).map((item) => {
|
|
188
|
+
const text = item.replace(/\\,/g, ",");
|
|
189
|
+
const kv = /^\s*(.+)\s:\s(.+)$/.exec(text);
|
|
190
|
+
return kv ? { text: kv[1].trim(), value: kv[2].trim() } : { text: text.trim(), value: text.trim() };
|
|
191
|
+
});
|
|
159
192
|
}
|
|
160
193
|
|
|
161
194
|
function optionsOf(values: string[], current?: { text: string | string[]; value: string | string[] }) {
|
|
@@ -192,13 +225,16 @@ export function variableModel(variable: VariableEntity): VariableModel {
|
|
|
192
225
|
}) as VariableModel;
|
|
193
226
|
case "custom": {
|
|
194
227
|
const values: string[] = p.values;
|
|
195
|
-
const
|
|
228
|
+
const query = values.map(escapeCustom).join(",");
|
|
229
|
+
const options = customVariableOptions(query);
|
|
230
|
+
const current = p.current ?? (options[0] !== undefined ? { text: options[0].text, value: options[0].value } : undefined);
|
|
231
|
+
const selected = current ? (Array.isArray(current.value) ? current.value : [current.value]) : [];
|
|
196
232
|
return compact({
|
|
197
233
|
...common,
|
|
198
|
-
query
|
|
234
|
+
query,
|
|
199
235
|
...multi,
|
|
200
236
|
current,
|
|
201
|
-
options:
|
|
237
|
+
options: options.map((o) => ({ selected: selected.includes(o.value), ...o })),
|
|
202
238
|
}) as VariableModel;
|
|
203
239
|
}
|
|
204
240
|
case "interval": {
|
|
@@ -266,47 +302,110 @@ export function targetJson(query: QueryEntity, index: number, panelDatasource?:
|
|
|
266
302
|
});
|
|
267
303
|
}
|
|
268
304
|
|
|
305
|
+
/** A bitmask of grid columns [x, x + w), clipped to the grid. */
|
|
306
|
+
function columns(x: number, w: number): number {
|
|
307
|
+
const from = Math.max(0, x);
|
|
308
|
+
const to = Math.min(GRID_COLUMNS, x + w);
|
|
309
|
+
return to > from ? ((1 << (to - from)) - 1) << from : 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Places panels on Grafana's grid. It keeps an occupancy grid, so a panel
|
|
314
|
+
* placed automatically never lands on one placed explicitly (reserved up
|
|
315
|
+
* front) or on one placed before it.
|
|
316
|
+
*/
|
|
269
317
|
class Layout {
|
|
270
318
|
private x = 0;
|
|
271
|
-
private y
|
|
319
|
+
private y: number;
|
|
272
320
|
private lineH = 0;
|
|
273
|
-
private bottom
|
|
321
|
+
private bottom: number;
|
|
322
|
+
/** Taken cells: one column bitmask per grid line. */
|
|
323
|
+
private readonly taken: number[] = [];
|
|
324
|
+
|
|
325
|
+
constructor(top = 0) {
|
|
326
|
+
this.y = top;
|
|
327
|
+
this.bottom = top;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Marks the cells of a panel with both `x` and `y` as taken, before anything is placed. */
|
|
331
|
+
reserve(gp: Partial<GridPos> | undefined, size: { w: number; h: number }): void {
|
|
332
|
+
if (gp?.x === undefined || gp?.y === undefined) return;
|
|
333
|
+
this.take(gp.x, gp.y, gp.w ?? size.w, gp.h ?? size.h);
|
|
334
|
+
}
|
|
274
335
|
|
|
275
336
|
place(gp: Partial<GridPos> | undefined, size: { w: number; h: number }): GridPos {
|
|
276
337
|
const w = gp?.w ?? size.w;
|
|
277
338
|
const h = gp?.h ?? size.h;
|
|
278
339
|
if (gp?.x !== undefined && gp?.y !== undefined) {
|
|
340
|
+
this.take(gp.x, gp.y, w, h);
|
|
279
341
|
this.bottom = Math.max(this.bottom, gp.y + h);
|
|
280
342
|
return { h, w, x: gp.x, y: gp.y };
|
|
281
343
|
}
|
|
282
|
-
if (
|
|
283
|
-
|
|
344
|
+
if (gp?.y !== undefined) {
|
|
345
|
+
// Only `y`: the first free column on that line, else on the first line below with room.
|
|
346
|
+
for (let y = Math.max(0, gp.y); ; y++) {
|
|
347
|
+
const x = this.freeColumn(0, y, w, h);
|
|
348
|
+
if (x === undefined) continue;
|
|
349
|
+
this.take(x, y, w, h);
|
|
350
|
+
this.bottom = Math.max(this.bottom, y + h);
|
|
351
|
+
return { h, w, x, y };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Flow: the first free spot on the current line after the previous panel, else the next line.
|
|
355
|
+
// Only `x`: that column, on the first line where it is free.
|
|
356
|
+
for (;;) {
|
|
357
|
+
const x =
|
|
358
|
+
gp?.x === undefined
|
|
359
|
+
? this.freeColumn(this.x, this.y, w, h)
|
|
360
|
+
: gp.x >= this.x && this.free(gp.x, this.y, w, h)
|
|
361
|
+
? gp.x
|
|
362
|
+
: undefined;
|
|
363
|
+
if (x !== undefined) {
|
|
364
|
+
const pos = { h, w, x, y: this.y };
|
|
365
|
+
this.take(x, this.y, w, h);
|
|
366
|
+
this.x = x + w;
|
|
367
|
+
this.lineH = Math.max(this.lineH, h);
|
|
368
|
+
this.bottom = Math.max(this.bottom, this.y + h);
|
|
369
|
+
return pos;
|
|
370
|
+
}
|
|
371
|
+
this.y += this.lineH || 1;
|
|
284
372
|
this.x = 0;
|
|
285
373
|
this.lineH = 0;
|
|
286
374
|
}
|
|
287
|
-
const pos = { h, w, x: this.x, y: this.y };
|
|
288
|
-
this.x += w;
|
|
289
|
-
this.lineH = Math.max(this.lineH, h);
|
|
290
|
-
this.bottom = Math.max(this.bottom, this.y + h);
|
|
291
|
-
return pos;
|
|
292
375
|
}
|
|
293
376
|
|
|
294
|
-
/**
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
377
|
+
/** Marks the line of a row header with an explicit `y` as taken, before anything is placed. */
|
|
378
|
+
reserveRow(y: number | undefined): void {
|
|
379
|
+
if (y !== undefined) this.take(0, y, GRID_COLUMNS, 1);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** A full-width row header on line `explicitY`, else on the first free line below everything placed so far. */
|
|
383
|
+
row(explicitY?: number): GridPos {
|
|
384
|
+
let y = explicitY ?? Math.max(this.bottom, this.y + this.lineH);
|
|
385
|
+
if (explicitY === undefined) while (!this.free(0, y, GRID_COLUMNS, 1)) y++;
|
|
386
|
+
this.take(0, y, GRID_COLUMNS, 1);
|
|
298
387
|
this.x = 0;
|
|
299
388
|
this.y = y + 1;
|
|
300
389
|
this.lineH = 0;
|
|
301
390
|
this.bottom = y + 1;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
391
|
+
return { h: 1, w: GRID_COLUMNS, x: 0, y };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** The first column at or after `from` where a w×h panel fits on line `y`. A panel wider than the grid fits only at 0. */
|
|
395
|
+
private freeColumn(from: number, y: number, w: number, h: number): number | undefined {
|
|
396
|
+
for (let x = from; x === 0 || x + w <= GRID_COLUMNS; x++) if (this.free(x, y, w, h)) return x;
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private free(x: number, y: number, w: number, h: number): boolean {
|
|
401
|
+
const mask = columns(x, w);
|
|
402
|
+
for (let line = Math.max(0, y); line < y + h; line++) if ((this.taken[line] ?? 0) & mask) return false;
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private take(x: number, y: number, w: number, h: number): void {
|
|
407
|
+
const mask = columns(x, w);
|
|
408
|
+
for (let line = Math.max(0, y); line < y + h; line++) this.taken[line] = (this.taken[line] ?? 0) | mask;
|
|
310
409
|
}
|
|
311
410
|
}
|
|
312
411
|
|
|
@@ -334,7 +433,8 @@ function panelJson(panel: PanelEntity, gridPos: GridPos, id: number, inherited?:
|
|
|
334
433
|
const def = panel.panelDefinition;
|
|
335
434
|
const own = datasourceRef(p.datasource) ?? inherited;
|
|
336
435
|
const queries = (p.targets ?? []).filter(isQueryEntity);
|
|
337
|
-
|
|
436
|
+
// A query under a Mixed panel names its own datasource; one that names none goes to the default, not to Mixed.
|
|
437
|
+
const targets = queries.map((q, i) => targetJson(q, i, sameRef(own, MIXED) ? undefined : own));
|
|
338
438
|
const refs = targets.map((t) => t.datasource as DataSourceRef | undefined);
|
|
339
439
|
let datasource = own;
|
|
340
440
|
if (!datasource && refs.length > 0) {
|
|
@@ -374,19 +474,29 @@ function panelJson(panel: PanelEntity, gridPos: GridPos, id: number, inherited?:
|
|
|
374
474
|
/** A dashboard's `panels` array: rows and panels, laid out, with ids. */
|
|
375
475
|
export function panelsJson(items: Array<PanelEntity | RowEntity>): Array<PanelJson | RowPanelJson> {
|
|
376
476
|
const layout = new Layout();
|
|
477
|
+
// Panels on the dashboard's grid with both x and y, and rows with a y, take their cells first; a collapsed row's panels are laid out on their own.
|
|
478
|
+
for (const item of items) {
|
|
479
|
+
if (isRowEntity(item)) layout.reserveRow(item.props.gridPos?.y);
|
|
480
|
+
const onGrid = isRowEntity(item) ? (item.props.collapsed ? [] : (item.props.panels ?? [])) : [item];
|
|
481
|
+
for (const p of onGrid.filter(isPanelEntity)) layout.reserve(p.props.gridPos, p.panelDefinition.defaultSize);
|
|
482
|
+
}
|
|
377
483
|
const ids = new Ids(explicitIds(items));
|
|
378
484
|
const out: Array<PanelJson | RowPanelJson> = [];
|
|
379
485
|
for (const item of items) {
|
|
380
486
|
if (isRowEntity(item)) {
|
|
381
487
|
const r = item.props;
|
|
382
|
-
const
|
|
488
|
+
const pos = layout.row(r.gridPos?.y);
|
|
383
489
|
const rowId = ids.take(r.id);
|
|
384
490
|
const rowRef = datasourceRef(r.datasource);
|
|
385
|
-
const
|
|
386
|
-
|
|
491
|
+
const collapsed = r.collapsed ?? false;
|
|
492
|
+
// A collapsed row's panels sit under its header on a grid of their own; the next item starts right under the header.
|
|
493
|
+
const rowLayout = collapsed ? new Layout(pos.y + 1) : layout;
|
|
494
|
+
const panels = (r.panels ?? []).filter(isPanelEntity);
|
|
495
|
+
if (collapsed) for (const p of panels) rowLayout.reserve(p.props.gridPos, p.panelDefinition.defaultSize);
|
|
496
|
+
const children = panels.map((p) => {
|
|
497
|
+
const gp = rowLayout.place(p.props.gridPos, p.panelDefinition.defaultSize);
|
|
387
498
|
return panelJson(p, gp, ids.take(p.props.id), rowRef);
|
|
388
499
|
});
|
|
389
|
-
const collapsed = r.collapsed ?? false;
|
|
390
500
|
out.push(
|
|
391
501
|
compact({
|
|
392
502
|
type: "row" as const,
|
|
@@ -399,8 +509,7 @@ export function panelsJson(items: Array<PanelEntity | RowEntity>): Array<PanelJs
|
|
|
399
509
|
repeat: repeatName(r.repeat),
|
|
400
510
|
}),
|
|
401
511
|
);
|
|
402
|
-
if (collapsed)
|
|
403
|
-
else out.push(...children);
|
|
512
|
+
if (!collapsed) out.push(...children);
|
|
404
513
|
} else if (isPanelEntity(item)) {
|
|
405
514
|
const gp = layout.place(item.props.gridPos, item.panelDefinition.defaultSize);
|
|
406
515
|
out.push(panelJson(item, gp, ids.take(item.props.id)));
|
|
@@ -431,7 +540,7 @@ export function renderDashboard(dashboard: DashboardEntity, exportName?: string)
|
|
|
431
540
|
liveNow: p.liveNow,
|
|
432
541
|
panels: panelsJson(p.panels ?? []),
|
|
433
542
|
refresh: p.refresh,
|
|
434
|
-
schemaVersion: DASHBOARD_SCHEMA_VERSION,
|
|
543
|
+
schemaVersion: p.schemaVersion ?? DASHBOARD_SCHEMA_VERSION,
|
|
435
544
|
tags: p.tags ?? [],
|
|
436
545
|
templating: { list: (p.variables ?? []).filter(isVariableEntity).map(variableModel) },
|
|
437
546
|
time: p.time ?? { from: "now-6h", to: "now" },
|
|
@@ -450,9 +559,9 @@ export function dashboardJson(dashboard: DashboardEntity, exportName?: string):
|
|
|
450
559
|
|
|
451
560
|
// ── Datasources and providers ───────────────────────────────────
|
|
452
561
|
|
|
453
|
-
/** Replace every declared
|
|
562
|
+
/** Replace every declared or external datasource inside plugin settings with its uid. */
|
|
454
563
|
function resolveNested(value: unknown): unknown {
|
|
455
|
-
if (
|
|
564
|
+
if (isDatasourceDeclaration(value)) return value.uid;
|
|
456
565
|
if (Array.isArray(value)) return value.map(resolveNested);
|
|
457
566
|
if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
458
567
|
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, resolveNested(v)]));
|
|
@@ -483,6 +592,11 @@ export function provisionedDatasource(ds: DatasourceEntity): ProvisionedDatasour
|
|
|
483
592
|
});
|
|
484
593
|
}
|
|
485
594
|
|
|
595
|
+
/** One `ExternalDatasource` as the checks and the index see it. */
|
|
596
|
+
export function externalDatasourceRecord(ds: ExternalDatasourceEntity): ExternalDatasourceRecord {
|
|
597
|
+
return compact({ type: ds.datasourceType, uid: ds.uid, name: ds.props.name });
|
|
598
|
+
}
|
|
599
|
+
|
|
486
600
|
/** One dashboard provider as a provisioning entry. */
|
|
487
601
|
export function provisionedProvider(p: DashboardProviderProps): ProvisionedProvider {
|
|
488
602
|
const pinnedFolder = p.folder !== undefined || p.folderUid !== undefined;
|
|
@@ -516,8 +630,21 @@ export function dashboardProvidersYaml(providers: ProvisionedProvider[]): string
|
|
|
516
630
|
return yamlFile("Grafana dashboard provisioning, generated by chant.", { apiVersion: 1, providers });
|
|
517
631
|
}
|
|
518
632
|
|
|
633
|
+
/**
|
|
634
|
+
* A dashboard's `folder` as its directory under `dashboards/`: one
|
|
635
|
+
* subdirectory per `/`-separated level, which a provider with
|
|
636
|
+
* `foldersFromFilesStructure` turns into nested folders on Grafana 13.1 and
|
|
637
|
+
* later (GRAF109 says what earlier versions do). Leading dots and blank
|
|
638
|
+
* levels are dropped, so a folder can't name a directory outside
|
|
639
|
+
* `dashboards/`; a backslash becomes `-`. Empty when nothing is left: the
|
|
640
|
+
* General folder.
|
|
641
|
+
*/
|
|
519
642
|
function folderDir(folder: string): string {
|
|
520
|
-
return folder
|
|
643
|
+
return folder
|
|
644
|
+
.split("/")
|
|
645
|
+
.map((level) => level.replace(/\\+/g, "-").trim().replace(/^\.+/, "").trim())
|
|
646
|
+
.filter((level) => level !== "")
|
|
647
|
+
.join("/");
|
|
521
648
|
}
|
|
522
649
|
|
|
523
650
|
// ── The whole build ─────────────────────────────────────────────
|
|
@@ -534,13 +661,19 @@ export function buildGrafana(entities: Map<string, Declarable> | Iterable<Declar
|
|
|
534
661
|
.map(([, e]) => provisionedDatasource(e as DatasourceEntity))
|
|
535
662
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
536
663
|
|
|
664
|
+
const externalDatasources: ExternalDatasourceRecord[] = named
|
|
665
|
+
.filter(([, e]) => isExternalDatasource(e))
|
|
666
|
+
.map(([, e]) => externalDatasourceRecord(e as ExternalDatasourceEntity))
|
|
667
|
+
.sort((a, b) => a.uid.localeCompare(b.uid));
|
|
668
|
+
|
|
537
669
|
const dashboards: BuiltDashboard[] = [];
|
|
538
670
|
for (const [name, e] of named) {
|
|
539
671
|
if (!isDashboardEntity(e)) continue;
|
|
540
672
|
const json = renderDashboard(e, name);
|
|
541
673
|
const uid = json.uid as string;
|
|
542
674
|
const folder = e.props.folder;
|
|
543
|
-
const
|
|
675
|
+
const sub = folder ? folderDir(folder) : "";
|
|
676
|
+
const dir = sub ? `${DASHBOARDS_DIR}/${sub}` : DASHBOARDS_DIR;
|
|
544
677
|
let file = `${dir}/${uid}.json`;
|
|
545
678
|
for (let n = 2; file in files; n++) file = `${dir}/${uid}-${n}.json`;
|
|
546
679
|
files[file] = `${JSON.stringify(json, null, 2)}\n`;
|
|
@@ -552,19 +685,23 @@ export function buildGrafana(entities: Map<string, Declarable> | Iterable<Declar
|
|
|
552
685
|
declaredProviders.length > 0
|
|
553
686
|
? declaredProviders.map(provisionedProvider)
|
|
554
687
|
: dashboards.length > 0
|
|
555
|
-
? [provisionedProvider({ name:
|
|
688
|
+
? [provisionedProvider({ name: DEFAULT_PROVIDER_NAME })]
|
|
556
689
|
: [];
|
|
557
690
|
|
|
558
691
|
if (datasources.length > 0) files[DATASOURCES_FILE] = datasourcesYaml(datasources);
|
|
559
692
|
if (providers.length > 0) files[DASHBOARD_PROVIDERS_FILE] = dashboardProvidersYaml(providers);
|
|
693
|
+
const alerting = buildAlerting(named.map(([, e]) => e));
|
|
694
|
+
if (alerting) files[ALERTING_FILE] = alertingYaml(alerting.file);
|
|
560
695
|
|
|
561
696
|
const index: GrafanaIndex = {
|
|
562
697
|
grafanaSchema: `${GRAFANA_SCHEMA_PIN.source}@${GRAFANA_SCHEMA_PIN.ref}`,
|
|
563
698
|
dashboards: dashboards.map(({ uid, title, folder, file }) => ({ uid, title, ...(folder ? { folder } : {}), file })),
|
|
564
699
|
datasources: datasources.map(({ name, type, uid }) => ({ name, type, uid })),
|
|
700
|
+
...(externalDatasources.length > 0 ? { externalDatasources } : {}),
|
|
701
|
+
...(alerting ? { alerting: alerting.index } : {}),
|
|
565
702
|
files: Object.keys(files).sort(),
|
|
566
703
|
};
|
|
567
|
-
return { dashboards, datasources, providers, files, index };
|
|
704
|
+
return { dashboards, datasources, externalDatasources, providers, ...(alerting ? { alerting: alerting.file } : {}), files, index };
|
|
568
705
|
}
|
|
569
706
|
|
|
570
707
|
/** Every file the grafana entities render to, by path relative to the output directory. */
|
package/src/catalog.ts
CHANGED
|
@@ -5,13 +5,23 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
8
|
-
import { DATASOURCE_TYPE } from "./datasource";
|
|
8
|
+
import { DATASOURCE_TYPE, EXTERNAL_DATASOURCE_TYPE } from "./datasource";
|
|
9
9
|
import { DASHBOARD_TYPE, DASHBOARD_PROVIDER_TYPE } from "./dashboard";
|
|
10
10
|
import * as panels from "./panels";
|
|
11
11
|
import * as queries from "./query";
|
|
12
12
|
import { VARIABLE_TYPE_PREFIX } from "./variables";
|
|
13
|
+
import {
|
|
14
|
+
ALERT_QUERY_TYPE,
|
|
15
|
+
ALERT_RULE_GROUP_TYPE,
|
|
16
|
+
ALERT_RULE_TYPE,
|
|
17
|
+
CONTACT_POINT_TYPE,
|
|
18
|
+
EXPRESSION_TYPE_PREFIX,
|
|
19
|
+
MUTE_TIMING_TYPE,
|
|
20
|
+
NOTIFICATION_POLICY_TYPE,
|
|
21
|
+
NOTIFICATION_TEMPLATE_TYPE,
|
|
22
|
+
} from "./alerting";
|
|
13
23
|
|
|
14
|
-
export type CatalogKind = "dashboard" | "datasource" | "provider" | "panel" | "row" | "query" | "variable";
|
|
24
|
+
export type CatalogKind = "dashboard" | "datasource" | "provider" | "panel" | "row" | "query" | "variable" | "alerting" | "expression";
|
|
15
25
|
|
|
16
26
|
export interface CatalogEntry {
|
|
17
27
|
className: string;
|
|
@@ -59,6 +69,15 @@ const VARIABLES: Array<[string, string, string]> = [
|
|
|
59
69
|
["TextboxVariable", "textbox", "A free-text box"],
|
|
60
70
|
];
|
|
61
71
|
|
|
72
|
+
const EXPRESSIONS: Array<[string, string, string]> = [
|
|
73
|
+
["ReduceExpression", "reduce", "A server-side expression reducing each series to one number (last, mean, max, ...)"],
|
|
74
|
+
["MathExpression", "math", "A server-side math expression over other results by refId, e.g. $A / $B"],
|
|
75
|
+
["ThresholdExpression", "threshold", "A server-side threshold, with an optional recovery threshold"],
|
|
76
|
+
["ResampleExpression", "resample", "A server-side expression resampling a time series to a fixed window"],
|
|
77
|
+
["ClassicConditionsExpression", "classic_conditions", "Grafana's legacy classic conditions, as a server-side expression"],
|
|
78
|
+
["SqlExpression", "sql", "A server-side SQL expression over the other results"],
|
|
79
|
+
];
|
|
80
|
+
|
|
62
81
|
export const BUILTIN_CATALOG: CatalogEntry[] = [
|
|
63
82
|
{
|
|
64
83
|
className: "Dashboard",
|
|
@@ -74,6 +93,13 @@ export const BUILTIN_CATALOG: CatalogEntry[] = [
|
|
|
74
93
|
entityKind: "resource",
|
|
75
94
|
description: "A datasource, declared once and referenced by panels, queries and variables",
|
|
76
95
|
},
|
|
96
|
+
{
|
|
97
|
+
className: "ExternalDatasource",
|
|
98
|
+
entityType: EXTERNAL_DATASOURCE_TYPE,
|
|
99
|
+
kind: "datasource",
|
|
100
|
+
entityKind: "resource",
|
|
101
|
+
description: "A datasource that already exists in Grafana: referenced like a Datasource and checked by GRAF101/GRAF102, never provisioned",
|
|
102
|
+
},
|
|
77
103
|
{
|
|
78
104
|
className: "DashboardProvider",
|
|
79
105
|
entityType: DASHBOARD_PROVIDER_TYPE,
|
|
@@ -88,6 +114,64 @@ export const BUILTIN_CATALOG: CatalogEntry[] = [
|
|
|
88
114
|
entityKind: "property",
|
|
89
115
|
description: "A full-width row header; its panels are placed below it, or inside it when collapsed",
|
|
90
116
|
},
|
|
117
|
+
{
|
|
118
|
+
className: "AlertRuleGroup",
|
|
119
|
+
entityType: ALERT_RULE_GROUP_TYPE,
|
|
120
|
+
kind: "alerting",
|
|
121
|
+
entityKind: "resource",
|
|
122
|
+
description: "A group of Grafana-managed alert and recording rules in a folder, evaluated together at one interval",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
className: "ContactPoint",
|
|
126
|
+
entityType: CONTACT_POINT_TYPE,
|
|
127
|
+
kind: "alerting",
|
|
128
|
+
entityKind: "resource",
|
|
129
|
+
description: "A contact point: the integrations (email, Slack, webhook, ...) alerts are sent to, under one name",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
className: "NotificationPolicy",
|
|
133
|
+
entityType: NOTIFICATION_POLICY_TYPE,
|
|
134
|
+
kind: "alerting",
|
|
135
|
+
entityKind: "resource",
|
|
136
|
+
description: "An organisation's notification policy tree: the root receiver and the routes that match alerts to contact points",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
className: "MuteTiming",
|
|
140
|
+
entityType: MUTE_TIMING_TYPE,
|
|
141
|
+
kind: "alerting",
|
|
142
|
+
entityKind: "resource",
|
|
143
|
+
description: "A named time interval that mutes (or, as active_time_intervals, enables) notifications",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
className: "NotificationTemplate",
|
|
147
|
+
entityType: NOTIFICATION_TEMPLATE_TYPE,
|
|
148
|
+
kind: "alerting",
|
|
149
|
+
entityKind: "resource",
|
|
150
|
+
description: "A notification template group, Go templates contact point settings can use",
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
className: "AlertRule",
|
|
154
|
+
entityType: ALERT_RULE_TYPE,
|
|
155
|
+
kind: "alerting",
|
|
156
|
+
entityKind: "property",
|
|
157
|
+
description: "A Grafana-managed alert or recording rule: queries, expressions and the condition that fires it",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
className: "AlertQuery",
|
|
161
|
+
entityType: ALERT_QUERY_TYPE,
|
|
162
|
+
kind: "alerting",
|
|
163
|
+
entityKind: "property",
|
|
164
|
+
description: "A datasource query of an alert rule, with its model as Grafana stores it and its time range",
|
|
165
|
+
},
|
|
166
|
+
...EXPRESSIONS.map(
|
|
167
|
+
([className, type, description]): CatalogEntry => ({
|
|
168
|
+
className,
|
|
169
|
+
entityType: `${EXPRESSION_TYPE_PREFIX}${type}`,
|
|
170
|
+
kind: "expression",
|
|
171
|
+
entityKind: "property",
|
|
172
|
+
description,
|
|
173
|
+
}),
|
|
174
|
+
),
|
|
91
175
|
...panelEntries,
|
|
92
176
|
...queryEntries,
|
|
93
177
|
...VARIABLES.map(
|
package/src/codegen/docs.ts
CHANGED
|
@@ -13,7 +13,8 @@ function serviceFromType(resourceType: string): string {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const overview = `The grafana lexicon types what a [Grafana](https://grafana.com/docs/grafana/latest/) dashboard
|
|
16
|
-
shows: datasources, dashboards, rows, panels, queries and variables
|
|
16
|
+
shows: datasources, dashboards, rows, panels, queries and variables, and
|
|
17
|
+
Grafana-managed alerting. \`chant build\`
|
|
17
18
|
writes one JSON file per dashboard, the JSON Grafana imports as it is, plus the
|
|
18
19
|
provisioning files Grafana reads for dashboards and datasources. Grafana server
|
|
19
20
|
settings, users and plugins are out of scope.
|
|
@@ -28,11 +29,29 @@ const requests = new TimeSeriesPanel({ title: "Requests per second", datasource:
|
|
|
28
29
|
export const overview = new Dashboard({ title: "Overview", panels: [requests] });
|
|
29
30
|
\`\`\`
|
|
30
31
|
|
|
31
|
-
Panels: time series, stat, gauge, table, logs, traces, heatmap
|
|
32
|
-
|
|
32
|
+
Panels: time series, stat, gauge, table, logs, traces, heatmap, text, bar
|
|
33
|
+
chart, bar gauge, pie chart, state timeline, status history, histogram, node
|
|
34
|
+
graph, XY chart, trend, canvas, geomap, flame graph and alert list, plus rows. Queries: Prometheus (PromQL), Tempo (TraceQL) and Loki (LogQL). Panel
|
|
33
35
|
options and query fields are generated from Grafana's own schemas at a pinned
|
|
34
|
-
version,
|
|
36
|
+
version, corrected against Grafana's CUE, and track Grafana 12.4 and 13.x. Every
|
|
37
|
+
build validates the dashboards against the same schemas.
|
|
35
38
|
\`definePanel\` and \`defineQuery\` add plugins chant doesn't ship.
|
|
39
|
+
\`chant import\` turns dashboard JSON exported from Grafana into this TypeScript,
|
|
40
|
+
and it builds back to the same dashboard (see
|
|
41
|
+
[Importing Dashboards](./importing/)). \`chant lifecycle diff --live\` reports
|
|
42
|
+
a dashboard edited in Grafana as drift, property by property, and
|
|
43
|
+
\`chant import --from <env>\` writes a running Grafana's dashboards as
|
|
44
|
+
TypeScript (see [Drift and Live Export](./observing/)). \`grafanaApply\` writes
|
|
45
|
+
a build to a running Grafana over its HTTP API, with folders and library
|
|
46
|
+
panels, and prunes the project's own dashboards and folders (see
|
|
47
|
+
[Apply over the API](./applying/)).
|
|
48
|
+
|
|
49
|
+
Grafana-managed alerting is typed too: rule groups with their queries and
|
|
50
|
+
server-side expressions (reduce, math, threshold, resample, classic
|
|
51
|
+
conditions, SQL), contact points, the notification policy tree, mute timings
|
|
52
|
+
and templates, written to Grafana's alerting provisioning file.
|
|
53
|
+
\`SloAlertRules\` turns a prometheus \`Slo\` into burn-rate alert rules. See
|
|
54
|
+
[Alerting](./alerting/).
|
|
36
55
|
|
|
37
56
|
Three composites build whole dashboards from declarations in other lexicons:
|
|
38
57
|
\`RedDashboard\` from an otel \`spanmetrics\` connector, \`SloDashboard\` from a
|
|
@@ -40,10 +59,17 @@ prometheus \`Slo\` and \`AgentDashboard\` from the otel GenAI preset. Metric nam
|
|
|
40
59
|
come from the declaration, so renaming one moves the panels. See
|
|
41
60
|
[Dashboards from Declarations](./composites/).
|
|
42
61
|
|
|
43
|
-
Checks catch a query aimed at a datasource nobody declared
|
|
62
|
+
Checks catch a query aimed at a datasource nobody declared, with
|
|
63
|
+
\`Datasource\` or \`ExternalDatasource\` (GRAF101), or of the
|
|
44
64
|
wrong type (GRAF102), a \`$variable\` the dashboard doesn't declare (GRAF103),
|
|
45
65
|
duplicate uids and ids (GRAF104), panels off the grid or overlapping (GRAF105),
|
|
46
|
-
|
|
66
|
+
anything Grafana's dashboard schema rejects (GRAF107), PromQL sent to a
|
|
67
|
+
Prometheus that doesn't parse (GRAF108), dashboard providers that load a
|
|
68
|
+
dashboard twice or not into its declared folder (GRAF109), and a unit Grafana
|
|
69
|
+
doesn't know (GRAF115). For alerting, they catch rules whose condition or expressions name
|
|
70
|
+
no query (GRAF111), queries to undeclared datasources (GRAF112), routes to
|
|
71
|
+
undeclared contact points or mute timings (GRAF113), and uids, intervals and
|
|
72
|
+
duplicates Grafana refuses (GRAF114).
|
|
47
73
|
`;
|
|
48
74
|
|
|
49
75
|
const outputFormat = `The grafana lexicon writes Grafana's own files, keyed by path under the
|
|
@@ -54,9 +80,12 @@ directory of the \`-o\` output:
|
|
|
54
80
|
- \`provisioning/datasources/chant.yaml\`: every declared datasource.
|
|
55
81
|
- \`provisioning/dashboards/chant.yaml\`: a file provider for the dashboards
|
|
56
82
|
directory; a default one unless a \`DashboardProvider\` is declared.
|
|
83
|
+
- \`provisioning/alerting/chant.yaml\`: rule groups, contact points, the
|
|
84
|
+
notification policy tree, mute timings and templates, when the build
|
|
85
|
+
declares any.
|
|
57
86
|
|
|
58
87
|
The primary output (the \`-o\` file itself) is a JSON index of the dashboards,
|
|
59
|
-
datasources and files that were built.
|
|
88
|
+
datasources, alerting and files that were built.
|
|
60
89
|
|
|
61
90
|
- Dashboard JSON carries the pinned schema's \`schemaVersion\`, no numeric \`id\`,
|
|
62
91
|
and every field Grafana's schema requires (link defaults, an empty
|
|
@@ -67,8 +96,12 @@ datasources and files that were built.
|
|
|
67
96
|
referenced, and as its uid inside another datasource's \`jsonData\`.
|
|
68
97
|
- \`chant build\` rewrites JSON files with sorted keys; Grafana does not care
|
|
69
98
|
about key order.
|
|
70
|
-
-
|
|
71
|
-
|
|
99
|
+
- The dashboard JSON carries no ownership marker. Grafana keeps who manages a
|
|
100
|
+
dashboard in the resource's metadata on its \`dashboard.grafana.app\` API,
|
|
101
|
+
not in the JSON: for a provisioned dashboard, the name of the provider that
|
|
102
|
+
loaded it, which is \`chant\` unless a \`DashboardProvider\` names another.
|
|
103
|
+
\`chant lifecycle diff --live\` reads that back (see
|
|
104
|
+
[Drift and Live Export](../observing/)).
|
|
72
105
|
`;
|
|
73
106
|
|
|
74
107
|
export async function generateDocs(opts?: { verbose?: boolean }): Promise<void> {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON Schema (draft-07, the subset cog emits) to TypeScript declarations.
|
|
3
3
|
*
|
|
4
|
-
* Each vendored schema
|
|
5
|
-
* type per entry in `definitions`,
|
|
6
|
-
* comments. `required` is kept, so the types say what the schema says; the
|
|
4
|
+
* Each vendored schema, with its correction overlay applied, becomes one
|
|
5
|
+
* module under `src/schema/`: one exported type per entry in `definitions`,
|
|
6
|
+
* with the schema's descriptions as doc comments. `required` is kept, so the types say what the schema says; the
|
|
7
7
|
* entity props wrap panel options in `DeepPartial` where Grafana fills the
|
|
8
8
|
* rest in on import.
|
|
9
9
|
*/
|
|
@@ -99,6 +99,15 @@ function objectType(node: Node, indent: string): string {
|
|
|
99
99
|
return lines.length === 2 ? "Record<string, never>" : lines.join("\n");
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* A branch that only constrains which keys are present (`{ required: [...] }`),
|
|
104
|
+
* like the overlay's "a panel has a `type` or a `libraryPanel`". It says
|
|
105
|
+
* nothing about the shape, so the type ignores it and validation enforces it.
|
|
106
|
+
*/
|
|
107
|
+
function constraintOnly(branches: Node[] | undefined): boolean {
|
|
108
|
+
return Array.isArray(branches) && branches.every((b) => Object.keys(b).every((k) => k === "required"));
|
|
109
|
+
}
|
|
110
|
+
|
|
102
111
|
/** The TypeScript type for one schema node. */
|
|
103
112
|
export function tsType(node: Node | boolean | undefined, indent = ""): string {
|
|
104
113
|
if (node === undefined || node === true) return "unknown";
|
|
@@ -106,8 +115,8 @@ export function tsType(node: Node | boolean | undefined, indent = ""): string {
|
|
|
106
115
|
if (node.$ref) return refName(node.$ref);
|
|
107
116
|
if (node.const !== undefined) return literal(node.const);
|
|
108
117
|
if (Array.isArray(node.enum)) return union(node.enum.map(literal));
|
|
109
|
-
if (node.oneOf) return union(node.oneOf.map((n) => paren(tsType(n, indent))));
|
|
110
|
-
if (node.anyOf) return union(node.anyOf.map((n) => paren(tsType(n, indent))));
|
|
118
|
+
if (node.oneOf && !constraintOnly(node.oneOf)) return union(node.oneOf.map((n) => paren(tsType(n, indent))));
|
|
119
|
+
if (node.anyOf && !constraintOnly(node.anyOf)) return union(node.anyOf.map((n) => paren(tsType(n, indent))));
|
|
111
120
|
if (node.allOf) return node.allOf.map((n) => paren(tsType(n, indent))).join(" & ");
|
|
112
121
|
|
|
113
122
|
const types = Array.isArray(node.type) ? node.type : node.type ? [node.type] : [];
|
|
@@ -154,7 +163,8 @@ export function schemaModule(name: SchemaName, schema: Record<string, unknown>,
|
|
|
154
163
|
const defs = (schema.definitions ?? {}) as Record<string, Node>;
|
|
155
164
|
const out: string[] = [
|
|
156
165
|
"// Code generated by chant generate from the vendored Grafana schema",
|
|
157
|
-
`// src/spec/schemas/${name}.jsonschema.json (see src/pin.ts)
|
|
166
|
+
`// src/spec/schemas/${name}.jsonschema.json (see src/pin.ts) and its correction`,
|
|
167
|
+
"// overlay in src/spec/overlay/ (see src/spec/overlay.ts). DO NOT EDIT.",
|
|
158
168
|
"/* eslint-disable */",
|
|
159
169
|
"",
|
|
160
170
|
];
|