@intentius/chant-lexicon-grafana 0.99.0 → 0.100.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 +12 -6
- package/dist/annotations.d.ts +61 -0
- package/dist/annotations.d.ts.map +1 -0
- package/dist/api/apply.d.ts +8 -4
- package/dist/api/apply.d.ts.map +1 -1
- package/dist/api/fake-grafana.d.ts +2 -0
- package/dist/api/fake-grafana.d.ts.map +1 -1
- package/dist/api/folders.d.ts +53 -17
- package/dist/api/folders.d.ts.map +1 -1
- package/dist/build.d.ts +26 -5
- 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/generate.d.ts +7 -1
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/composites/shared.d.ts +4 -3
- package/dist/composites/shared.d.ts.map +1 -1
- package/dist/dashboard.d.ts +42 -14
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/datasource-refs.d.ts +9 -5
- package/dist/datasource-refs.d.ts.map +1 -1
- package/dist/datasource-settings.d.ts +834 -0
- package/dist/datasource-settings.d.ts.map +1 -0
- package/dist/datasource.d.ts +53 -11
- package/dist/datasource.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts +1 -1
- package/dist/deep-observe-hooks.d.ts.map +1 -1
- package/dist/deep-observe.d.ts +9 -4
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +1 -0
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/detect.d.ts +4 -2
- package/dist/detect.d.ts.map +1 -1
- package/dist/folder.d.ts +68 -0
- package/dist/folder.d.ts.map +1 -0
- package/dist/import/generator.d.ts.map +1 -1
- package/dist/import/mappings.d.ts +12 -6
- package/dist/import/mappings.d.ts.map +1 -1
- package/dist/import/model.d.ts +7 -0
- package/dist/import/model.d.ts.map +1 -1
- package/dist/import/normalize.d.ts.map +1 -1
- package/dist/import/parser.d.ts +16 -1
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/import/testdata/fixtures.d.ts +6 -0
- package/dist/import/testdata/fixtures.d.ts.map +1 -1
- package/dist/import/v2.d.ts +4 -3
- package/dist/import/v2.d.ts.map +1 -1
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/init-templates.d.ts +27 -0
- package/dist/init-templates.d.ts.map +1 -0
- package/dist/integrity.json +11 -9
- package/dist/lint/audit-catalog.d.ts +1 -1
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/graf101.d.ts +1 -1
- package/dist/lint/post-synth/graf104.d.ts +2 -2
- package/dist/lint/post-synth/graf106.d.ts +2 -2
- package/dist/lint/post-synth/graf110.d.ts +8 -0
- package/dist/lint/post-synth/graf110.d.ts.map +1 -0
- 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/manifest.json +1 -1
- package/dist/meta.json +70 -0
- package/dist/okf/index.md +18 -3
- package/dist/okf/rules/GRAF101.md +2 -2
- package/dist/okf/rules/GRAF104.md +2 -2
- package/dist/okf/rules/GRAF106.md +2 -2
- package/dist/okf/rules/GRAF110.md +11 -0
- package/dist/okf/types/AdhocVariable.md +9 -0
- package/dist/okf/types/AzureMonitorQuery.md +9 -0
- package/dist/okf/types/BigQueryQuery.md +9 -0
- package/dist/okf/types/CloudMonitoringQuery.md +9 -0
- package/dist/okf/types/CloudWatchQuery.md +9 -0
- package/dist/okf/types/Dashboard.md +1 -1
- package/dist/okf/types/Datasource.md +1 -1
- package/dist/okf/types/DatasourceProvisioning.md +9 -0
- package/dist/okf/types/ElasticsearchQuery.md +9 -0
- package/dist/okf/types/ExternalDatasource.md +1 -1
- package/dist/okf/types/Folder.md +9 -0
- package/dist/okf/types/GroupByVariable.md +9 -0
- package/dist/okf/types/MSSQLQuery.md +9 -0
- package/dist/okf/types/MySQLQuery.md +9 -0
- package/dist/okf/types/PostgresQuery.md +9 -0
- package/dist/okf/types/PyroscopeQuery.md +9 -0
- package/dist/okf/types/SwitchVariable.md +9 -0
- package/dist/op/activities/grafana-apply.d.ts +11 -0
- package/dist/op/activities/grafana-apply.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/ownership.d.ts +11 -0
- package/dist/ownership.d.ts.map +1 -1
- package/dist/pin.d.ts +1 -1
- package/dist/pin.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/promql-check.d.ts.map +1 -1
- package/dist/query-models.d.ts +95 -0
- package/dist/query-models.d.ts.map +1 -0
- package/dist/query.d.ts +107 -10
- package/dist/query.d.ts.map +1 -1
- package/dist/rules/graf101.ts +2 -2
- package/dist/rules/graf104.ts +3 -3
- package/dist/rules/graf106.ts +3 -3
- package/dist/rules/graf110.ts +17 -0
- package/dist/rules/grafana-helpers.ts +13 -3
- package/dist/schema/azuremonitor.gen.d.ts +354 -0
- package/dist/schema/azuremonitor.gen.d.ts.map +1 -0
- package/dist/schema/bigquery.gen.d.ts +93 -0
- package/dist/schema/bigquery.gen.d.ts.map +1 -0
- package/dist/schema/cloudwatch.gen.d.ts +394 -0
- package/dist/schema/cloudwatch.gen.d.ts.map +1 -0
- package/dist/schema/elasticsearch.gen.d.ts +444 -0
- package/dist/schema/elasticsearch.gen.d.ts.map +1 -0
- package/dist/schema/googlecloudmonitoring.gen.d.ts +312 -0
- package/dist/schema/googlecloudmonitoring.gen.d.ts.map +1 -0
- package/dist/schema/grafanapyroscope.gen.d.ts +60 -0
- package/dist/schema/grafanapyroscope.gen.d.ts.map +1 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/table.gen.d.ts +13 -0
- package/dist/schema/table.gen.d.ts.map +1 -1
- package/dist/schema-validate.d.ts +9 -0
- package/dist/schema-validate.d.ts.map +1 -1
- package/dist/skill-defs.d.ts.map +1 -1
- package/dist/skills/chant-grafana-operations.md +108 -0
- package/dist/skills/chant-grafana.md +5 -4
- package/dist/spec/schemas.d.ts +9 -7
- package/dist/spec/schemas.d.ts.map +1 -1
- package/dist/spec/schemas.gen.d.ts +3 -0
- package/dist/spec/schemas.gen.d.ts.map +1 -0
- package/dist/validate-alerting.d.ts.map +1 -1
- package/dist/validate-output.d.ts +15 -2
- package/dist/validate-output.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validation.d.ts +14 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/variables.d.ts +102 -5
- package/dist/variables.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/annotations.ts +84 -0
- package/src/api/apply.test.ts +19 -1
- package/src/api/apply.ts +18 -8
- package/src/api/fake-grafana.ts +6 -2
- package/src/api/folders.ts +106 -32
- package/src/build.test.ts +142 -6
- package/src/build.ts +126 -24
- package/src/catalog.ts +20 -2
- package/src/codegen/docs.ts +13 -6
- package/src/codegen/generate.ts +27 -1
- package/src/composites/shared.ts +3 -2
- package/src/dashboard.ts +59 -14
- package/src/datasource-refs.ts +30 -6
- package/src/datasource-settings.test.ts +106 -0
- package/src/datasource-settings.ts +941 -0
- package/src/datasource.ts +71 -11
- package/src/deep-observe-hooks.ts +16 -1
- package/src/deep-observe.test.ts +47 -1
- package/src/deep-observe.ts +36 -13
- package/src/describe-resources.test.ts +16 -0
- package/src/describe-resources.ts +36 -1
- package/src/detect.ts +3 -3
- package/src/folder.ts +118 -0
- package/src/generated/lexicon-grafana.json +70 -0
- package/src/import/generated-types.e2e.test.ts +15 -3
- package/src/import/generator.ts +2 -1
- package/src/import/mappings.ts +98 -26
- package/src/import/model.ts +7 -0
- package/src/import/normalize.ts +32 -3
- package/src/import/parser.test.ts +97 -21
- package/src/import/parser.ts +169 -18
- package/src/import/roundtrip.test.ts +104 -9
- package/src/import/testdata/fixtures.ts +8 -0
- package/src/import/v2.ts +4 -3
- package/src/index.ts +122 -13
- package/src/init-templates.test.ts +74 -0
- package/src/init-templates.ts +349 -0
- package/src/lint/audit-catalog.ts +9 -1
- package/src/lint/post-synth/graf101.ts +2 -2
- package/src/lint/post-synth/graf104.ts +3 -3
- package/src/lint/post-synth/graf106.ts +3 -3
- package/src/lint/post-synth/graf110.ts +17 -0
- package/src/lint/post-synth/grafana-helpers.ts +13 -3
- package/src/lint/post-synth/index.ts +2 -0
- package/src/lint/post-synth/post-synth.test.ts +99 -2
- package/src/load-cost.test.ts +35 -0
- package/src/op/activities/grafana-apply.e2e.test.ts +14 -5
- package/src/op/activities/grafana-apply.test.ts +16 -2
- package/src/op/activities/grafana-apply.ts +29 -3
- package/src/op/activities/index.ts +1 -1
- package/src/ownership.ts +19 -0
- package/src/panels.test.ts +3 -1
- package/src/pin.ts +12 -0
- package/src/plugin.test.ts +9 -2
- package/src/plugin.ts +9 -0
- package/src/promql-check.ts +5 -1
- package/src/query-models.ts +94 -0
- package/src/query.test.ts +166 -0
- package/src/query.ts +177 -12
- package/src/schema/azuremonitor.gen.ts +381 -0
- package/src/schema/bigquery.gen.ts +110 -0
- package/src/schema/cloudwatch.gen.ts +423 -0
- package/src/schema/elasticsearch.gen.ts +508 -0
- package/src/schema/googlecloudmonitoring.gen.ts +330 -0
- package/src/schema/grafanapyroscope.gen.ts +66 -0
- package/src/schema/index.ts +6 -0
- package/src/schema/table.gen.ts +14 -0
- package/src/schema-validate.ts +58 -7
- package/src/skill-defs.ts +17 -0
- package/src/skills/chant-grafana-operations.md +108 -0
- package/src/skills/chant-grafana.md +5 -4
- package/src/spec/overlay/table.overlay.json +19 -0
- package/src/spec/schemas/azuremonitor.jsonschema.json +679 -0
- package/src/spec/schemas/bigquery.jsonschema.json +290 -0
- package/src/spec/schemas/cloudwatch.jsonschema.json +721 -0
- package/src/spec/schemas/elasticsearch.jsonschema.json +1695 -0
- package/src/spec/schemas/googlecloudmonitoring.jsonschema.json +488 -0
- package/src/spec/schemas/grafanapyroscope.jsonschema.json +93 -0
- package/src/spec/schemas.gen.ts +40 -0
- package/src/spec/schemas.ts +9 -7
- package/src/validate-alerting.ts +4 -2
- package/src/validate-output.ts +96 -4
- package/src/validate.ts +14 -0
- package/src/validation.ts +27 -0
- package/src/variables.ts +113 -6
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"AdhocVariable": {
|
|
3
|
+
"resourceType": "Grafana::Variable::adhoc",
|
|
4
|
+
"kind": "resource",
|
|
5
|
+
"lexicon": "grafana"
|
|
6
|
+
},
|
|
2
7
|
"AlertListPanel": {
|
|
3
8
|
"resourceType": "Grafana::Panel::alertlist",
|
|
4
9
|
"kind": "resource",
|
|
@@ -19,6 +24,11 @@
|
|
|
19
24
|
"kind": "resource",
|
|
20
25
|
"lexicon": "grafana"
|
|
21
26
|
},
|
|
27
|
+
"AzureMonitorQuery": {
|
|
28
|
+
"resourceType": "Grafana::Query::grafana-azure-monitor-datasource",
|
|
29
|
+
"kind": "resource",
|
|
30
|
+
"lexicon": "grafana"
|
|
31
|
+
},
|
|
22
32
|
"BarChartPanel": {
|
|
23
33
|
"resourceType": "Grafana::Panel::barchart",
|
|
24
34
|
"kind": "resource",
|
|
@@ -29,6 +39,11 @@
|
|
|
29
39
|
"kind": "resource",
|
|
30
40
|
"lexicon": "grafana"
|
|
31
41
|
},
|
|
42
|
+
"BigQueryQuery": {
|
|
43
|
+
"resourceType": "Grafana::Query::grafana-bigquery-datasource",
|
|
44
|
+
"kind": "resource",
|
|
45
|
+
"lexicon": "grafana"
|
|
46
|
+
},
|
|
32
47
|
"CanvasPanel": {
|
|
33
48
|
"resourceType": "Grafana::Panel::canvas",
|
|
34
49
|
"kind": "resource",
|
|
@@ -39,6 +54,16 @@
|
|
|
39
54
|
"kind": "resource",
|
|
40
55
|
"lexicon": "grafana"
|
|
41
56
|
},
|
|
57
|
+
"CloudMonitoringQuery": {
|
|
58
|
+
"resourceType": "Grafana::Query::stackdriver",
|
|
59
|
+
"kind": "resource",
|
|
60
|
+
"lexicon": "grafana"
|
|
61
|
+
},
|
|
62
|
+
"CloudWatchQuery": {
|
|
63
|
+
"resourceType": "Grafana::Query::cloudwatch",
|
|
64
|
+
"kind": "resource",
|
|
65
|
+
"lexicon": "grafana"
|
|
66
|
+
},
|
|
42
67
|
"ConstantVariable": {
|
|
43
68
|
"resourceType": "Grafana::Variable::constant",
|
|
44
69
|
"kind": "resource",
|
|
@@ -69,11 +94,21 @@
|
|
|
69
94
|
"kind": "resource",
|
|
70
95
|
"lexicon": "grafana"
|
|
71
96
|
},
|
|
97
|
+
"DatasourceProvisioning": {
|
|
98
|
+
"resourceType": "Grafana::DatasourceProvisioning",
|
|
99
|
+
"kind": "resource",
|
|
100
|
+
"lexicon": "grafana"
|
|
101
|
+
},
|
|
72
102
|
"DatasourceVariable": {
|
|
73
103
|
"resourceType": "Grafana::Variable::datasource",
|
|
74
104
|
"kind": "resource",
|
|
75
105
|
"lexicon": "grafana"
|
|
76
106
|
},
|
|
107
|
+
"ElasticsearchQuery": {
|
|
108
|
+
"resourceType": "Grafana::Query::elasticsearch",
|
|
109
|
+
"kind": "resource",
|
|
110
|
+
"lexicon": "grafana"
|
|
111
|
+
},
|
|
77
112
|
"ExternalDatasource": {
|
|
78
113
|
"resourceType": "Grafana::ExternalDatasource",
|
|
79
114
|
"kind": "resource",
|
|
@@ -84,6 +119,11 @@
|
|
|
84
119
|
"kind": "resource",
|
|
85
120
|
"lexicon": "grafana"
|
|
86
121
|
},
|
|
122
|
+
"Folder": {
|
|
123
|
+
"resourceType": "Grafana::Folder",
|
|
124
|
+
"kind": "resource",
|
|
125
|
+
"lexicon": "grafana"
|
|
126
|
+
},
|
|
87
127
|
"GaugePanel": {
|
|
88
128
|
"resourceType": "Grafana::Panel::gauge",
|
|
89
129
|
"kind": "resource",
|
|
@@ -94,6 +134,11 @@
|
|
|
94
134
|
"kind": "resource",
|
|
95
135
|
"lexicon": "grafana"
|
|
96
136
|
},
|
|
137
|
+
"GroupByVariable": {
|
|
138
|
+
"resourceType": "Grafana::Variable::groupby",
|
|
139
|
+
"kind": "resource",
|
|
140
|
+
"lexicon": "grafana"
|
|
141
|
+
},
|
|
97
142
|
"HeatmapPanel": {
|
|
98
143
|
"resourceType": "Grafana::Panel::heatmap",
|
|
99
144
|
"kind": "resource",
|
|
@@ -124,11 +169,21 @@
|
|
|
124
169
|
"kind": "resource",
|
|
125
170
|
"lexicon": "grafana"
|
|
126
171
|
},
|
|
172
|
+
"MSSQLQuery": {
|
|
173
|
+
"resourceType": "Grafana::Query::mssql",
|
|
174
|
+
"kind": "resource",
|
|
175
|
+
"lexicon": "grafana"
|
|
176
|
+
},
|
|
127
177
|
"MuteTiming": {
|
|
128
178
|
"resourceType": "Grafana::MuteTiming",
|
|
129
179
|
"kind": "resource",
|
|
130
180
|
"lexicon": "grafana"
|
|
131
181
|
},
|
|
182
|
+
"MySQLQuery": {
|
|
183
|
+
"resourceType": "Grafana::Query::mysql",
|
|
184
|
+
"kind": "resource",
|
|
185
|
+
"lexicon": "grafana"
|
|
186
|
+
},
|
|
132
187
|
"NodeGraphPanel": {
|
|
133
188
|
"resourceType": "Grafana::Panel::nodeGraph",
|
|
134
189
|
"kind": "resource",
|
|
@@ -149,11 +204,21 @@
|
|
|
149
204
|
"kind": "resource",
|
|
150
205
|
"lexicon": "grafana"
|
|
151
206
|
},
|
|
207
|
+
"PostgresQuery": {
|
|
208
|
+
"resourceType": "Grafana::Query::grafana-postgresql-datasource",
|
|
209
|
+
"kind": "resource",
|
|
210
|
+
"lexicon": "grafana"
|
|
211
|
+
},
|
|
152
212
|
"PromQuery": {
|
|
153
213
|
"resourceType": "Grafana::Query::prometheus",
|
|
154
214
|
"kind": "resource",
|
|
155
215
|
"lexicon": "grafana"
|
|
156
216
|
},
|
|
217
|
+
"PyroscopeQuery": {
|
|
218
|
+
"resourceType": "Grafana::Query::grafana-pyroscope-datasource",
|
|
219
|
+
"kind": "resource",
|
|
220
|
+
"lexicon": "grafana"
|
|
221
|
+
},
|
|
157
222
|
"QueryVariable": {
|
|
158
223
|
"resourceType": "Grafana::Variable::query",
|
|
159
224
|
"kind": "resource",
|
|
@@ -194,6 +259,11 @@
|
|
|
194
259
|
"kind": "resource",
|
|
195
260
|
"lexicon": "grafana"
|
|
196
261
|
},
|
|
262
|
+
"SwitchVariable": {
|
|
263
|
+
"resourceType": "Grafana::Variable::switch",
|
|
264
|
+
"kind": "resource",
|
|
265
|
+
"lexicon": "grafana"
|
|
266
|
+
},
|
|
197
267
|
"TablePanel": {
|
|
198
268
|
"resourceType": "Grafana::Panel::table",
|
|
199
269
|
"kind": "resource",
|
|
@@ -18,7 +18,7 @@ import { join } from "path";
|
|
|
18
18
|
import * as ts from "typescript";
|
|
19
19
|
import { GrafanaParser } from "./parser";
|
|
20
20
|
import { GrafanaGenerator } from "./generator";
|
|
21
|
-
import { ALERTING, COMMUNITY, KUBE_PROMETHEUS, UI_EXPORTS, V2_EXPORTS, exampleOutputs, pkgDir, read, removeDir, repoRoot, writeFiles } from "./testdata/fixtures";
|
|
21
|
+
import { ALERTING, COMMUNITY, KUBE_PROMETHEUS, PROVISIONING, UI_EXPORTS, V2_EXPORTS, exampleOutputs, pkgDir, read, removeDir, repoRoot, writeFiles } from "./testdata/fixtures";
|
|
22
22
|
|
|
23
23
|
type Files = Array<{ path: string; content: string }>;
|
|
24
24
|
|
|
@@ -68,13 +68,25 @@ function byProperty(errors: string[]): Record<string, number> {
|
|
|
68
68
|
|
|
69
69
|
describe("the generated source type-checks against the lexicon's types", () => {
|
|
70
70
|
test("Grafana's UI exports, the v2 dashboards and the examples' dashboards type-check clean", async () => {
|
|
71
|
-
const projects: Record<string, Files> = Object.fromEntries(
|
|
71
|
+
const projects: Record<string, Files> = Object.fromEntries(
|
|
72
|
+
[...UI_EXPORTS, ...V2_EXPORTS, "provisioning/typed-plugins.datasources.yaml"].map((f) => [f, generate(read(f))]),
|
|
73
|
+
);
|
|
72
74
|
for (const [name, text] of await exampleOutputs()) {
|
|
73
75
|
if (/^[^/]+\/dashboards\/.*\.json$/.test(name)) projects[name] = generate(text);
|
|
74
76
|
}
|
|
75
77
|
expect(typeErrors(projects)).toEqual([]);
|
|
76
78
|
}, 180_000);
|
|
77
79
|
|
|
80
|
+
test("real-world provisioning files: settings the pinned plugins no longer read are where tsc points", () => {
|
|
81
|
+
const found: Record<string, Record<string, number>> = {};
|
|
82
|
+
for (const file of PROVISIONING) found[file] = byProperty(typeErrors({ [file]: generate(read(file)) }));
|
|
83
|
+
expect(found).toEqual({
|
|
84
|
+
"provisioning/typed-plugins.datasources.yaml": {},
|
|
85
|
+
// Tempo's lokiSearch, which tempo 13.1.5 (bundled with Grafana 13.2.2) no longer reads.
|
|
86
|
+
"provisioning/docker-otel-lgtm.datasources.yaml": { lokiSearch: 1 },
|
|
87
|
+
});
|
|
88
|
+
}, 120_000);
|
|
89
|
+
|
|
78
90
|
test("alerting provisioning files, and what the alerting example builds, type-check clean", async () => {
|
|
79
91
|
const projects: Record<string, Files> = Object.fromEntries(ALERTING.map((f) => [f, generate(read(f))]));
|
|
80
92
|
for (const [name, text] of await exampleOutputs()) {
|
|
@@ -90,7 +102,7 @@ describe("the generated source type-checks against the lexicon's types", () => {
|
|
|
90
102
|
expect(found).toEqual({
|
|
91
103
|
"community/node-exporter-full.json": { step: 273, metric: 1 },
|
|
92
104
|
"community/k8s-views-global.json": {},
|
|
93
|
-
"community/k8s-views-pods.json": {
|
|
105
|
+
"community/k8s-views-pods.json": { wrapText: 2 },
|
|
94
106
|
"community/traefik.json": {},
|
|
95
107
|
"community/redis.json": { metric: 11, step: 6, time_options: 1, unitScale: 13 },
|
|
96
108
|
"community/prometheus-2-stats.json": { metric: 3, now: 1, step: 18 },
|
package/src/import/generator.ts
CHANGED
|
@@ -405,6 +405,7 @@ export function generatePlanModules(plan: Plan): {
|
|
|
405
405
|
classRef = className;
|
|
406
406
|
}
|
|
407
407
|
mod.declarables++;
|
|
408
|
+
const typed = d.typeArguments && d.typeArguments.length > 0 ? `${classRef}<${d.typeArguments.join(", ")}>` : classRef;
|
|
408
409
|
const entries: Array<[string, string]> = [];
|
|
409
410
|
for (const [key, raw] of Object.entries(d.props ?? {})) {
|
|
410
411
|
if (raw === undefined) continue;
|
|
@@ -415,7 +416,7 @@ export function generatePlanModules(plan: Plan): {
|
|
|
415
416
|
}
|
|
416
417
|
mod.types.add("PropsOf");
|
|
417
418
|
const cn = constName(`${name}${pascal(key) || "Value"}`);
|
|
418
|
-
const head = `const ${cn}: PropsOf<typeof ${
|
|
419
|
+
const head = `const ${cn}: PropsOf<typeof ${typed}>[${JSON.stringify(key)}] = `;
|
|
419
420
|
lines.push(`${head}${tsLiteral(value, 0, head.length)};`);
|
|
420
421
|
entries.push([key, cn]);
|
|
421
422
|
}
|
package/src/import/mappings.ts
CHANGED
|
@@ -8,13 +8,19 @@
|
|
|
8
8
|
* in `panels.ts` is used by the importer with no change here. Any other
|
|
9
9
|
* type is declared in the imported source with `definePanel`.
|
|
10
10
|
* - Query types (#2951): the same, through `registeredQueries()`, keyed by
|
|
11
|
-
* datasource plugin type
|
|
11
|
+
* datasource plugin type or one of its `aliases` (`postgres`); any other
|
|
12
|
+
* type gets a `defineQuery`.
|
|
13
|
+
* - Datasource links in a provisioning file (#2951): `JSONDATA_LINKS` in
|
|
14
|
+
* `../datasource-settings.ts` lists the `jsonData` fields that hold another
|
|
15
|
+
* datasource's uid and the plugin types each takes; the parser turns such a
|
|
16
|
+
* uid into a reference. Add an entry with the `LinkedDatasource` field.
|
|
12
17
|
* - Variable types (#2952): `VARIABLE_MAPPINGS` has one entry per type the
|
|
13
|
-
* lexicon has a class for. A type with no entry (
|
|
14
|
-
* is reported and left out.
|
|
15
|
-
* - Dashboard and panel fields (
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
+
* lexicon has a class for. A type with no entry (`system`, `snapshot`,
|
|
19
|
+
* which Grafana does not save) is reported and left out.
|
|
20
|
+
* - Dashboard and panel fields (any new prop): `DASHBOARD_FIELDS`,
|
|
21
|
+
* `PANEL_FIELDS` and `ROW_FIELDS` list the JSON keys copied onto a prop as
|
|
22
|
+
* they are. Annotations (#2953) are read by the parser's `annotations()`,
|
|
23
|
+
* which resolves their datasources. A key in none of the tables, and not
|
|
18
24
|
* handled by the parser, is reported and left out, unless it is at the
|
|
19
25
|
* value Grafana assumes when it is missing (see `./normalize.ts`).
|
|
20
26
|
*/
|
|
@@ -22,7 +28,7 @@
|
|
|
22
28
|
import { registeredPanels, type PanelDefinition } from "../panels";
|
|
23
29
|
import { registeredQueries, type QueryDefinition } from "../query";
|
|
24
30
|
import type { VariableHide } from "../variables";
|
|
25
|
-
import { splitValues } from "./normalize";
|
|
31
|
+
import { deepEqual, splitValues } from "./normalize";
|
|
26
32
|
|
|
27
33
|
type Json = Record<string, unknown>;
|
|
28
34
|
|
|
@@ -96,7 +102,7 @@ export function builtinPanelFor(type: string): PanelDefinition | undefined {
|
|
|
96
102
|
|
|
97
103
|
/** The class a query to a datasource of this plugin type is declared with, when chant ships one. */
|
|
98
104
|
export function builtinQueryFor(datasourceType: string): QueryDefinition | undefined {
|
|
99
|
-
return registeredQueries().find((d) => d.builtin && d.datasourceType === datasourceType);
|
|
105
|
+
return registeredQueries().find((d) => d.builtin && (d.datasourceType === datasourceType || d.aliases?.includes(datasourceType)));
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
/**
|
|
@@ -166,29 +172,15 @@ export const VARIABLE_MAPPINGS: Readonly<Record<string, VariableMapping>> = {
|
|
|
166
172
|
keys: ["datasource", "query", "definition", "regex", "refresh", "sort", "options", "current", ...MULTI_KEYS],
|
|
167
173
|
convert(ctx) {
|
|
168
174
|
const { json } = ctx;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
query = str(obj.query);
|
|
173
|
-
if (query === undefined) {
|
|
174
|
-
ctx.dropVariable("its query is an object with no query string, which QueryVariable cannot hold yet (#2952)");
|
|
175
|
-
return undefined;
|
|
176
|
-
}
|
|
177
|
-
// refId is the editor's own id for the query, which Grafana gives the string form back.
|
|
178
|
-
const rest = Object.keys(obj).filter((k) => k !== "query" && k !== "refId");
|
|
179
|
-
ctx.replace(
|
|
180
|
-
"query",
|
|
181
|
-
query,
|
|
182
|
-
rest.length === 0 ? undefined : `is an object; it is written as its query string, and its ${rest.join(" and ")} ${rest.length === 1 ? "is" : "are"} not carried (#2952)`,
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
query ??= "";
|
|
186
|
-
if (nonEmpty(json.definition) && json.definition !== query) ctx.drop("definition", `(it differs from the query, which is kept)`);
|
|
175
|
+
// The object form (Prometheus writes `{ qryType, query, refId }`) is carried as it is: the datasource reads all of it.
|
|
176
|
+
const query: string | Json = json.query !== null && typeof json.query === "object" && !Array.isArray(json.query) ? (json.query as Json) : (str(json.query) ?? "");
|
|
177
|
+
const text = typeof query === "string" ? query : str(query.query);
|
|
187
178
|
const refresh = typeof json.refresh === "number" ? json.refresh : 1;
|
|
188
179
|
if (refresh === 0 && nonEmpty(json.options)) {
|
|
189
180
|
ctx.drop("options", "(QueryVariable does not store options; with refresh never, Grafana shows none until it is refreshed)");
|
|
190
181
|
}
|
|
191
182
|
const out: Json = { datasource: ctx.datasource(json.datasource, `${ctx.path}/datasource`), query };
|
|
183
|
+
if (nonEmpty(json.definition) && json.definition !== text) out.definition = json.definition;
|
|
192
184
|
if (out.datasource === undefined) {
|
|
193
185
|
ctx.dropVariable("it has no datasource chant can refer to, and QueryVariable needs one");
|
|
194
186
|
return undefined;
|
|
@@ -258,7 +250,87 @@ export const VARIABLE_MAPPINGS: Readonly<Record<string, VariableMapping>> = {
|
|
|
258
250
|
return value === "" ? {} : { value };
|
|
259
251
|
},
|
|
260
252
|
},
|
|
253
|
+
adhoc: {
|
|
254
|
+
className: "AdhocVariable",
|
|
255
|
+
keys: ["datasource", "filters", "baseFilters", "defaultKeys", "allowCustomValue", "enableGroupBy", "current", "options"],
|
|
256
|
+
convert(ctx) {
|
|
257
|
+
const { json } = ctx;
|
|
258
|
+
const out: Json = { datasource: ctx.datasource(json.datasource, `${ctx.path}/datasource`) };
|
|
259
|
+
if (out.datasource === undefined) {
|
|
260
|
+
ctx.dropVariable("it has no datasource chant can refer to, and AdhocVariable needs one");
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
if (filters(json.filters)) out.filters = json.filters;
|
|
264
|
+
if (filters(json.baseFilters)) out.baseFilters = json.baseFilters;
|
|
265
|
+
if (Array.isArray(json.defaultKeys)) out.defaultKeys = json.defaultKeys;
|
|
266
|
+
if (json.allowCustomValue === false) out.allowCustomValue = false;
|
|
267
|
+
if (json.enableGroupBy === true) out.enableGroupBy = true;
|
|
268
|
+
return out;
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
groupby: {
|
|
272
|
+
className: "GroupByVariable",
|
|
273
|
+
// A group by variable is always multi-value; Grafana reads no `multi` on it.
|
|
274
|
+
keys: ["datasource", "options", "current", "defaultValue", "allowCustomValue", "multi"],
|
|
275
|
+
convert(ctx) {
|
|
276
|
+
const { json } = ctx;
|
|
277
|
+
const out: Json = { datasource: ctx.datasource(json.datasource, `${ctx.path}/datasource`) };
|
|
278
|
+
if (out.datasource === undefined) {
|
|
279
|
+
ctx.dropVariable("it has no datasource chant can refer to, and GroupByVariable needs one");
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
if (Array.isArray(json.options) && json.options.length > 0) out.options = json.options.map(keyOption);
|
|
283
|
+
const def = json.defaultValue as Json | undefined;
|
|
284
|
+
if (nonEmpty(def)) out.defaultValue = Array.isArray(def!.value) && deepEqual(def!.text, def!.value) ? def!.value : def;
|
|
285
|
+
// The build writes the default as the current selection when there is no other.
|
|
286
|
+
if (nonEmpty(json.current) && !deepEqual(json.current, def)) out.current = json.current;
|
|
287
|
+
if (json.allowCustomValue === false) out.allowCustomValue = false;
|
|
288
|
+
return out;
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
switch: {
|
|
292
|
+
className: "SwitchVariable",
|
|
293
|
+
// Grafana reads no `query` on a switch (the v2 down-conversion writes an empty one).
|
|
294
|
+
keys: ["current", "options", "query"],
|
|
295
|
+
convert(ctx) {
|
|
296
|
+
const { json } = ctx;
|
|
297
|
+
const options = Array.isArray(json.options) ? json.options : [];
|
|
298
|
+
// Grafana keeps the enabled value first and the disabled value second.
|
|
299
|
+
const on = firstValue(options[0]) ?? "true";
|
|
300
|
+
const off = firstValue(options[1]) ?? "false";
|
|
301
|
+
const out: Json = {};
|
|
302
|
+
if (on !== "true") out.enabledValue = on;
|
|
303
|
+
if (off !== "false") out.disabledValue = off;
|
|
304
|
+
const value = firstValue(json.current);
|
|
305
|
+
if (value === on) out.enabled = true;
|
|
306
|
+
else if (value !== off) {
|
|
307
|
+
ctx.replace("current", { text: off, value: off }, `is ${value === undefined ? "missing" : `"${value}", neither the enabled nor the disabled value`}; the switch starts off`);
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
},
|
|
311
|
+
},
|
|
261
312
|
};
|
|
262
313
|
|
|
314
|
+
/** An ad hoc filter list, or undefined when it is empty (the build writes `[]`). */
|
|
315
|
+
function filters(v: unknown): unknown[] | undefined {
|
|
316
|
+
return Array.isArray(v) && v.length > 0 ? v : undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** A `{ text, value }` option as the shortest prop that builds back to it: the value when the text is the same. */
|
|
320
|
+
function keyOption(o: unknown): unknown {
|
|
321
|
+
if (o && typeof o === "object" && !Array.isArray(o)) {
|
|
322
|
+
const { text, value, ...rest } = o as Json;
|
|
323
|
+
if (Object.keys(rest).length === 0 && typeof value === "string" && text === value) return value;
|
|
324
|
+
}
|
|
325
|
+
return o;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** The first value of a `VariableOption`'s value, which may be a list. */
|
|
329
|
+
function firstValue(o: unknown): string | undefined {
|
|
330
|
+
if (!o || typeof o !== "object") return undefined;
|
|
331
|
+
const v = (o as Json).value;
|
|
332
|
+
return typeof v === "string" ? v : Array.isArray(v) && typeof v[0] === "string" ? v[0] : undefined;
|
|
333
|
+
}
|
|
334
|
+
|
|
263
335
|
/** Keys every variable type reads: the ones `CommonVariableProps` has, and `type`. */
|
|
264
336
|
export const COMMON_VARIABLE_KEYS: readonly string[] = ["type", "name", "label", "description", "hide", "skipUrlSync"];
|
package/src/import/model.ts
CHANGED
|
@@ -59,6 +59,13 @@ export interface Declaration {
|
|
|
59
59
|
readonly className?: string;
|
|
60
60
|
/** For `new`: the id of the `CustomClass` declaring `className`, when it is not a package export. */
|
|
61
61
|
readonly customClass?: string;
|
|
62
|
+
/**
|
|
63
|
+
* For `new`: type arguments for the class, as TypeScript source. The
|
|
64
|
+
* consts lifted out of the props are typed `PropsOf<typeof Class<...>>`,
|
|
65
|
+
* so a `Datasource<"prometheus">`'s `jsonData` const is typed as
|
|
66
|
+
* Prometheus settings rather than any plugin's.
|
|
67
|
+
*/
|
|
68
|
+
readonly typeArguments?: readonly string[];
|
|
62
69
|
/** For `new`: the constructor's props, in source order. */
|
|
63
70
|
readonly props?: Record<string, unknown>;
|
|
64
71
|
/** For `value`: the value, and its type annotation with the package types it names. */
|
package/src/import/normalize.ts
CHANGED
|
@@ -89,6 +89,7 @@ export const VARIABLE_DEFAULTS: Readonly<Json> = {
|
|
|
89
89
|
/** Per variable type, Grafana's value for a key the JSON leaves out, over `VARIABLE_DEFAULTS`. */
|
|
90
90
|
export const VARIABLE_TYPE_DEFAULTS: Readonly<Record<string, Json>> = {
|
|
91
91
|
query: { refresh: 1 },
|
|
92
|
+
adhoc: { filters: [], baseFilters: [], enableGroupBy: false },
|
|
92
93
|
datasource: { refresh: 1 },
|
|
93
94
|
interval: { refresh: 2, auto: false, auto_count: 30, auto_min: "10s" },
|
|
94
95
|
};
|
|
@@ -264,12 +265,40 @@ function normalizeVariable(variable: unknown): unknown {
|
|
|
264
265
|
else if (out.datasource !== undefined) out.datasource = canonicalRef(out.datasource);
|
|
265
266
|
const query = typeof out.query === "string" ? out.query : undefined;
|
|
266
267
|
switch (type) {
|
|
267
|
-
case "query":
|
|
268
|
-
// `definition` is the query as the editor shows it; Grafana reads `query
|
|
269
|
-
|
|
268
|
+
case "query": {
|
|
269
|
+
// `definition` is the query as the editor shows it; Grafana reads `query` (an object query's text is its `query`).
|
|
270
|
+
const text = query ?? (isObject(out.query) && typeof out.query.query === "string" ? out.query.query : undefined);
|
|
271
|
+
if (out.definition === undefined || out.definition === "" || (text !== undefined && out.definition === text)) delete out.definition;
|
|
270
272
|
// Grafana runs the query again when the dashboard loads (or the time range changes).
|
|
271
273
|
if (out.refresh !== 0) delete out.options;
|
|
272
274
|
break;
|
|
275
|
+
}
|
|
276
|
+
case "adhoc":
|
|
277
|
+
// The filters are the selection; Grafana reads neither of these for an ad hoc variable.
|
|
278
|
+
delete out.current;
|
|
279
|
+
delete out.options;
|
|
280
|
+
break;
|
|
281
|
+
case "groupby": {
|
|
282
|
+
// With nothing selected, Grafana starts from the default.
|
|
283
|
+
const cur = isObject(out.current) ? out.current : undefined;
|
|
284
|
+
const empty = !cur || Object.keys(cur).length === 0 || (Array.isArray(cur.value) && cur.value.length === 0);
|
|
285
|
+
if (empty || deepEqual(cur, out.defaultValue)) delete out.current;
|
|
286
|
+
if (Array.isArray(out.options) && out.options.length === 0) delete out.options;
|
|
287
|
+
// Always multi-value.
|
|
288
|
+
delete out.multi;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case "switch": {
|
|
292
|
+
// Grafana reads only the values: the enabled one first, the disabled one second, and the current one.
|
|
293
|
+
const value = (o: unknown): unknown => (isObject(o) ? (Array.isArray(o.value) ? o.value[0] : o.value) : undefined);
|
|
294
|
+
const opts = Array.isArray(out.options) ? out.options : [];
|
|
295
|
+
const on = value(opts[0]) ?? "true";
|
|
296
|
+
const off = value(opts[1]) ?? "false";
|
|
297
|
+
out.options = [on, off];
|
|
298
|
+
out.current = value(out.current) ?? off;
|
|
299
|
+
delete out.query;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
273
302
|
case "custom":
|
|
274
303
|
case "interval": {
|
|
275
304
|
const values = splitValues(query ?? "");
|
|
@@ -233,26 +233,84 @@ describe("variables", () => {
|
|
|
233
233
|
expect(p.declarations.filter((d) => d.module === "variables").map((d) => d.id)[0]).toBe("variable:ds");
|
|
234
234
|
});
|
|
235
235
|
|
|
236
|
-
test("a type the lexicon has no class for is reported and left out
|
|
237
|
-
const { plan: p, warnings, edits } = plan([{ type: "
|
|
236
|
+
test("a type the lexicon has no class for is reported and left out", () => {
|
|
237
|
+
const { plan: p, warnings, edits } = plan([{ type: "system", name: "__org", query: "" }]);
|
|
238
238
|
expect(p.declarations.filter((d) => d.module === "variables")).toEqual([]);
|
|
239
|
-
expect(warnings).toEqual(['variable "
|
|
239
|
+
expect(warnings).toEqual(['variable "__org" (system) is not carried: chant has no system variable, so it is left out']);
|
|
240
240
|
expect(edits).toContainEqual({ op: "remove", path: "/templating/list/0" });
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
-
test("
|
|
243
|
+
test("ad hoc, group by and switch variables", () => {
|
|
244
244
|
const { plan: p, warnings, edits } = plan([
|
|
245
|
-
{
|
|
245
|
+
{
|
|
246
|
+
type: "adhoc",
|
|
247
|
+
name: "Filters",
|
|
248
|
+
datasource: prom,
|
|
249
|
+
filters: [{ key: "namespace", operator: "=", value: "shop" }],
|
|
250
|
+
baseFilters: [],
|
|
251
|
+
defaultKeys: [{ text: "pod", value: "pod" }],
|
|
252
|
+
allowCustomValue: false,
|
|
253
|
+
enableGroupBy: false,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
type: "groupby",
|
|
257
|
+
name: "by",
|
|
258
|
+
datasource: prom,
|
|
259
|
+
options: [{ text: "pod", value: "pod" }, { text: "Node", value: "node" }],
|
|
260
|
+
current: { text: ["node"], value: ["node"] },
|
|
261
|
+
defaultValue: { text: ["pod"], value: ["pod"] },
|
|
262
|
+
},
|
|
263
|
+
{ type: "switch", name: "on", current: { text: "true", value: "true" }, options: [{ text: "true", value: "true" }, { text: "false", value: "false" }] },
|
|
264
|
+
{ type: "switch", name: "q", current: { text: "0.5", value: "0.5" }, options: [{ text: "0.99", value: "0.99" }, { text: "0.5", value: "0.5" }] },
|
|
246
265
|
]);
|
|
247
|
-
expect(
|
|
248
|
-
expect(edits
|
|
249
|
-
|
|
250
|
-
expect(
|
|
266
|
+
expect(warnings).toEqual([]);
|
|
267
|
+
expect(edits.filter((e) => "path" in e && e.path.startsWith("/templating"))).toEqual([]);
|
|
268
|
+
const props = (id: string) => [decl(p.declarations, id).className, decl(p.declarations, id).props];
|
|
269
|
+
expect(props("variable:Filters")).toEqual([
|
|
270
|
+
"AdhocVariable",
|
|
271
|
+
{
|
|
272
|
+
name: "Filters",
|
|
273
|
+
datasource: { $decl: "datasource:prometheus:prom" },
|
|
274
|
+
filters: [{ key: "namespace", operator: "=", value: "shop" }],
|
|
275
|
+
defaultKeys: [{ text: "pod", value: "pod" }],
|
|
276
|
+
allowCustomValue: false,
|
|
277
|
+
},
|
|
278
|
+
]);
|
|
279
|
+
expect(props("variable:by")).toEqual([
|
|
280
|
+
"GroupByVariable",
|
|
281
|
+
{
|
|
282
|
+
name: "by",
|
|
283
|
+
datasource: { $decl: "datasource:prometheus:prom" },
|
|
284
|
+
options: ["pod", { text: "Node", value: "node" }],
|
|
285
|
+
defaultValue: ["pod"],
|
|
286
|
+
current: { text: ["node"], value: ["node"] },
|
|
287
|
+
},
|
|
288
|
+
]);
|
|
289
|
+
expect(props("variable:on")).toEqual(["SwitchVariable", { name: "on", enabled: true }]);
|
|
290
|
+
expect(props("variable:q")).toEqual(["SwitchVariable", { name: "q", enabledValue: "0.99", disabledValue: "0.5" }]);
|
|
251
291
|
});
|
|
252
292
|
|
|
253
|
-
test("
|
|
254
|
-
const { warnings } = plan([{ type: "
|
|
255
|
-
expect(warnings).toEqual([
|
|
293
|
+
test("a switch whose current value is neither of its values starts off, and says so", () => {
|
|
294
|
+
const { warnings, edits } = plan([{ type: "switch", name: "s", current: { text: "maybe", value: "maybe" }, options: [{ text: "true", value: "true" }, { text: "false", value: "false" }] }]);
|
|
295
|
+
expect(warnings).toEqual(['variable "s": current is "maybe", neither the enabled nor the disabled value; the switch starts off']);
|
|
296
|
+
expect(edits).toContainEqual({ op: "replace", path: "/templating/list/0/current", value: { text: "false", value: "false" } });
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("an ad hoc or group by variable with no datasource chant can refer to is left out", () => {
|
|
300
|
+
const { warnings } = plan([{ type: "adhoc", name: "f", datasource: { type: "prometheus" }, filters: [] }]);
|
|
301
|
+
expect(warnings).toContainEqual(expect.stringMatching(/^variable "f" is not carried: it has no datasource chant can refer to, and AdhocVariable needs one/));
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("an object query is carried as it is, with a definition that differs from its text", () => {
|
|
305
|
+
const query = { qryType: 1, query: "label_values(job)", refId: "PrometheusVariableQueryEditor-VariableQuery" };
|
|
306
|
+
const { plan: p, warnings, edits } = plan([
|
|
307
|
+
{ type: "query", name: "job", datasource: prom, query, definition: "label_values(job)" },
|
|
308
|
+
{ type: "query", name: "ns", datasource: { type: "cloudwatch", uid: "cw" }, query: { namespace: "AWS/EC2" }, definition: "Namespaces" },
|
|
309
|
+
]);
|
|
310
|
+
expect(warnings).toEqual([]);
|
|
311
|
+
expect(edits.filter((e) => "path" in e && e.path.startsWith("/templating"))).toEqual([]);
|
|
312
|
+
expect(decl(p.declarations, "variable:job").props).toEqual({ name: "job", datasource: { $decl: "datasource:prometheus:prom" }, query });
|
|
313
|
+
expect(decl(p.declarations, "variable:ns").props).toMatchObject({ query: { namespace: "AWS/EC2" }, definition: "Namespaces" });
|
|
256
314
|
});
|
|
257
315
|
|
|
258
316
|
test("keys at Grafana's default are dropped quietly; others are named", () => {
|
|
@@ -387,26 +445,44 @@ describe("the dashboard", () => {
|
|
|
387
445
|
expect(warnings).toContain('dashboard: uid is missing, so the dashboard is given the uid "my-board", from its title');
|
|
388
446
|
});
|
|
389
447
|
|
|
390
|
-
test("the built-in annotation is Grafana's default; any other is
|
|
448
|
+
test("the built-in annotation is Grafana's default and left out; any other is carried (#2953)", () => {
|
|
391
449
|
const builtin = { builtIn: 1, datasource: { type: "datasource", uid: "grafana" }, enable: true, hide: true, iconColor: "rgba(0, 211, 255, 1)", name: "Annotations & Alerts", type: "dashboard" };
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
expect(
|
|
395
|
-
|
|
450
|
+
const only = planDashboard(dashboard({ annotations: { list: [builtin] } }));
|
|
451
|
+
expect(only.warnings).toEqual([]);
|
|
452
|
+
expect(only.plan.declarations.find((d) => d.id === "dashboard")!.props!.annotations).toBeUndefined();
|
|
453
|
+
const deploys = { name: "Deploys", datasource: { type: "prometheus", uid: "prom" }, iconColor: "red", expr: "changes(up[5m]) > 0", step: "60s", target: { refId: "Anno" } };
|
|
454
|
+
const other = planDashboard(dashboard({ annotations: { list: [builtin, deploys, { name: "Off", enable: false, iconColor: "blue", hide: null }] } }));
|
|
455
|
+
expect(other.warnings).toEqual(['annotation "Deploys": enable is missing, so it is written as true, which runs the query']);
|
|
456
|
+
const props = other.plan.declarations.find((d) => d.id === "dashboard")!.props!;
|
|
457
|
+
expect(props.annotations).toEqual([
|
|
458
|
+
{ name: "Deploys", datasource: { $decl: "datasource:prometheus:prom" }, iconColor: "red", expr: "changes(up[5m]) > 0", step: "60s", target: { refId: "Anno" } },
|
|
459
|
+
{ name: "Off", enable: false, iconColor: "blue" },
|
|
460
|
+
]);
|
|
461
|
+
expect(other.plan.declarations).toContainEqual(expect.objectContaining({ id: "datasource:prometheus:prom", className: "ExternalDatasource" }));
|
|
462
|
+
expect(other.edits).toContainEqual({ op: "replace", path: "/annotations/list/1/enable", value: true });
|
|
463
|
+
expect(other.edits).toContainEqual({ op: "remove", path: "/annotations/list/2/hide" });
|
|
396
464
|
});
|
|
397
465
|
});
|
|
398
466
|
|
|
399
467
|
describe("provisioning files", () => {
|
|
400
468
|
test("a datasource provisioning file: one Datasource each, unknown keys named", () => {
|
|
401
|
-
const ir = parse("apiVersion: 1\nprune: true\ndatasources:\n - name: Prometheus\n type: prometheus\n url: http://prom:9090\n password: x\n");
|
|
469
|
+
const ir = parse("apiVersion: 1\nprune: true\ndatasources:\n - name: Prometheus\n type: prometheus\n url: http://prom:9090\n password: x\nextra: 1\n");
|
|
402
470
|
const { plan } = ir.resources[0].properties as unknown as PlanResourceProperties;
|
|
403
471
|
expect(plan.declarations).toEqual([
|
|
404
472
|
expect.objectContaining({ className: "Datasource", props: { name: "Prometheus", type: "prometheus", url: "http://prom:9090" } }),
|
|
405
473
|
]);
|
|
406
|
-
expect(ir.warnings).toEqual([
|
|
407
|
-
|
|
408
|
-
|
|
474
|
+
expect(ir.warnings).toEqual(['datasource "Prometheus": password is not carried (no prop takes it)', "the provisioning file: extra is not carried (no prop takes it)"]);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test("a datasource provisioning file's prune and deleteDatasources become a DatasourceProvisioning (#2953)", () => {
|
|
478
|
+
const pruned = parse("apiVersion: 1\nprune: true\ndatasources: []\n").resources[0].properties as unknown as PlanResourceProperties;
|
|
479
|
+
expect(pruned.plan.declarations).toEqual([]);
|
|
480
|
+
const kept = parse("apiVersion: 1\ndeleteDatasources:\n - name: Old\n orgId: 2\n - name: Gone\ndatasources: []\n");
|
|
481
|
+
const { plan } = kept.resources[0].properties as unknown as PlanResourceProperties;
|
|
482
|
+
expect(plan.declarations).toEqual([
|
|
483
|
+
expect.objectContaining({ className: "DatasourceProvisioning", props: { prune: false, deleteDatasources: [{ name: "Old", orgId: 2 }, { name: "Gone" }] } }),
|
|
409
484
|
]);
|
|
485
|
+
expect(kept.warnings).toEqual([]);
|
|
410
486
|
});
|
|
411
487
|
|
|
412
488
|
test("a dashboard provisioning file: file providers only", () => {
|