andoncloud-prometheus-widget 1.3.18 → 1.3.19

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/dist/index.js CHANGED
@@ -1,19 +1,18 @@
1
1
  import { t as thumbnail_default } from "./thumbnail-B_DBfYD0.js";
2
2
  import { registerTranslations } from "andoncloud-sdk";
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { generateTimeRange, getCurrentShift, getRecentShift, normalizeShifts, useGqlClients } from "andoncloud-dashboard-toolkit";
5
5
  import { BaseWidget } from "andoncloud-widget-base";
6
6
  import { useTranslation } from "react-i18next";
7
7
  import { Clear } from "@mui/icons-material";
8
8
  import { TabContext, TabList, TabPanel } from "@mui/lab";
9
- import { Box, Button, FormControl, FormHelperText, IconButton, InputLabel, MenuItem, Select, Stack, Tab, TextField } from "@mui/material";
9
+ import { Alert, Box, Button, Divider, FormControl, FormHelperText, IconButton, InputLabel, MenuItem, Select, Stack, Tab, TextField, Typography } from "@mui/material";
10
10
  import { getIn } from "formik";
11
- import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
12
  import "chartjs-adapter-dayjs-4";
13
13
  import { Line } from "react-chartjs-2";
14
14
  import { CategoryScale, Chart, Filler, Legend, LineElement, LinearScale, PointElement, TimeScale, Tooltip } from "chart.js";
15
15
  import dayjs from "dayjs";
16
- import pick from "lodash.pick";
17
16
  import { PrometheusDriver } from "prometheus-query";
18
17
  import Color from "color";
19
18
  import { satisfies } from "compare-versions";
@@ -26,51 +25,73 @@ const resources = {
26
25
  "addAnotherDisplayedQueryParameter": "Add another displayed query parameter",
27
26
  "addAnotherQuery": "Add another query",
28
27
  "advanced": "Advanced",
28
+ "credentialsHelper": "Credentials are optional. Fill in only if the instance requires authentication.",
29
29
  "currentShift": "Current shift",
30
30
  "customTitle": "Custom title",
31
31
  "displayedQueryParameter": "Displayed query parameter",
32
+ "displayName": "PromQL chart",
32
33
  "endpointUrl": "Endpoint URL",
34
+ "errorAuth": "Could not authenticate to Prometheus. Check the username and password in widget settings.",
35
+ "errorForbidden": "Authenticated, but the Prometheus user has no permission to read these metrics.",
36
+ "errorUnreachable": "Could not reach Prometheus at the configured URL.",
37
+ "insecureWarning": "Credentials will be sent over an unencrypted connection (HTTP). Use HTTPS to protect them.",
33
38
  "name": "Name",
39
+ "password": "Password",
34
40
  "period": "Period",
35
41
  "previousShift": "Previous shift",
36
42
  "query": "Query",
43
+ "removeParam": "Remove parameter",
44
+ "removeQuery": "Remove query",
45
+ "sectionAuthorization": "Authorization",
46
+ "sectionData": "Data",
37
47
  "settings": "Settings",
38
48
  "thisFieldIsRequired": "This field is required",
39
- "xAxisUnit": "X axis unit",
40
- "displayName": "PromQL chart",
41
- "titleQueries_one": "{{count}} query",
42
- "titleQueries_other": "{{count}} queries",
43
49
  "titleCurrentShift": "current shift",
44
50
  "titlePreviousShift": "previous shift",
51
+ "titleQueries_one": "{{count}} query",
52
+ "titleQueries_other": "{{count}} queries",
53
+ "username": "Username",
54
+ "xAxisUnit": "X axis unit",
45
55
  "yAxisUnit": "Y axis unit"
46
56
  } } },
47
57
  pl: { translation: { prometheusWidget: {
48
58
  "addAnotherDisplayedQueryParameter": "Dodaj kolejny wyświetlany parametr zapytania",
49
59
  "addAnotherQuery": "Dodaj kolejne zapytanie",
50
60
  "advanced": "Zaawansowane",
61
+ "credentialsHelper": "Dane autoryzacyjne są opcjonalne. Wypełnij tylko jeśli instancja wymaga uwierzytelniania.",
51
62
  "currentShift": "Obecna zmiana",
52
63
  "customTitle": "Własny tytuł",
53
64
  "displayedQueryParameter": "Wyświetlany parametr zapytania",
65
+ "displayName": "Wykres PromQL",
54
66
  "endpointUrl": "Adres URL",
67
+ "errorAuth": "Nie można uwierzytelnić się w Prometheusie. Sprawdź nazwę użytkownika i hasło w ustawieniach widgetu.",
68
+ "errorForbidden": "Uwierzytelnienie powiodło się, ale użytkownik Prometheusa nie ma uprawnień do tych metryk.",
69
+ "errorUnreachable": "Nie można połączyć się z Prometheusem pod skonfigurowanym adresem.",
70
+ "insecureWarning": "Dane logowania zostaną wysłane nieszyfrowanym połączeniem (HTTP). Użyj HTTPS, aby je chronić.",
55
71
  "name": "Nazwa",
72
+ "password": "Hasło",
56
73
  "period": "Okres",
57
74
  "previousShift": "Poprzednia zmiana",
58
75
  "query": "Zapytanie",
76
+ "removeParam": "Usuń parametr",
77
+ "removeQuery": "Usuń zapytanie",
78
+ "sectionAuthorization": "Autoryzacja",
79
+ "sectionData": "Dane",
59
80
  "settings": "Ustawienia",
60
81
  "thisFieldIsRequired": "To pole jest wymagane",
61
- "xAxisUnit": "Jednostka osi X",
62
- "displayName": "Wykres PromQL",
63
- "titleQueries_one": "{{count}} zapytanie",
64
- "titleQueries_few": "{{count}} zapytania",
65
- "titleQueries_many": "{{count}} zapytań",
66
82
  "titleCurrentShift": "obecna zmiana",
67
83
  "titlePreviousShift": "poprzednia zmiana",
84
+ "titleQueries_few": "{{count}} zapytania",
85
+ "titleQueries_many": "{{count}} zapytań",
86
+ "titleQueries_one": "{{count}} zapytanie",
87
+ "username": "Nazwa użytkownika",
88
+ "xAxisUnit": "Jednostka osi X",
68
89
  "yAxisUnit": "Jednostka osi Y"
69
90
  } } }
70
91
  };
71
92
  //#endregion
72
93
  //#region src/version.ts
73
- const LIBRARY_VERSION = "1.3.18";
94
+ const LIBRARY_VERSION = "1.3.19";
74
95
  //#endregion
75
96
  //#region src/types.ts
76
97
  let QueriesPeriod = /* @__PURE__ */ function(QueriesPeriod) {
@@ -79,14 +100,87 @@ let QueriesPeriod = /* @__PURE__ */ function(QueriesPeriod) {
79
100
  return QueriesPeriod;
80
101
  }({});
81
102
  //#endregion
103
+ //#region src/components/SettingsFormContent/utils.ts
104
+ const isInsecureEndpoint = (url) => /^http:\/\//i.test(url);
105
+ //#endregion
106
+ //#region src/components/SettingsFormContent/styles.ts
107
+ const styles = {
108
+ sectionDivider: {
109
+ marginTop: 3,
110
+ marginBottom: 1,
111
+ color: (theme) => theme.palette.text.primary,
112
+ fontWeight: 600,
113
+ letterSpacing: "0.08em",
114
+ textTransform: "uppercase",
115
+ fontSize: "0.75rem",
116
+ "&::before, &::after": { borderColor: "rgba(255, 255, 255, 0.15)" }
117
+ },
118
+ formControl: { marginTop: 2 },
119
+ addButton: { marginTop: 2 },
120
+ credentialsHelper: {
121
+ marginTop: 1,
122
+ color: (theme) => theme.palette.text.primary
123
+ }
124
+ };
125
+ //#endregion
126
+ //#region src/components/SettingsFormContent/CredentialsFields/index.tsx
127
+ const CredentialsFields = ({ formProps }) => {
128
+ const [passwordInput, setPasswordInput] = useState("");
129
+ const { t } = useTranslation();
130
+ const handlePasswordChange = (event) => {
131
+ setPasswordInput(event.target.value);
132
+ formProps.setFieldValue("password", event.target.value);
133
+ };
134
+ const showInsecureWarning = isInsecureEndpoint(formProps.values.endpointUrl);
135
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
136
+ /* @__PURE__ */ jsx(FormHelperText, {
137
+ sx: styles.credentialsHelper,
138
+ "data-testid": "prometheus.settings.credentials-helper",
139
+ children: t("prometheusWidget.credentialsHelper")
140
+ }),
141
+ /* @__PURE__ */ jsx(FormControl, {
142
+ sx: styles.formControl,
143
+ fullWidth: true,
144
+ children: /* @__PURE__ */ jsx(TextField, {
145
+ name: "username",
146
+ label: t("prometheusWidget.username"),
147
+ value: formProps.values.username || "",
148
+ onChange: formProps.handleChange,
149
+ onBlur: formProps.handleBlur,
150
+ "data-testid": "prometheus.settings.username",
151
+ fullWidth: true
152
+ })
153
+ }),
154
+ /* @__PURE__ */ jsx(FormControl, {
155
+ sx: styles.formControl,
156
+ fullWidth: true,
157
+ children: /* @__PURE__ */ jsx(TextField, {
158
+ name: "password",
159
+ type: "password",
160
+ label: t("prometheusWidget.password"),
161
+ value: passwordInput,
162
+ onChange: handlePasswordChange,
163
+ onBlur: formProps.handleBlur,
164
+ "data-testid": "prometheus.settings.password",
165
+ fullWidth: true
166
+ })
167
+ }),
168
+ showInsecureWarning && /* @__PURE__ */ jsx(Alert, {
169
+ severity: "warning",
170
+ sx: styles.formControl,
171
+ "data-testid": "prometheus.settings.insecure-warning",
172
+ children: t("prometheusWidget.insecureWarning")
173
+ })
174
+ ] });
175
+ };
176
+ //#endregion
82
177
  //#region src/components/SettingsFormContent/index.tsx
178
+ const emptyQueryItem = {
179
+ query: "",
180
+ displayName: ""
181
+ };
83
182
  const SettingsFormContent = ({ formProps }) => {
84
183
  const [selectedTab, setSelectedTab] = useState("settings");
85
- const emptyQueryItem = {
86
- query: "",
87
- displayName: ""
88
- };
89
- const formControlStyles = { mt: 2 };
90
184
  const { t } = useTranslation();
91
185
  const setPrometheusQuery = (i, item) => {
92
186
  const queries = [...formProps.values.prometheusQueries || [emptyQueryItem]];
@@ -121,7 +215,7 @@ const SettingsFormContent = ({ formProps }) => {
121
215
  };
122
216
  const mapPeriodToLabel = (period) => {
123
217
  switch (period) {
124
- case QueriesPeriod.PREVIOUS_SHIFT: return t("prometheusWidget.previousShift");
218
+ case "PREVIOUS_SHIFT": return t("prometheusWidget.previousShift");
125
219
  default: return t("prometheusWidget.currentShift");
126
220
  }
127
221
  };
@@ -145,7 +239,7 @@ const SettingsFormContent = ({ formProps }) => {
145
239
  mb: 2,
146
240
  children: [
147
241
  /* @__PURE__ */ jsx(FormControl, {
148
- sx: formControlStyles,
242
+ sx: styles.formControl,
149
243
  fullWidth: true,
150
244
  children: /* @__PURE__ */ jsx(TextField, {
151
245
  name: "endpointUrl",
@@ -159,8 +253,17 @@ const SettingsFormContent = ({ formProps }) => {
159
253
  fullWidth: true
160
254
  })
161
255
  }),
256
+ /* @__PURE__ */ jsx(Divider, {
257
+ sx: styles.sectionDivider,
258
+ children: t("prometheusWidget.sectionAuthorization")
259
+ }),
260
+ /* @__PURE__ */ jsx(CredentialsFields, { formProps }),
261
+ /* @__PURE__ */ jsx(Divider, {
262
+ sx: styles.sectionDivider,
263
+ children: t("prometheusWidget.sectionData")
264
+ }),
162
265
  /* @__PURE__ */ jsxs(FormControl, {
163
- sx: formControlStyles,
266
+ sx: styles.formControl,
164
267
  fullWidth: true,
165
268
  children: [
166
269
  /* @__PURE__ */ jsx(InputLabel, {
@@ -171,16 +274,16 @@ const SettingsFormContent = ({ formProps }) => {
171
274
  variant: "outlined",
172
275
  name: "queriesPeriod",
173
276
  labelId: "queries-period-label",
174
- value: formProps.values.queriesPeriod || QueriesPeriod.CURRENT_SHIFT,
277
+ value: formProps.values.queriesPeriod || "CURRENT_SHIFT",
175
278
  onChange: formProps.handleChange,
176
279
  onBlur: formProps.handleBlur,
177
280
  error: formProps.touched.queriesPeriod && Boolean(formProps.errors.queriesPeriod),
178
281
  "data-testid": "prometheus.settings.period-select",
179
282
  fullWidth: true,
180
- children: Object.keys(QueriesPeriod).map((key) => /* @__PURE__ */ jsx(MenuItem, {
181
- value: QueriesPeriod[key],
182
- children: mapPeriodToLabel(QueriesPeriod[key])
183
- }, key))
283
+ children: Object.values(QueriesPeriod).map((value) => /* @__PURE__ */ jsx(MenuItem, {
284
+ value,
285
+ children: mapPeriodToLabel(value)
286
+ }, value))
184
287
  }),
185
288
  /* @__PURE__ */ jsx(FormHelperText, {
186
289
  error: true,
@@ -189,7 +292,7 @@ const SettingsFormContent = ({ formProps }) => {
189
292
  ]
190
293
  }),
191
294
  (formProps.values.prometheusQueries || [emptyQueryItem]).map(({ query, displayName }, i) => /* @__PURE__ */ jsx(FormControl, {
192
- sx: formControlStyles,
295
+ sx: styles.formControl,
193
296
  fullWidth: true,
194
297
  children: /* @__PURE__ */ jsxs(Stack, {
195
298
  direction: "row",
@@ -221,6 +324,7 @@ const SettingsFormContent = ({ formProps }) => {
221
324
  }),
222
325
  /* @__PURE__ */ jsx(IconButton, {
223
326
  color: "error",
327
+ "aria-label": t("prometheusWidget.removeQuery"),
224
328
  onClick: () => removeQueryField(i),
225
329
  "data-testid": `prometheus.settings.remove-query-${i}`,
226
330
  children: /* @__PURE__ */ jsx(Clear, {})
@@ -231,13 +335,13 @@ const SettingsFormContent = ({ formProps }) => {
231
335
  /* @__PURE__ */ jsx(Button, {
232
336
  color: "secondary",
233
337
  onClick: addQueryField,
234
- sx: { mt: (theme) => theme.spacing(2) },
338
+ sx: styles.addButton,
235
339
  "data-testid": "prometheus.settings.add-query",
236
340
  fullWidth: true,
237
341
  children: t("prometheusWidget.addAnotherQuery")
238
342
  }),
239
343
  (formProps.values.displayedQueryParams || [""]).map((param, i) => /* @__PURE__ */ jsx(FormControl, {
240
- sx: formControlStyles,
344
+ sx: styles.formControl,
241
345
  fullWidth: true,
242
346
  children: /* @__PURE__ */ jsxs(Stack, {
243
347
  direction: "row",
@@ -246,12 +350,13 @@ const SettingsFormContent = ({ formProps }) => {
246
350
  label: t("prometheusWidget.displayedQueryParameter"),
247
351
  value: param,
248
352
  onChange: (e) => setDisplayedQueryParam(i, e.target.value),
249
- error: formProps.touched.displayedQueryParams?.[i] && Boolean(getIn(formProps.errors, `displayedQueryParams.[${i}]`)),
250
- helperText: formProps.touched.displayedQueryParams?.[i] && getIn(formProps.errors, `displayedQueryParams.[${i}]`),
353
+ error: Boolean(Array.isArray(formProps.touched.displayedQueryParams) && formProps.touched.displayedQueryParams[i] && getIn(formProps.errors, `displayedQueryParams.[${i}]`)),
354
+ helperText: Array.isArray(formProps.touched.displayedQueryParams) && formProps.touched.displayedQueryParams[i] ? getIn(formProps.errors, `displayedQueryParams.[${i}]`) : void 0,
251
355
  "data-testid": `prometheus.settings.param-input-${i}`,
252
356
  fullWidth: true
253
357
  }), /* @__PURE__ */ jsx(IconButton, {
254
358
  color: "error",
359
+ "aria-label": t("prometheusWidget.removeParam"),
255
360
  onClick: () => removeDisplayedQueryParam(i),
256
361
  "data-testid": `prometheus.settings.remove-param-${i}`,
257
362
  children: /* @__PURE__ */ jsx(Clear, {})
@@ -261,13 +366,13 @@ const SettingsFormContent = ({ formProps }) => {
261
366
  /* @__PURE__ */ jsx(Button, {
262
367
  color: "secondary",
263
368
  onClick: addDisplayedQueryParam,
264
- sx: { mt: (theme) => theme.spacing(2) },
369
+ sx: styles.addButton,
265
370
  "data-testid": "prometheus.settings.add-param",
266
371
  fullWidth: true,
267
372
  children: t("prometheusWidget.addAnotherDisplayedQueryParameter")
268
373
  }),
269
374
  /* @__PURE__ */ jsx(FormControl, {
270
- sx: formControlStyles,
375
+ sx: styles.formControl,
271
376
  fullWidth: true,
272
377
  children: /* @__PURE__ */ jsx(TextField, {
273
378
  name: "xAxisUnit",
@@ -282,7 +387,7 @@ const SettingsFormContent = ({ formProps }) => {
282
387
  })
283
388
  }),
284
389
  /* @__PURE__ */ jsx(FormControl, {
285
- sx: formControlStyles,
390
+ sx: styles.formControl,
286
391
  fullWidth: true,
287
392
  children: /* @__PURE__ */ jsx(TextField, {
288
393
  name: "yAxisUnit",
@@ -315,13 +420,21 @@ const SettingsFormContent = ({ formProps }) => {
315
420
  };
316
421
  //#endregion
317
422
  //#region src/helpers.ts
423
+ const classifyFetchError = (err) => {
424
+ const e = err;
425
+ const status = e?.httpStatus ?? e?.response?.status;
426
+ if (status === 401) return "auth";
427
+ if (status === 403) return "forbidden";
428
+ return "unreachable";
429
+ };
430
+ const pickKeys = (obj, keys) => Object.fromEntries(keys.filter((k) => k in obj).map((k) => [k, obj[k]]));
318
431
  const getQueriesTimeRange = (period, data) => {
319
432
  const currentDate = dayjs();
320
433
  const normalizedShifts = normalizeShifts(data?.shifts, currentDate);
321
434
  const currentShift = getCurrentShift(currentDate, normalizedShifts);
322
435
  const recentShift = getRecentShift(currentDate, normalizedShifts);
323
436
  switch (period) {
324
- case QueriesPeriod.CURRENT_SHIFT:
437
+ case "CURRENT_SHIFT":
325
438
  if (currentShift) return {
326
439
  startedAt: currentShift.startedAt.toDate(),
327
440
  finishedAt: currentShift.finishedAt.toDate()
@@ -331,7 +444,7 @@ const getQueriesTimeRange = (period, data) => {
331
444
  finishedAt: recentShift.finishedAt.toDate()
332
445
  };
333
446
  return null;
334
- case QueriesPeriod.PREVIOUS_SHIFT:
447
+ case "PREVIOUS_SHIFT":
335
448
  if (recentShift) return {
336
449
  startedAt: recentShift.startedAt.toDate(),
337
450
  finishedAt: recentShift.finishedAt.toDate()
@@ -341,6 +454,21 @@ const getQueriesTimeRange = (period, data) => {
341
454
  }
342
455
  };
343
456
  //#endregion
457
+ //#region src/components/PrometheusChart/constants.ts
458
+ const COLORS = [
459
+ "#36A2EB",
460
+ "#FF6384",
461
+ "#4BC0C0",
462
+ "#FF9F40",
463
+ "#9966FF",
464
+ "#FFCD56",
465
+ "#C9CBCF",
466
+ "#7BC8A4",
467
+ "#E7E9ED",
468
+ "#FF6B6B"
469
+ ];
470
+ const DEFAULT_FONT_COLOR = "rgba(255, 255, 255, 0.75)";
471
+ //#endregion
344
472
  //#region src/components/PrometheusChart/GradientPlugin.ts
345
473
  const createGradient = (ctx, area) => {
346
474
  return ctx.createLinearGradient(0, area.bottom, 0, area.top);
@@ -368,31 +496,49 @@ const GradientPlugin = {
368
496
  //#region src/components/PrometheusChart/index.tsx
369
497
  Chart.register(CategoryScale, LinearScale, TimeScale, PointElement, LineElement, Filler, Legend, Tooltip);
370
498
  Chart.defaults.font.size = 12;
371
- Chart.defaults.color = "rgba(255, 255, 255, 0.75)";
372
- const COLORS = [
373
- "#36A2EB",
374
- "#FF6384",
375
- "#4BC0C0",
376
- "#FF9F40",
377
- "#9966FF",
378
- "#FFCD56",
379
- "#C9CBCF",
380
- "#7BC8A4",
381
- "#E7E9ED",
382
- "#FF6B6B"
383
- ];
499
+ Chart.defaults.color = DEFAULT_FONT_COLOR;
500
+ const errorTranslationKey = {
501
+ auth: "prometheusWidget.errorAuth",
502
+ forbidden: "prometheusWidget.errorForbidden",
503
+ unreachable: "prometheusWidget.errorUnreachable"
504
+ };
505
+ const errorMessageStyles = {
506
+ width: "100%",
507
+ height: "100%",
508
+ display: "flex",
509
+ alignItems: "center",
510
+ justifyContent: "center",
511
+ textAlign: "center",
512
+ padding: 2
513
+ };
384
514
  const PrometheusChart = ({ data, settings }) => {
385
515
  const chart = useRef(null);
516
+ const hiddenSeriesRef = useRef({});
517
+ const fetchGenRef = useRef(0);
386
518
  const [chartData, setChartData] = useState({
387
519
  labels: [],
388
520
  datasets: []
389
521
  });
390
522
  const [queriesTimeRange, setQueriesTimeRange] = useState(null);
391
523
  const [timeLabels, setTimeLabels] = useState([]);
392
- const hiddenSeriesRef = useRef({});
524
+ const [error, setError] = useState(null);
525
+ const driver = useMemo(() => {
526
+ const options = { endpoint: settings.endpointUrl };
527
+ if (settings.username && settings.password) options.auth = {
528
+ username: settings.username,
529
+ password: settings.password
530
+ };
531
+ return new PrometheusDriver(options);
532
+ }, [
533
+ settings.endpointUrl,
534
+ settings.username,
535
+ settings.password
536
+ ]);
537
+ const { t } = useTranslation();
393
538
  const fetchData = useCallback(async (timeRange) => {
394
539
  if (!settings.endpointUrl || !settings.prometheusQueries?.length) return;
395
- const driver = new PrometheusDriver({ endpoint: settings.endpointUrl });
540
+ fetchGenRef.current += 1;
541
+ const gen = fetchGenRef.current;
396
542
  const start = new Date(timeRange.startedAt);
397
543
  const end = timeRange.finishedAt ? new Date(timeRange.finishedAt) : /* @__PURE__ */ new Date();
398
544
  const step = 30;
@@ -400,39 +546,51 @@ const PrometheusChart = ({ data, settings }) => {
400
546
  const ds = chart.current.data.datasets[i];
401
547
  if (ds.label) hiddenSeriesRef.current[ds.label] = !chart.current.isDatasetVisible(i);
402
548
  }
403
- const queries = settings.prometheusQueries.map(({ query }) => query);
404
- const results = await Promise.all(queries.map((query) => driver.rangeQuery(query, start, end, step)));
405
- let seriesIndex = 0;
406
- setChartData({
407
- labels: [],
408
- datasets: results.flatMap((result, queryIndex) => result.result.map((serie) => {
409
- const idx = seriesIndex++;
410
- const queryConfig = settings.prometheusQueries[queryIndex];
411
- const metricLabels = serie.metric.labels || {};
412
- const filteredValues = Object.values(pick(metricLabels, settings.displayedQueryParams)).join(" - ");
413
- let label;
414
- if (queryConfig?.displayName) label = filteredValues ? `${queryConfig.displayName} - ${filteredValues}` : queryConfig.displayName;
415
- else {
416
- const metricName = serie.metric.name || queryConfig?.query || "";
417
- label = filteredValues ? `${metricName} ${JSON.stringify(pick(metricLabels, settings.displayedQueryParams))}` : metricName;
418
- }
419
- return {
420
- label,
421
- data: serie.values.map((v) => ({
422
- x: v.time,
423
- y: v.value
424
- })),
425
- fill: true,
426
- tension: .4,
427
- borderColor: COLORS[idx % COLORS.length],
428
- backgroundColor: COLORS[idx % COLORS.length],
429
- borderWidth: 3,
430
- pointRadius: 0,
431
- hidden: hiddenSeriesRef.current[label] || false
432
- };
433
- }))
434
- });
435
- }, [settings.endpointUrl, settings.prometheusQueries]);
549
+ try {
550
+ const queries = settings.prometheusQueries.map(({ query }) => query);
551
+ const results = await Promise.all(queries.map((query) => driver.rangeQuery(query, start, end, step)));
552
+ if (gen !== fetchGenRef.current) return;
553
+ let seriesIndex = 0;
554
+ setChartData({
555
+ labels: [],
556
+ datasets: results.flatMap((result, queryIndex) => result.result.map((serie) => {
557
+ const idx = seriesIndex++;
558
+ const queryConfig = settings.prometheusQueries[queryIndex];
559
+ const metricLabels = serie.metric.labels || {};
560
+ const filteredValues = Object.values(pickKeys(metricLabels, settings.displayedQueryParams)).join(" - ");
561
+ let label;
562
+ if (queryConfig?.displayName) label = filteredValues ? `${queryConfig.displayName} - ${filteredValues}` : queryConfig.displayName;
563
+ else {
564
+ const metricName = serie.metric.name || queryConfig?.query || "";
565
+ label = filteredValues ? `${metricName} ${JSON.stringify(pickKeys(metricLabels, settings.displayedQueryParams))}` : metricName;
566
+ }
567
+ return {
568
+ label,
569
+ data: serie.values.map((v) => ({
570
+ x: v.time,
571
+ y: v.value
572
+ })),
573
+ fill: true,
574
+ tension: .4,
575
+ borderColor: COLORS[idx % COLORS.length],
576
+ backgroundColor: COLORS[idx % COLORS.length],
577
+ borderWidth: 3,
578
+ pointRadius: 0,
579
+ hidden: hiddenSeriesRef.current[label] || false
580
+ };
581
+ }))
582
+ });
583
+ setError(null);
584
+ } catch (err) {
585
+ if (gen !== fetchGenRef.current) return;
586
+ setError(classifyFetchError(err));
587
+ }
588
+ }, [
589
+ settings.endpointUrl,
590
+ settings.prometheusQueries,
591
+ settings.displayedQueryParams,
592
+ driver
593
+ ]);
436
594
  useEffect(() => {
437
595
  if (queriesTimeRange) {
438
596
  setTimeLabels(generateTimeRange(dayjs(queriesTimeRange.startedAt), dayjs(queriesTimeRange.finishedAt), "minutes", 15).map((d) => d.toDate()));
@@ -441,7 +599,7 @@ const PrometheusChart = ({ data, settings }) => {
441
599
  }, [queriesTimeRange, fetchData]);
442
600
  useEffect(() => {
443
601
  setQueriesTimeRange(getQueriesTimeRange(settings.queriesPeriod, data));
444
- if (settings?.queriesPeriod === QueriesPeriod.CURRENT_SHIFT) {
602
+ if (settings?.queriesPeriod === "CURRENT_SHIFT") {
445
603
  const intervalId = setInterval(() => {
446
604
  setQueriesTimeRange(getQueriesTimeRange(settings.queriesPeriod, data));
447
605
  }, 3e4);
@@ -451,6 +609,15 @@ const PrometheusChart = ({ data, settings }) => {
451
609
  }
452
610
  return () => {};
453
611
  }, [data, settings]);
612
+ if (error) return /* @__PURE__ */ jsx(Box, {
613
+ sx: errorMessageStyles,
614
+ "data-testid": `prometheus-widget.error.${error}`,
615
+ children: /* @__PURE__ */ jsx(Typography, {
616
+ variant: "body2",
617
+ color: "text.primary",
618
+ children: t(errorTranslationKey[error])
619
+ })
620
+ });
454
621
  return queriesTimeRange && /* @__PURE__ */ jsx(Line, {
455
622
  ref: chart,
456
623
  data: chartData,
@@ -506,13 +673,16 @@ const WidgetView = ({ data, settings }) => {
506
673
  };
507
674
  //#endregion
508
675
  //#region src/components/Widget/utils.ts
676
+ const isLegacyQuery = (query) => typeof query === "string";
509
677
  const getSettingsFormProps = (t) => {
510
678
  yup.setLocale({ mixed: { required: t("prometheusWidget.thisFieldIsRequired") } });
511
679
  return {
512
680
  initialValues: {
513
681
  customTitle: "",
514
682
  endpointUrl: "",
515
- queriesPeriod: QueriesPeriod.CURRENT_SHIFT,
683
+ username: "",
684
+ password: "",
685
+ queriesPeriod: "CURRENT_SHIFT",
516
686
  prometheusQueries: [],
517
687
  displayedQueryParams: [],
518
688
  xAxisUnit: "",
@@ -520,6 +690,8 @@ const getSettingsFormProps = (t) => {
520
690
  },
521
691
  validationSchema: yup.object({
522
692
  endpointUrl: yup.string().required(),
693
+ username: yup.string(),
694
+ password: yup.string(),
523
695
  queriesPeriod: yup.string().required(),
524
696
  prometheusQueries: yup.array().of(yup.object().shape({
525
697
  query: yup.string().required(),
@@ -528,22 +700,26 @@ const getSettingsFormProps = (t) => {
528
700
  displayedQueryParams: yup.array().of(yup.string().required()),
529
701
  xAxisUnit: yup.string().required(),
530
702
  yAxisUnit: yup.string().required()
531
- })
703
+ }),
704
+ onSubmit: () => {}
532
705
  };
533
706
  };
534
707
  const migrateSettings = (settings) => {
535
- if (!satisfies(settings.version || "1.0.0", ">=1.2.5")) return {
536
- ...settings,
537
- prometheusQueries: settings.prometheusQueries.map((query) => ({
538
- query,
539
- displayName: ""
540
- }))
541
- };
708
+ if (!satisfies(settings.version || "1.0.0", ">=1.2.5")) {
709
+ const queries = settings.prometheusQueries;
710
+ return {
711
+ ...settings,
712
+ prometheusQueries: queries.map((query) => isLegacyQuery(query) ? {
713
+ query,
714
+ displayName: ""
715
+ } : query)
716
+ };
717
+ }
542
718
  return settings;
543
719
  };
544
720
  //#endregion
545
721
  //#region src/components/Widget/index.tsx
546
- const Widget = ({ url, wsUrl, lang, data, ...widgetProps }) => {
722
+ const Widget = ({ url, wsUrl, lang, data, sidePanelOpened = false, updateSidePanelProps = () => {}, ...widgetProps }) => {
547
723
  const { graphqlSdk, gqlWsClient } = useGqlClients({
548
724
  url,
549
725
  wsUrl,
@@ -558,10 +734,11 @@ const Widget = ({ url, wsUrl, lang, data, ...widgetProps }) => {
558
734
  }, [data, graphqlSdk]);
559
735
  return /* @__PURE__ */ jsx(BaseWidget, {
560
736
  ...widgetProps,
561
- url,
562
737
  lang,
563
738
  locales: resources,
564
739
  data: widgetData,
740
+ sidePanelOpened,
741
+ updateSidePanelProps,
565
742
  gqlClients: {
566
743
  graphqlSdk,
567
744
  gqlWsClient