@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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import * as panels from "./panels";
|
|
3
|
+
import { Dashboard } from "./dashboard";
|
|
4
|
+
import { renderDashboard } from "./build";
|
|
5
|
+
import { validateDashboardSchema } from "./schema-validate";
|
|
6
|
+
import { SCHEMA_NAMES } from "./pin";
|
|
7
|
+
|
|
8
|
+
const builtins = panels.registeredPanels().filter((d) => d.builtin);
|
|
9
|
+
|
|
10
|
+
describe("built-in panels", () => {
|
|
11
|
+
test("every panel type Grafana ships with a schema at the pin has a class", () => {
|
|
12
|
+
const panelSchemas = SCHEMA_NAMES.filter((n) => !["dashboard", "prometheus", "tempo", "loki", "expr"].includes(n));
|
|
13
|
+
expect(builtins.map((d) => d.schema).filter(Boolean).sort()).toEqual([...panelSchemas].sort());
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("the panels with no upstream schema are typed by hand and not validated beyond the Panel envelope", () => {
|
|
17
|
+
expect(builtins.filter((d) => !d.schema).map((d) => d.type).sort()).toEqual(["alertlist", "flamegraph", "traces"]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("the node graph keeps Grafana's camelCase id; a plugin chant does not ship may not", () => {
|
|
21
|
+
expect(panels.NodeGraphPanel.definition.type).toBe("nodeGraph");
|
|
22
|
+
expect(() => panels.definePanel()({ type: "myGraph", className: "MyGraph", defaultSize: { w: 1, h: 1 } })).toThrow(/not a panel plugin id/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test.each(builtins.map((d) => [d.className, d] as const))("%s renders a dashboard GRAF107 accepts", (_name, def) => {
|
|
26
|
+
const Cls = (panels as unknown as Record<string, panels.PanelClass>)[def.className];
|
|
27
|
+
const json = renderDashboard(new Dashboard({ title: def.className, panels: [new Cls({ title: "p" })] }));
|
|
28
|
+
expect(json.panels![0]).toMatchObject({ type: def.type, gridPos: { w: def.defaultSize.w, h: def.defaultSize.h } });
|
|
29
|
+
expect(validateDashboardSchema(json as unknown as Record<string, unknown>)).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("typed options reach the JSON, and a value the schema rejects is a GRAF107 error", () => {
|
|
33
|
+
const pie = new panels.PieChartPanel({ options: { pieType: "donut", sort: "desc", displayLabels: ["percent"] } });
|
|
34
|
+
const ok = renderDashboard(new Dashboard({ title: "Pie", panels: [pie] }));
|
|
35
|
+
expect((ok.panels![0] as { options?: unknown }).options).toEqual({ pieType: "donut", sort: "desc", displayLabels: ["percent"] });
|
|
36
|
+
expect(validateDashboardSchema(ok as unknown as Record<string, unknown>)).toEqual([]);
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
const bad = new panels.BarGaugePanel({ options: { displayMode: "sparkle" as any } });
|
|
40
|
+
const problems = validateDashboardSchema(renderDashboard(new Dashboard({ title: "Bad", panels: [bad] })) as unknown as Record<string, unknown>);
|
|
41
|
+
expect(problems).toEqual([expect.objectContaining({ path: "/panels/0/options/displayMode", severity: "error" })]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("the hand-typed alert list options type what Grafana writes", () => {
|
|
45
|
+
const list = new panels.AlertListPanel({ options: { viewMode: "stat", sortOrder: 3, stateFilter: { firing: true, error: true }, folder: null } });
|
|
46
|
+
expect(renderDashboard(new Dashboard({ title: "Alerts", panels: [list] })).panels![0]).toMatchObject({
|
|
47
|
+
type: "alertlist",
|
|
48
|
+
options: { viewMode: "stat", sortOrder: 3, stateFilter: { firing: true, error: true }, folder: null },
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
package/src/panels.ts
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
*
|
|
12
12
|
* `gridPos` is optional. A panel without `x` and `y` is placed by the
|
|
13
13
|
* dashboard: left to right in declaration order, wrapping at Grafana's
|
|
14
|
-
* 24-column width, below the previous row of panels
|
|
15
|
-
* placed exactly there
|
|
14
|
+
* 24-column width, below the previous row of panels, and around any cells
|
|
15
|
+
* already taken. A panel with both is placed exactly there and reserves its
|
|
16
|
+
* cells first; GRAF105 reports overlaps between explicit panels. A panel
|
|
17
|
+
* with only `x` or only `y` keeps that column or line.
|
|
16
18
|
*/
|
|
17
19
|
|
|
18
20
|
import { createProperty } from "@intentius/chant/runtime";
|
|
@@ -23,7 +25,6 @@ import type { VariableEntity } from "./variables";
|
|
|
23
25
|
import type { SchemaName } from "./pin";
|
|
24
26
|
import type {
|
|
25
27
|
DashboardLink,
|
|
26
|
-
DataTransformerConfig,
|
|
27
28
|
FieldConfig,
|
|
28
29
|
FieldConfigSource,
|
|
29
30
|
GridPos,
|
|
@@ -35,6 +36,19 @@ import type * as table from "./schema/table.gen";
|
|
|
35
36
|
import type * as logs from "./schema/logs.gen";
|
|
36
37
|
import type * as heatmap from "./schema/heatmap.gen";
|
|
37
38
|
import type * as text from "./schema/text.gen";
|
|
39
|
+
import type * as barchart from "./schema/barchart.gen";
|
|
40
|
+
import type * as bargauge from "./schema/bargauge.gen";
|
|
41
|
+
import type * as piechart from "./schema/piechart.gen";
|
|
42
|
+
import type * as statetimeline from "./schema/statetimeline.gen";
|
|
43
|
+
import type * as statushistory from "./schema/statushistory.gen";
|
|
44
|
+
import type * as histogram from "./schema/histogram.gen";
|
|
45
|
+
import type * as nodegraph from "./schema/nodegraph.gen";
|
|
46
|
+
import type * as xychart from "./schema/xychart.gen";
|
|
47
|
+
import type * as trend from "./schema/trend.gen";
|
|
48
|
+
import type * as canvas from "./schema/canvas.gen";
|
|
49
|
+
import type * as geomap from "./schema/geomap.gen";
|
|
50
|
+
import type { AlertListOptions, FlameGraphOptions } from "./panel-options";
|
|
51
|
+
import type { Transformation } from "./transformations";
|
|
38
52
|
|
|
39
53
|
/** `fieldConfig` with `defaults.custom` typed for the panel. */
|
|
40
54
|
export interface PanelFieldConfig<C> {
|
|
@@ -59,7 +73,8 @@ export interface PanelProps<O = Record<string, unknown>, C = Record<string, unkn
|
|
|
59
73
|
/** The panel plugin's own options, typed from its schema. */
|
|
60
74
|
options?: DeepPartial<O>;
|
|
61
75
|
fieldConfig?: PanelFieldConfig<C>;
|
|
62
|
-
|
|
76
|
+
/** Typed per transformer: `{ id: "organize", options: { ... } }`, `transformation(id, options)`, or `customTransformation()` for an id or option the types don't know. */
|
|
77
|
+
transformations?: Transformation[];
|
|
63
78
|
links?: PanelLink[];
|
|
64
79
|
/** Repeat the panel once per value of this variable. */
|
|
65
80
|
repeat?: VariableEntity | string;
|
|
@@ -118,9 +133,11 @@ export function panelDefinitionFor(type: string): PanelDefinition | undefined {
|
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
const PLUGIN_ID = /^[a-z0-9][a-z0-9-_]*$/;
|
|
136
|
+
/** Core panel ids predate the lowercase rule plugin ids follow: the node graph is `nodeGraph`. */
|
|
137
|
+
const CORE_PLUGIN_ID = /^[A-Za-z0-9][A-Za-z0-9-_]*$/;
|
|
121
138
|
|
|
122
139
|
function makePanelClass<T extends string, O, C>(def: PanelDefinition<T>): PanelClass<T, O, C> {
|
|
123
|
-
if (!PLUGIN_ID.test(def.type)) throw new Error(`grafana: "${def.type}" is not a panel plugin id`);
|
|
140
|
+
if (!(def.builtin ? CORE_PLUGIN_ID : PLUGIN_ID).test(def.type)) throw new Error(`grafana: "${def.type}" is not a panel plugin id`);
|
|
124
141
|
if (def.type === "row") throw new Error('grafana: "row" is not a panel; use Row');
|
|
125
142
|
const existing = registry().get(def.type);
|
|
126
143
|
if (existing?.builtin && !def.builtin) {
|
|
@@ -145,12 +162,12 @@ function makePanelClass<T extends string, O, C>(def: PanelDefinition<T>): PanelC
|
|
|
145
162
|
*
|
|
146
163
|
* @example
|
|
147
164
|
* ```ts
|
|
148
|
-
* interface
|
|
165
|
+
* interface ClockOptions { mode?: "time" | "countdown"; clockType?: "24 hour" | "12 hour" }
|
|
149
166
|
*
|
|
150
|
-
* export const
|
|
151
|
-
* type: "
|
|
152
|
-
* className: "
|
|
153
|
-
* defaultSize: { w:
|
|
167
|
+
* export const ClockPanel = definePanel<ClockOptions>()({
|
|
168
|
+
* type: "grafana-clock-panel",
|
|
169
|
+
* className: "ClockPanel",
|
|
170
|
+
* defaultSize: { w: 6, h: 4 },
|
|
154
171
|
* });
|
|
155
172
|
* ```
|
|
156
173
|
*/
|
|
@@ -243,6 +260,121 @@ export const TextPanel = makePanelClass<"text", text.Options, Record<string, unk
|
|
|
243
260
|
builtin: true,
|
|
244
261
|
});
|
|
245
262
|
|
|
263
|
+
export const BarChartPanel = makePanelClass<"barchart", barchart.Options, barchart.FieldConfig>({
|
|
264
|
+
type: "barchart",
|
|
265
|
+
className: "BarChartPanel",
|
|
266
|
+
description: "Bar chart: categorical values as bars, grouped or stacked",
|
|
267
|
+
defaultSize: { w: 12, h: 8 },
|
|
268
|
+
schema: "barchart",
|
|
269
|
+
builtin: true,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
export const BarGaugePanel = makePanelClass<"bargauge", bargauge.Options, Record<string, unknown>>({
|
|
273
|
+
type: "bargauge",
|
|
274
|
+
className: "BarGaugePanel",
|
|
275
|
+
description: "Bar gauge: one bar per series, filled against min, max and thresholds",
|
|
276
|
+
defaultSize: { w: 12, h: 8 },
|
|
277
|
+
schema: "bargauge",
|
|
278
|
+
builtin: true,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
export const PieChartPanel = makePanelClass<"piechart", piechart.Options, piechart.FieldConfig>({
|
|
282
|
+
type: "piechart",
|
|
283
|
+
className: "PieChartPanel",
|
|
284
|
+
description: "Pie chart: each series' share of the total, as a pie or donut",
|
|
285
|
+
defaultSize: { w: 8, h: 8 },
|
|
286
|
+
schema: "piechart",
|
|
287
|
+
builtin: true,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
export const StateTimelinePanel = makePanelClass<"state-timeline", statetimeline.Options, statetimeline.FieldConfig>({
|
|
291
|
+
type: "state-timeline",
|
|
292
|
+
className: "StateTimelinePanel",
|
|
293
|
+
description: "State timeline: state changes over time, one lane per series",
|
|
294
|
+
defaultSize: { w: 24, h: 8 },
|
|
295
|
+
schema: "statetimeline",
|
|
296
|
+
builtin: true,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
export const StatusHistoryPanel = makePanelClass<"status-history", statushistory.Options, statushistory.FieldConfig>({
|
|
300
|
+
type: "status-history",
|
|
301
|
+
className: "StatusHistoryPanel",
|
|
302
|
+
description: "Status history: periodic states over time as a grid of cells",
|
|
303
|
+
defaultSize: { w: 24, h: 8 },
|
|
304
|
+
schema: "statushistory",
|
|
305
|
+
builtin: true,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
export const HistogramPanel = makePanelClass<"histogram", histogram.Options, histogram.FieldConfig>({
|
|
309
|
+
type: "histogram",
|
|
310
|
+
className: "HistogramPanel",
|
|
311
|
+
description: "Histogram: the distribution of values, bucketed, over the whole time range",
|
|
312
|
+
defaultSize: { w: 12, h: 8 },
|
|
313
|
+
schema: "histogram",
|
|
314
|
+
builtin: true,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
export const NodeGraphPanel = makePanelClass<"nodeGraph", nodegraph.Options, Record<string, unknown>>({
|
|
318
|
+
type: "nodeGraph",
|
|
319
|
+
className: "NodeGraphPanel",
|
|
320
|
+
description: "Node graph: a directed graph of nodes and edges, e.g. a service map",
|
|
321
|
+
defaultSize: { w: 24, h: 12 },
|
|
322
|
+
schema: "nodegraph",
|
|
323
|
+
builtin: true,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
export const XYChartPanel = makePanelClass<"xychart", xychart.Options, xychart.FieldConfig>({
|
|
327
|
+
type: "xychart",
|
|
328
|
+
className: "XYChartPanel",
|
|
329
|
+
description: "XY chart: one field plotted against another, as points or lines",
|
|
330
|
+
defaultSize: { w: 12, h: 8 },
|
|
331
|
+
schema: "xychart",
|
|
332
|
+
builtin: true,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
export const TrendPanel = makePanelClass<"trend", trend.Options, trend.FieldConfig>({
|
|
336
|
+
type: "trend",
|
|
337
|
+
className: "TrendPanel",
|
|
338
|
+
description: "Trend: values against a numeric x field that is not time",
|
|
339
|
+
defaultSize: { w: 12, h: 8 },
|
|
340
|
+
schema: "trend",
|
|
341
|
+
builtin: true,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
export const CanvasPanel = makePanelClass<"canvas", canvas.Options, Record<string, unknown>>({
|
|
345
|
+
type: "canvas",
|
|
346
|
+
className: "CanvasPanel",
|
|
347
|
+
description: "Canvas: freely placed elements, text, icons and metrics bound to data",
|
|
348
|
+
defaultSize: { w: 12, h: 10 },
|
|
349
|
+
schema: "canvas",
|
|
350
|
+
builtin: true,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
export const GeomapPanel = makePanelClass<"geomap", geomap.Options, Record<string, unknown>>({
|
|
354
|
+
type: "geomap",
|
|
355
|
+
className: "GeomapPanel",
|
|
356
|
+
description: "Geomap: data on a world map, as markers, heatmaps or routes",
|
|
357
|
+
defaultSize: { w: 12, h: 10 },
|
|
358
|
+
schema: "geomap",
|
|
359
|
+
builtin: true,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
export const FlameGraphPanel = makePanelClass<"flamegraph", FlameGraphOptions, Record<string, unknown>>({
|
|
363
|
+
type: "flamegraph",
|
|
364
|
+
className: "FlameGraphPanel",
|
|
365
|
+
description: "Flame graph: profiling data, e.g. from Pyroscope, as a flame graph and table",
|
|
366
|
+
defaultSize: { w: 24, h: 12 },
|
|
367
|
+
builtin: true,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
export const AlertListPanel = makePanelClass<"alertlist", AlertListOptions, Record<string, unknown>>({
|
|
371
|
+
type: "alertlist",
|
|
372
|
+
className: "AlertListPanel",
|
|
373
|
+
description: "Alert list: alert rules and their current state, no queries",
|
|
374
|
+
defaultSize: { w: 8, h: 10 },
|
|
375
|
+
builtin: true,
|
|
376
|
+
});
|
|
377
|
+
|
|
246
378
|
// ── Rows ────────────────────────────────────────────────────────
|
|
247
379
|
|
|
248
380
|
export interface RowProps {
|
|
@@ -256,6 +388,12 @@ export interface RowProps {
|
|
|
256
388
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
257
389
|
datasource?: DatasourceInput<any>;
|
|
258
390
|
id?: number;
|
|
391
|
+
/**
|
|
392
|
+
* The grid line the row header sits on. Without it, the row goes on the
|
|
393
|
+
* first free line below everything declared before it. A row header is
|
|
394
|
+
* always full width and one line high, so `y` is all it takes.
|
|
395
|
+
*/
|
|
396
|
+
gridPos?: { y: number };
|
|
259
397
|
}
|
|
260
398
|
|
|
261
399
|
export interface RowEntity extends Declarable {
|
package/src/pin.ts
CHANGED
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
* kinds. `grafana/grafana-foundation-sdk` runs them through cog and
|
|
6
6
|
* publishes, beside its builder libraries, one JSON Schema per kind under
|
|
7
7
|
* `jsonschema/`. This lexicon vendors the files it uses into `src/spec/schemas/`
|
|
8
|
-
* (exact bytes, at the commit below)
|
|
9
|
-
*
|
|
8
|
+
* (exact bytes, at the commit below), applies the correction overlay in
|
|
9
|
+
* `src/spec/overlay/` (see `src/spec/overlay.ts`), and generates
|
|
10
|
+
* `src/schema/*.gen.ts` from the result. Nothing is fetched at build or test
|
|
11
|
+
* time.
|
|
12
|
+
*
|
|
13
|
+
* The types track Grafana 12.4 and 13.x: v0.0.20 is labelled with the
|
|
14
|
+
* v11.6.x kind registry, but its dashboard schema largely matches those
|
|
15
|
+
* releases, and the overlay covers the rest.
|
|
10
16
|
*
|
|
11
17
|
* Bumping the pin: change `ref` and `commit`, run `just fetch-schemas`
|
|
12
18
|
* (downloads and prints the new digests), paste the digests here, run
|
|
13
|
-
* `npm run generate
|
|
19
|
+
* `npm run generate` (it refuses overlay patches the new schemas already
|
|
20
|
+
* carry; delete those), and review the diff of `src/schema/`.
|
|
14
21
|
*/
|
|
15
22
|
|
|
16
23
|
export interface GrafanaSchemaPin {
|
|
@@ -20,10 +27,13 @@ export interface GrafanaSchemaPin {
|
|
|
20
27
|
ref: string;
|
|
21
28
|
/** The exact commit the vendored files were taken from. */
|
|
22
29
|
commit: string;
|
|
23
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* The kind registry label the SDK publishes the schemas under. It lags the
|
|
32
|
+
* content: the v0.0.20 schemas are labelled v11.6.x and cover 12.4 and 13.x.
|
|
33
|
+
*/
|
|
24
34
|
kindRegistry: string;
|
|
25
35
|
/** sha256 of each vendored file, keyed by schema name (`src/spec/schemas/<name>.jsonschema.json`). */
|
|
26
|
-
files: Readonly<Record<
|
|
36
|
+
files: Readonly<Record<VendoredSchemaName, string>>;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
export const SCHEMA_NAMES = [
|
|
@@ -35,13 +45,41 @@ export const SCHEMA_NAMES = [
|
|
|
35
45
|
"logs",
|
|
36
46
|
"heatmap",
|
|
37
47
|
"text",
|
|
48
|
+
"barchart",
|
|
49
|
+
"bargauge",
|
|
50
|
+
"piechart",
|
|
51
|
+
"statetimeline",
|
|
52
|
+
"statushistory",
|
|
53
|
+
"histogram",
|
|
54
|
+
"nodegraph",
|
|
55
|
+
"xychart",
|
|
56
|
+
"trend",
|
|
57
|
+
"canvas",
|
|
58
|
+
"geomap",
|
|
38
59
|
"prometheus",
|
|
39
60
|
"tempo",
|
|
40
61
|
"loki",
|
|
62
|
+
"expr",
|
|
41
63
|
] as const;
|
|
42
64
|
|
|
43
65
|
export type SchemaName = (typeof SCHEMA_NAMES)[number];
|
|
44
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Schemas vendored from the same commit for reading, not for types: no
|
|
69
|
+
* `.gen.ts` is generated from them and GRAF107 does not use them, since chant
|
|
70
|
+
* builds classic dashboards. `dashboardv2` is the v2 dashboard kind the
|
|
71
|
+
* importer reads (#2947); `src/import/v2.test.ts` checks the importer knows
|
|
72
|
+
* every key it defines.
|
|
73
|
+
*/
|
|
74
|
+
export const IMPORT_ONLY_SCHEMA_NAMES = ["dashboardv2"] as const;
|
|
75
|
+
|
|
76
|
+
export type ImportOnlySchemaName = (typeof IMPORT_ONLY_SCHEMA_NAMES)[number];
|
|
77
|
+
|
|
78
|
+
/** Every schema file under `src/spec/schemas/`. */
|
|
79
|
+
export type VendoredSchemaName = SchemaName | ImportOnlySchemaName;
|
|
80
|
+
|
|
81
|
+
export const VENDORED_SCHEMA_NAMES: readonly VendoredSchemaName[] = [...SCHEMA_NAMES, ...IMPORT_ONLY_SCHEMA_NAMES];
|
|
82
|
+
|
|
45
83
|
export const GRAFANA_SCHEMA_PIN: GrafanaSchemaPin = Object.freeze({
|
|
46
84
|
source: "github.com/grafana/grafana-foundation-sdk/jsonschema",
|
|
47
85
|
ref: "v0.0.20",
|
|
@@ -56,13 +94,26 @@ export const GRAFANA_SCHEMA_PIN: GrafanaSchemaPin = Object.freeze({
|
|
|
56
94
|
logs: "0254600ef9720bd2c906628304a7c362c0182b4bff2fd7d91352d95a269b2dda",
|
|
57
95
|
heatmap: "1e1905eef14ada48c121374ecad41412380b317e8183771d99a7504753d5005c",
|
|
58
96
|
text: "db6d2e3e2c576d1c78ac2e08930f60dbc22bb7ee21abd0cd9204be49f0dbce09",
|
|
97
|
+
barchart: "36f16c10ac3529f88435b81af1d2e352bc915d228a0fb0418d4e46cc2615f6cb",
|
|
98
|
+
bargauge: "dbd22a7b71c4133b880ad39daeaf33c5bf0509fe0d5dc7a882fe3449207b636f",
|
|
99
|
+
piechart: "3edcd85eb8333db755562fc360e392795617582c8d03d11f5918247a4b0ceea9",
|
|
100
|
+
statetimeline: "4df9f488db587b074de51e2b10d6361e3dd390f110aee105029f334b863bd365",
|
|
101
|
+
statushistory: "3149edd7e3c9567913bb123ee764b118c816b9865ab7ec1f19275335b670f4b3",
|
|
102
|
+
histogram: "ef30d7b439b5ece72783be27575b736745aa3eedade37919fcf7560427eb09ac",
|
|
103
|
+
nodegraph: "76595130348310202d87c0efd1e248d60f391cf4cf39c0d3c8d30a251c1f9461",
|
|
104
|
+
xychart: "071b4866d0d292c772adb5946a6cae94cc2486b4a0acabb6247090b5f7a83f9f",
|
|
105
|
+
trend: "599a241e922217be840a65fbf731f7883a3304fd2b6074af01650d325ec0e629",
|
|
106
|
+
canvas: "53e5069cfeef461913cec759c120bc31167b7532be2b69d3c45ce7e4877290d7",
|
|
107
|
+
geomap: "8c8716e9bdbb6b0465b2e91ad1a1100d96a02b7f1be606bd90a6676d94897179",
|
|
59
108
|
prometheus: "726fc97eeb1e37791dbbf988c3cf40de17bb8e623926e6979c4a3f947cb1af87",
|
|
60
109
|
tempo: "21aec4c333c9b8e5e9228e85a6225c76161975abb54002e6f12e919e3b4e54ae",
|
|
61
110
|
loki: "03b81d6b952e3d31785c4b52637e2b7b1c170cc2ea8ec7ec671c21599508e31b",
|
|
111
|
+
expr: "b1f1c1e4f6bf00f0fb4cb32b5bcc1b0e91fad9b7ad9354ecb791322bd97a5564",
|
|
112
|
+
dashboardv2: "a2cfb8b731ff9f48c41f5aa06ade134cf0d93ab9f6f0567a34a4bc39a9fd984e",
|
|
62
113
|
}),
|
|
63
114
|
});
|
|
64
115
|
|
|
65
116
|
/** The raw URL of one schema file at the pinned commit. */
|
|
66
|
-
export function schemaUrl(name:
|
|
117
|
+
export function schemaUrl(name: VendoredSchemaName, pin: GrafanaSchemaPin = GRAFANA_SCHEMA_PIN): string {
|
|
67
118
|
return `https://raw.githubusercontent.com/grafana/grafana-foundation-sdk/${pin.commit}/jsonschema/${name}.jsonschema.json`;
|
|
68
119
|
}
|
package/src/plugin.test.ts
CHANGED
|
@@ -16,7 +16,7 @@ describe("grafana plugin", () => {
|
|
|
16
16
|
|
|
17
17
|
it("ships lint rules and post-synth checks, all under the GRAF prefix", () => {
|
|
18
18
|
const ids = [...grafanaPlugin.lintRules!().map((r) => r.id), ...grafanaPlugin.postSynthChecks!().map((c) => c.id)];
|
|
19
|
-
expect(ids).toEqual(["GRAF001", "GRAF002", "GRAF101", "GRAF102", "GRAF103", "GRAF104", "GRAF105", "GRAF106", "GRAF107"]);
|
|
19
|
+
expect(ids).toEqual(["GRAF001", "GRAF002", "GRAF101", "GRAF102", "GRAF103", "GRAF104", "GRAF105", "GRAF106", "GRAF107", "GRAF108", "GRAF109", "GRAF111", "GRAF112", "GRAF113", "GRAF114", "GRAF115"]);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it("catalogues every rule and check for chant audit, and nothing else", () => {
|
|
@@ -29,13 +29,16 @@ describe("grafana plugin", () => {
|
|
|
29
29
|
expect(grafanaPlugin.detectTemplate!({ panels: [], schemaVersion: 41, title: "x" })).toBe(true);
|
|
30
30
|
expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, datasources: [] })).toBe(true);
|
|
31
31
|
expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, providers: [] })).toBe(true);
|
|
32
|
+
expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, groups: [{ name: "g", folder: "F", rules: [] }] })).toBe(true);
|
|
33
|
+
expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, contactPoints: [] })).toBe(true);
|
|
34
|
+
expect(grafanaPlugin.detectTemplate!({ groups: [{ name: "g", rules: [{ alert: "A", expr: "up == 0" }] }] })).toBe(false);
|
|
32
35
|
expect(grafanaPlugin.detectTemplate!({ apiVersion: "v1", kind: "ConfigMap" })).toBe(false);
|
|
33
36
|
expect(grafanaPlugin.detectTemplate!({ receivers: {}, service: { pipelines: {} } })).toBe(false);
|
|
34
37
|
});
|
|
35
38
|
|
|
36
39
|
it("loads its skills with content", () => {
|
|
37
40
|
const skills = grafanaPlugin.skills!();
|
|
38
|
-
expect(skills.map((s) => s.name)).toEqual(["chant-grafana", "chant-grafana-provisioning"]);
|
|
41
|
+
expect(skills.map((s) => s.name)).toEqual(["chant-grafana", "chant-grafana-provisioning", "chant-grafana-alerting"]);
|
|
39
42
|
for (const s of skills) expect(s.content.length).toBeGreaterThan(200);
|
|
40
43
|
});
|
|
41
44
|
|
package/src/plugin.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LexiconPlugin } from "@intentius/chant/lexicon";
|
|
2
|
+
import type { OwnershipChannel } from "@intentius/chant/ownership";
|
|
2
3
|
import type { CompletionContext, HoverContext } from "@intentius/chant/lsp/types";
|
|
3
4
|
import type { McpResourceContribution } from "@intentius/chant/mcp/types";
|
|
4
5
|
import { createDiffTool } from "@intentius/chant/lexicon-plugin-helpers";
|
|
@@ -9,10 +10,16 @@ import { grafanaAuditCatalog } from "./lint/audit-catalog";
|
|
|
9
10
|
import { completions } from "./lsp/completions";
|
|
10
11
|
import { hover } from "./lsp/hover";
|
|
11
12
|
import { detectTemplate } from "./detect";
|
|
13
|
+
import { GrafanaParser } from "./import/parser";
|
|
14
|
+
import { GrafanaGenerator } from "./import/generator";
|
|
15
|
+
import { dashboardImporter } from "./import/embedded";
|
|
12
16
|
import { grafanaSkills } from "./skill-defs";
|
|
13
17
|
import { BUILTIN_CATALOG } from "./catalog";
|
|
14
18
|
import { GRAFANA_SCHEMA_PIN } from "./pin";
|
|
15
19
|
import { compositeCatalog } from "./composites/catalog";
|
|
20
|
+
import { grafanaConfigSchema } from "./config";
|
|
21
|
+
import { grafanaDeepNormalizationHooks } from "./deep-observe-hooks";
|
|
22
|
+
import { GRAFANA_OWNERSHIP_KEYS } from "./ownership";
|
|
16
23
|
|
|
17
24
|
const catalogResource: McpResourceContribution = {
|
|
18
25
|
uri: "grafana:resource-catalog",
|
|
@@ -24,6 +31,23 @@ const catalogResource: McpResourceContribution = {
|
|
|
24
31
|
},
|
|
25
32
|
};
|
|
26
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Where chant's marker can be read (#2946, see ./ownership.ts).
|
|
36
|
+
*
|
|
37
|
+
* A dashboard read over `/apis/dashboard.grafana.app` resolves `owned` or
|
|
38
|
+
* `foreign` on all three paths: from chant's managed-by label (stack and env
|
|
39
|
+
* included) when it was written through the API, or from the manager
|
|
40
|
+
* annotation Grafana writes on a dashboard one of the project's providers
|
|
41
|
+
* loaded. What resolves nothing, and says `unknown`, is kind- and
|
|
42
|
+
* server-shaped rather than path-shaped: datasources (their API has no
|
|
43
|
+
* labels) and dashboards on Grafana 11 (read over `/api/dashboards/uid`).
|
|
44
|
+
* An `owned: true` read withholds those as `filtered`.
|
|
45
|
+
*/
|
|
46
|
+
const ownershipChannel: OwnershipChannel = {
|
|
47
|
+
keys: GRAFANA_OWNERSHIP_KEYS,
|
|
48
|
+
reads: ["describeResources", "observeResourcesDeep", "exportResources"],
|
|
49
|
+
};
|
|
50
|
+
|
|
27
51
|
/**
|
|
28
52
|
* Grafana lexicon plugin.
|
|
29
53
|
*
|
|
@@ -36,6 +60,8 @@ const catalogResource: McpResourceContribution = {
|
|
|
36
60
|
export const grafanaPlugin: LexiconPlugin = {
|
|
37
61
|
name: "grafana",
|
|
38
62
|
serializer: grafanaSerializer,
|
|
63
|
+
configSchema: grafanaConfigSchema,
|
|
64
|
+
ownershipChannel,
|
|
39
65
|
|
|
40
66
|
// ── Required lifecycle methods ────────────────────────────────
|
|
41
67
|
|
|
@@ -75,6 +101,12 @@ export const grafanaPlugin: LexiconPlugin = {
|
|
|
75
101
|
return rules;
|
|
76
102
|
},
|
|
77
103
|
|
|
104
|
+
// Panels, rows, queries and variables live inside a dashboard, so core
|
|
105
|
+
// lint does not count them as resources (chant #2957).
|
|
106
|
+
propertyClassNames() {
|
|
107
|
+
return BUILTIN_CATALOG.filter((e) => e.entityKind === "property").map((e) => e.className);
|
|
108
|
+
},
|
|
109
|
+
|
|
78
110
|
postSynthChecks() {
|
|
79
111
|
return postSynthChecks;
|
|
80
112
|
},
|
|
@@ -101,6 +133,18 @@ export const grafanaPlugin: LexiconPlugin = {
|
|
|
101
133
|
return detectTemplate(data);
|
|
102
134
|
},
|
|
103
135
|
|
|
136
|
+
templateParser() {
|
|
137
|
+
return new GrafanaParser();
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
templateGenerator() {
|
|
141
|
+
return new GrafanaGenerator();
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
embeddedImporters() {
|
|
145
|
+
return [dashboardImporter];
|
|
146
|
+
},
|
|
147
|
+
|
|
104
148
|
completionProvider(ctx: CompletionContext) {
|
|
105
149
|
return completions(ctx);
|
|
106
150
|
},
|
|
@@ -109,6 +153,28 @@ export const grafanaPlugin: LexiconPlugin = {
|
|
|
109
153
|
return hover(ctx);
|
|
110
154
|
},
|
|
111
155
|
|
|
156
|
+
// ── Live observation and export (#2946) ──────────────────────
|
|
157
|
+
// Behind dynamic imports, so `chant build` never loads a transport. The
|
|
158
|
+
// hooks are static: core normalizes the declared tree with them whether or
|
|
159
|
+
// not a live read happens.
|
|
160
|
+
|
|
161
|
+
async describeResources(options) {
|
|
162
|
+
const { describeResources } = await import("./describe-resources");
|
|
163
|
+
return describeResources(options);
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
async observeResourcesDeep(options) {
|
|
167
|
+
const { observeResourcesDeepGrafana } = await import("./deep-observe");
|
|
168
|
+
return observeResourcesDeepGrafana(options);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
deepNormalizationHooks: grafanaDeepNormalizationHooks,
|
|
172
|
+
|
|
173
|
+
async exportResources(options) {
|
|
174
|
+
const { exportResources } = await import("./export-resources");
|
|
175
|
+
return exportResources(options);
|
|
176
|
+
},
|
|
177
|
+
|
|
112
178
|
async docs(options?: { verbose?: boolean }) {
|
|
113
179
|
const { generateDocs } = await import("./codegen/docs");
|
|
114
180
|
return generateDocs(options);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GRAF108's pieces: template-variable substitution, the PromQL inside a
|
|
3
|
+
* Prometheus query variable, and which queries of a dashboard get parsed.
|
|
4
|
+
* The check itself, over built and hand-written dashboards, is in
|
|
5
|
+
* lint/post-synth/post-synth.test.ts.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, test } from "vitest";
|
|
8
|
+
import { checkGrafanaPromql, prometheusQueries, substituteTemplateVariables, variablePromql } from "./promql-check";
|
|
9
|
+
import { knownDatasources } from "./datasource-refs";
|
|
10
|
+
|
|
11
|
+
describe("substituteTemplateVariables", () => {
|
|
12
|
+
test.each([
|
|
13
|
+
['sum(rate(x{a=~"$a"}[$__rate_interval]))', 'sum(rate(x{a=~"$a"}[5m]))'],
|
|
14
|
+
["rate(x[$__interval:$__interval])", "rate(x[5m:5m])"],
|
|
15
|
+
["rate(x[${window}]) offset $shift", "rate(x[5m]) offset 5m"],
|
|
16
|
+
["rate(x[${minutes}m])", "rate(x[1m])"],
|
|
17
|
+
['$metric{job="a"} / $__range_s', 'grafana_var{job="a"} / 1'],
|
|
18
|
+
["sum by ($group) (x) * $__interval_ms", "sum by (grafana_var) (x) * 1"],
|
|
19
|
+
["x @ ${__to:date:seconds}", "x @ 1"],
|
|
20
|
+
["rate([[metric]][[[w]]])", "rate(grafana_var[5m])"],
|
|
21
|
+
["topk(${n:raw}, x)", "topk(grafana_var, x)"],
|
|
22
|
+
["x{a='$a', b=`$b`}", "x{a='$a', b=`$b`}"],
|
|
23
|
+
['x{a="\\"$a"}[$__range]', 'x{a="\\"$a"}[5m]'],
|
|
24
|
+
])("%s", (query, text) => {
|
|
25
|
+
expect(substituteTemplateVariables(query).text).toBe(text);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("maps offsets in the substituted text back to the query", () => {
|
|
29
|
+
const s = substituteTemplateVariables("rate(x[$__rate_interval]) +");
|
|
30
|
+
expect(s.text).toBe("rate(x[5m]) +");
|
|
31
|
+
expect(s.originalOffset(7)).toBe(7);
|
|
32
|
+
expect(s.originalOffset(8)).toBe(7);
|
|
33
|
+
expect(s.originalOffset(11)).toBe(25);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("checkGrafanaPromql", () => {
|
|
38
|
+
test("passes the queries Grafana's own dashboards use", () => {
|
|
39
|
+
for (const q of [
|
|
40
|
+
'histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{route=~"$route"}[$__rate_interval])))',
|
|
41
|
+
'sum by (route) (rate(http_requests_total{job=~"$job", env=~"$env"}[$window]))',
|
|
42
|
+
"sum(increase(x[$__range])) / ($__range_s)",
|
|
43
|
+
'up{instance=~"[[instance]]"}',
|
|
44
|
+
]) {
|
|
45
|
+
expect({ q, r: checkGrafanaPromql(q) }).toEqual({ q, r: { ok: true } });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("reports an unbalanced expression against the original text", () => {
|
|
50
|
+
const r = checkGrafanaPromql('sum(x{cluster="$cluster"}');
|
|
51
|
+
expect(r).toEqual({ ok: false, message: expect.stringContaining('ends early (after "sum(x{cluster="$cluster"}")') });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("reports the offset in the query as written, not as substituted", () => {
|
|
55
|
+
const r = checkGrafanaPromql("rate(x[$__rate_interval]) by");
|
|
56
|
+
expect(r.ok).toBe(false);
|
|
57
|
+
if (!r.ok) expect(r.message).toMatch(/offset 2\d: "rate\(x\[\$__rate_interval\]\) ?" >>> /);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("variablePromql", () => {
|
|
62
|
+
test.each([
|
|
63
|
+
["label_names()", []],
|
|
64
|
+
['label_names(up{job="a"})', ['up{job="a"}']],
|
|
65
|
+
["label_values(job)", []],
|
|
66
|
+
['label_values(up{env="$env"}, job)', ['up{env="$env"}']],
|
|
67
|
+
["metrics(^http_.*)", []],
|
|
68
|
+
["query_result(topk(5, sum by (job) (up)))", ["topk(5, sum by (job) (up))"]],
|
|
69
|
+
['up{job="a"}', ['up{job="a"}']],
|
|
70
|
+
[" ", []],
|
|
71
|
+
])("%s", (query, promql) => {
|
|
72
|
+
expect(variablePromql(query)).toEqual(promql);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("prometheusQueries", () => {
|
|
77
|
+
const known = knownDatasources([
|
|
78
|
+
{ name: "Prometheus", type: "prometheus", uid: "prom" },
|
|
79
|
+
{ name: "Loki", type: "loki", uid: "loki" },
|
|
80
|
+
]);
|
|
81
|
+
const dashboard = {
|
|
82
|
+
templating: {
|
|
83
|
+
list: [
|
|
84
|
+
{ type: "datasource", name: "ds", query: "prometheus" },
|
|
85
|
+
{ type: "query", name: "job", datasource: { type: "prometheus", uid: "prom" }, query: { query: "label_values(up, job)", refId: "V" } },
|
|
86
|
+
{ type: "query", name: "stream", datasource: { type: "loki", uid: "loki" }, query: "label_values(app)" },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
panels: [
|
|
90
|
+
{ id: 1, title: "a", type: "stat", datasource: { type: "prometheus", uid: "prom" }, targets: [{ refId: "A", expr: "up" }, { refId: "B", expr: "" }] },
|
|
91
|
+
{ id: 2, title: "b", type: "logs", datasource: { type: "loki", uid: "loki" }, targets: [{ refId: "A", expr: '{app="x"} |= "error"' }] },
|
|
92
|
+
{ id: 3, title: "c", type: "stat", datasource: { type: "prometheus", uid: "${ds}" }, targets: [{ refId: "A", expr: "sum(up)" }] },
|
|
93
|
+
{ id: 4, title: "d", type: "stat", targets: [{ refId: "A", expr: "no datasource" }] },
|
|
94
|
+
{ id: 5, title: "e", type: "stat", datasource: { type: "datasource", uid: "-- Mixed --" }, targets: [{ refId: "A", expr: "x", datasource: { type: "loki", uid: "loki" } }, { refId: "B", expr: "y", datasource: { type: "prometheus", uid: "prom" } }] },
|
|
95
|
+
],
|
|
96
|
+
__elements: { lib: { name: "lib", kind: 1, model: { type: "stat", datasource: { type: "prometheus", uid: "prom" }, targets: [{ refId: "A", expr: "rate(x[5m])" }] } } },
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
test("takes only the queries that reach a Prometheus, including library panels", () => {
|
|
100
|
+
expect(prometheusQueries(dashboard, known)).toEqual([
|
|
101
|
+
{ where: 'panel "a" (id 1) query A', expr: "up" },
|
|
102
|
+
{ where: 'panel "c" (id 3) query A', expr: "sum(up)" },
|
|
103
|
+
{ where: 'panel "e" (id 5) query B', expr: "y" },
|
|
104
|
+
{ where: 'variable "job" query', expr: "up" },
|
|
105
|
+
{ where: 'library panel "lib" query A', expr: "rate(x[5m])" },
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
});
|