Skip to content

Commit 9a197e2

Browse files
authoredMar 19, 2024
feat(custom): add helpers for dealing with panel IDs (grafana#179)
* fix(custom): allow to disable setting the panel ID * feat(custom): add parameter to disable overriding existing ids * feat(custom): add helpers for dealing with panel IDs
1 parent 6cdabe2 commit 9a197e2

File tree

25 files changed

+767
-128
lines changed

25 files changed

+767
-128
lines changed
 

‎custom/dashboard.libsonnet

+10-4
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet';
1313
+ self.time.withFrom('now-6h')
1414
+ self.time.withTo('now'),
1515

16-
withPanels(value): {
16+
withPanels(value, setPanelIDs=true): {
1717
_panels:: if std.isArray(value) then value else [value],
18-
panels: util.panel.setPanelIDs(self._panels),
18+
panels:
19+
if setPanelIDs
20+
then util.panel.setPanelIDs(self._panels)
21+
else self._panels,
1922
},
20-
withPanelsMixin(value): {
23+
withPanelsMixin(value, setPanelIDs=true): {
2124
_panels+:: if std.isArray(value) then value else [value],
22-
panels: util.panel.setPanelIDs(self._panels),
25+
panels:
26+
if setPanelIDs
27+
then util.panel.setPanelIDs(self._panels)
28+
else self._panels,
2329
},
2430

2531
graphTooltip+: {

‎custom/util/panel.libsonnet

+43-6
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ local xtd = import 'github.com/jsonnet-libs/xtd/main.libsonnet';
44
{
55
local this = self,
66

7+
// used in ../dashboard.libsonnet
78
'#setPanelIDs':: d.func.new(
89
|||
9-
`setPanelIDs` ensures that all `panels` have a unique ID, this functions is used in
10-
`dashboard.withPanels` and `dashboard.withPanelsMixin` to provide a consistent
11-
experience.
10+
`setPanelIDs` ensures that all `panels` have a unique ID, this function is used in `dashboard.withPanels` and `dashboard.withPanelsMixin` to provide a consistent experience.
1211
13-
used in ../dashboard.libsonnet
12+
`overrideExistingIDs` can be set to not replace existing IDs, consider validating the IDs with `validatePanelIDs()` to ensure there are no duplicate IDs.
1413
|||,
1514
args=[
1615
d.arg('panels', d.T.array),
16+
d.arg('overrideExistingIDs', d.T.bool, default=true),
1717
]
1818
),
19-
setPanelIDs(panels):
19+
setPanelIDs(panels, overrideExistingIDs=true):
2020
local infunc(panels, start=1) =
2121
std.foldl(
2222
function(acc, panel)
@@ -31,7 +31,12 @@ local xtd = import 'github.com/jsonnet-libs/xtd/main.libsonnet';
3131

3232
panels+: [
3333
panel
34-
+ { id: acc.index }
34+
+ (
35+
if overrideExistingIDs
36+
|| std.get(panel, 'id', null) == null
37+
then { id: acc.index }
38+
else {}
39+
)
3540
+ (
3641
if panel.type == 'row'
3742
&& 'panels' in panel
@@ -51,6 +56,38 @@ local xtd = import 'github.com/jsonnet-libs/xtd/main.libsonnet';
5156
).panels;
5257
infunc(panels),
5358

59+
'#getPanelIDs':: d.func.new(
60+
|||
61+
`getPanelIDs` returns an array with all panel IDs including IDs from panels in rows.
62+
|||,
63+
args=[
64+
d.arg('panels', d.T.array),
65+
]
66+
),
67+
getPanelIDs(panels):
68+
std.flattenArrays(
69+
std.map(
70+
function(panel)
71+
[panel.id]
72+
+ (if panel.type == 'row'
73+
then this.getPanelIDs(std.get(panel, 'panels', []))
74+
else []),
75+
panels
76+
)
77+
),
78+
79+
'#validatePanelIDs':: d.func.new(
80+
|||
81+
`validatePanelIDs` validates returns `false` if there are duplicate panel IDs in `panels`.
82+
|||,
83+
args=[
84+
d.arg('panels', d.T.array),
85+
]
86+
),
87+
validatePanelIDs(panels):
88+
local ids = this.getPanelIDs(panels);
89+
std.set(ids) == std.sort(ids),
90+
5491
'#sanitizePanel':: d.func.new(
5592
|||
5693
`sanitizePanel` ensures the panel has a valid `gridPos` and row panels have `collapsed` and `panels`. This function is recursively applied to panels inside row panels.

‎docs/API/util.md

+32-6
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,18 @@ Helper functions that work well with Grafonnet.
1111
* [`fn wrapPanels(panels, panelWidth, panelHeight, startY)`](#fn-gridwrappanels)
1212
* [`obj panel`](#obj-panel)
1313
* [`fn calculateLowestYforPanel(panel, panels)`](#fn-panelcalculatelowestyforpanel)
14+
* [`fn getPanelIDs(panels)`](#fn-panelgetpanelids)
1415
* [`fn getPanelsBeforeNextRow(panels)`](#fn-panelgetpanelsbeforenextrow)
1516
* [`fn groupPanelsInRows(panels)`](#fn-panelgrouppanelsinrows)
1617
* [`fn mapToRows(func, panels)`](#fn-panelmaptorows)
1718
* [`fn normalizeY(panels)`](#fn-panelnormalizey)
1819
* [`fn normalizeYInRow(rowPanel)`](#fn-panelnormalizeyinrow)
1920
* [`fn resolveCollapsedFlagOnRows(panels)`](#fn-panelresolvecollapsedflagonrows)
2021
* [`fn sanitizePanel(panel, defaultX=0, defaultY=0, defaultHeight=8, defaultWidth=8)`](#fn-panelsanitizepanel)
21-
* [`fn setPanelIDs(panels)`](#fn-panelsetpanelids)
22+
* [`fn setPanelIDs(panels, overrideExistingIDs=true)`](#fn-panelsetpanelids)
2223
* [`fn sortPanelsByXY(panels)`](#fn-panelsortpanelsbyxy)
2324
* [`fn sortPanelsInRow(rowPanel)`](#fn-panelsortpanelsinrow)
25+
* [`fn validatePanelIDs(panels)`](#fn-panelvalidatepanelids)
2426
* [`obj string`](#obj-string)
2527
* [`fn slugify(string)`](#fn-stringslugify)
2628

@@ -105,6 +107,18 @@ PARAMETERS:
105107

106108
`calculateLowestYforPanel` calculates Y for a given `panel` from the `gridPos` of an array of `panels`. This function is used in `normalizeY`.
107109

110+
#### fn panel.getPanelIDs
111+
112+
```jsonnet
113+
panel.getPanelIDs(panels)
114+
```
115+
116+
PARAMETERS:
117+
118+
* **panels** (`array`)
119+
120+
`getPanelIDs` returns an array with all panel IDs including IDs from panels in rows.
121+
108122
#### fn panel.getPanelsBeforeNextRow
109123

110124
```jsonnet
@@ -205,18 +219,18 @@ The default values for x,y,h,w are only applied if not already set.
205219
#### fn panel.setPanelIDs
206220

207221
```jsonnet
208-
panel.setPanelIDs(panels)
222+
panel.setPanelIDs(panels, overrideExistingIDs=true)
209223
```
210224

211225
PARAMETERS:
212226

213227
* **panels** (`array`)
228+
* **overrideExistingIDs** (`bool`)
229+
- default value: `true`
214230

215-
`setPanelIDs` ensures that all `panels` have a unique ID, this functions is used in
216-
`dashboard.withPanels` and `dashboard.withPanelsMixin` to provide a consistent
217-
experience.
231+
`setPanelIDs` ensures that all `panels` have a unique ID, this function is used in `dashboard.withPanels` and `dashboard.withPanelsMixin` to provide a consistent experience.
218232

219-
used in ../dashboard.libsonnet
233+
`overrideExistingIDs` can be set to not replace existing IDs, consider validating the IDs with `validatePanelIDs()` to ensure there are no duplicate IDs.
220234

221235
#### fn panel.sortPanelsByXY
222236

@@ -242,6 +256,18 @@ PARAMETERS:
242256

243257
`sortPanelsInRow` applies `sortPanelsByXY` on the panels in a rowPanel.
244258

259+
#### fn panel.validatePanelIDs
260+
261+
```jsonnet
262+
panel.validatePanelIDs(panels)
263+
```
264+
265+
PARAMETERS:
266+
267+
* **panels** (`array`)
268+
269+
`validatePanelIDs` validates returns `false` if there are duplicate panel IDs in `panels`.
270+
245271
### obj string
246272

247273

‎gen/grafonnet-v10.0.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.0.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.0.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.1.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.1.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.1.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.2.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.2.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.2.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.3.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.3.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.3.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.4.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.4.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v10.4.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v9.4.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v9.4.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v9.4.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v9.5.0/custom/dashboard.libsonnet

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v9.5.0/custom/util/panel.libsonnet

+43-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎gen/grafonnet-v9.5.0/docs/util.md

+32-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/util_test.jsonnet

+87
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,93 @@ test.new(std.thisFile)
9191
)
9292
)
9393
)
94+
+ (
95+
local panelsWithIntentionalManualIDs = [
96+
{ type: 'timeseries' },
97+
{ type: 'row' },
98+
{ type: 'timeseries' },
99+
{ type: 'stat' },
100+
{
101+
type: 'row',
102+
panels: [
103+
{ type: 'timeseries' },
104+
{ type: 'stat' },
105+
{ type: 'table', id: 500 },
106+
{ type: 'timeseries' },
107+
],
108+
},
109+
{ type: 'table' },
110+
{ type: 'timeseries' },
111+
];
112+
113+
local expected = [
114+
{ type: 'timeseries', id: 1 },
115+
{ type: 'row', id: 2 },
116+
{ type: 'timeseries', id: 3 },
117+
{ type: 'stat', id: 4 },
118+
{
119+
type: 'row',
120+
id: 5,
121+
panels: [
122+
{ type: 'timeseries', id: 6 },
123+
{ type: 'stat', id: 7 },
124+
{ type: 'table', id: 500 },
125+
{ type: 'timeseries', id: 9 },
126+
],
127+
},
128+
{ type: 'table', id: 10 },
129+
{ type: 'timeseries', id: 11 },
130+
];
131+
132+
local actual =
133+
util.panel.setPanelIDs(
134+
panelsWithIntentionalManualIDs,
135+
overrideExistingIDs=false,
136+
);
137+
138+
test.case.new(
139+
name='Panel IDs are sanitized without overriding existing IDs',
140+
test=test.expect.eqDiff(
141+
actual=actual,
142+
expected=expected,
143+
)
144+
)
145+
+ test.case.new(
146+
name='Panel IDs validation - success',
147+
test=test.expect.eqDiff(
148+
actual=util.panel.validatePanelIDs(expected),
149+
expected=true,
150+
)
151+
)
152+
)
153+
+ (
154+
local panelWithDuplicateIDs = [
155+
{ type: 'timeseries', id: 1 },
156+
{ type: 'row', id: 2 },
157+
{ type: 'timeseries', id: 3 },
158+
{ type: 'stat', id: 4 },
159+
{
160+
type: 'row',
161+
id: 5,
162+
panels: [
163+
{ type: 'timeseries', id: 6 },
164+
{ type: 'stat', id: 7 },
165+
{ type: 'table', id: 3 },
166+
{ type: 'timeseries', id: 9 },
167+
],
168+
},
169+
{ type: 'table', id: 10 },
170+
{ type: 'timeseries', id: 11 },
171+
];
172+
173+
test.case.new(
174+
name='Panel IDs validation - failure',
175+
test=test.expect.eqDiff(
176+
actual=util.panel.validatePanelIDs(panelWithDuplicateIDs),
177+
expected=false,
178+
)
179+
)
180+
)
94181

95182
// util.dashboard.getOptionsForCustomQuery
96183
+ (

0 commit comments

Comments
 (0)
Please sign in to comment.