diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d070f3..e6ab6b3 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## tip
+* BUGFIX: fix an issue with prettify query if the query includes Grafana variables in the lookbehind window. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/166).
+
## [v0.8.2](https://github.com/VictoriaMetrics/grafana-datasource/releases/tag/v0.8.2)
* BUGFIX: fix parsing of label names with special characters for the query builder. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/131#issuecomment-2105662179).
diff --git a/src/components/PrettifyQuery.test.tsx b/src/components/PrettifyQuery.test.tsx
new file mode 100644
index 0000000..1432c3c
--- /dev/null
+++ b/src/components/PrettifyQuery.test.tsx
@@ -0,0 +1,104 @@
+import { render, screen, act } from '@testing-library/react';
+import React from 'react';
+
+import { PrometheusDatasource } from "../datasource";
+
+import PrettifyQuery from './PrettifyQuery';
+
+
+const testQueries = [
+ {
+ name: 'empty query',
+ got:'',
+ want:''
+ },
+ {
+ name: 'query with defined lookbehind window',
+ got:'sum(rate(node_cpu_seconds_total{mode="idle"}[5m]))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}[5m]))'
+ },
+ {
+ name: 'query with grafana $__interval variable',
+ got:'sum(rate(node_cpu_seconds_total{mode="idle"}[$__interval]))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}[$__interval]))'
+ },
+ {
+ name: 'query with grafana variable and lookbehind window',
+ got:'sum(rate(node_cpu_seconds_total{mode="idle"}))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}))'
+ },
+ {
+ name: 'query with grafana $__interval_ms variable',
+ got: 'sum(rate(node_cpu_seconds_total{mode="idle"}[$__interval_ms]))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}[$__interval_ms]))'
+ },
+ {
+ name: 'query with grafana $__range variable',
+ got: 'sum(rate(node_cpu_seconds_total{mode="idle"}[$__range]))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}[$__range]))'
+ },
+ {
+ name: 'query with grafana $__range variable',
+ got: 'sum(rate(node_cpu_seconds_total{mode="idle"}[$__range_s]))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}[$__range_s]))'
+ },
+ {
+ name: 'query with grafana $__rate_interval variable',
+ got: 'sum(rate(node_cpu_seconds_total{mode="idle"}[$__rate_interval]))',
+ want:'sum(rate(node_cpu_seconds_total{mode="idle"}[$__rate_interval]))'
+ },
+ {
+ name: 'query with two grafana variables',
+ got: 'rate(metric_name[$__interval]) + rate(metric_name[$__range]) ',
+ want:'rate(metric_name[$__interval]) + rate(metric_name[$__range]) '
+ },
+ {
+ name: 'query with grafana variable and label value as lookbehind window',
+ got: 'rate(metric_name{mode="idle"}[$__interval]) + up{instance="[1i]"} ',
+ want:'rate(metric_name{mode="idle"}[$__interval]) + up{instance="[1i]"} '
+ }
+]
+
+const datasource = {
+ languageProvider: {
+ start: () => Promise.resolve([]),
+ syntax: () => {},
+ getLabelKeys: () => [],
+ metrics: [],
+ },
+ getInitHints: () => [],
+ prettifyRequest: async (expr: string) => {
+ return {
+ data: {
+ query: expr,
+ status: 'success'
+ }
+ }
+ }
+} as unknown as PrometheusDatasource;
+
+describe("Prettyfied Query", () => {
+ testQueries.forEach(async ({ name, got, want }) => {
+ it(`should prettify the query ${name}`, async () => {
+
+ const mockCallback = jest.fn(resp => {
+ const { expr } = resp;
+ expect(expr).toBe(want)
+ });
+
+ act(() => {
+ render();
+ });
+
+ const btn = await screen.findByRole('button');
+
+ await act(async () => {
+ btn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+ });
+ });
+});
diff --git a/src/components/PrettifyQuery.tsx b/src/components/PrettifyQuery.tsx
index 4b7f532..907dd5a 100755
--- a/src/components/PrettifyQuery.tsx
+++ b/src/components/PrettifyQuery.tsx
@@ -16,20 +16,54 @@ enum ResponseStatus {
Error = 'error'
}
+const GRAFANA_VARIABLES = [
+ "$__interval",
+ "$__interval_ms",
+ "$__range",
+ "$__range_s",
+ "$__range_ms",
+ "$__rate_interval",
+];
+
+interface GrafanaVariableReplacer {
+ variable: string;
+ defaultWindow: string;
+}
+
const PrettifyQuery: FC = ({
datasource,
query,
onChange
}) => {
- const [loading, setLoading] = useState(false)
+ const [loading, setLoading] = useState(false);
+
const handleClickPrettify = async () => {
setLoading(true)
try {
- const response = await datasource.prettifyRequest(query.expr)
+ let { expr } = query;
+ let grafanaVariables = [] as GrafanaVariableReplacer[];
+ GRAFANA_VARIABLES.forEach((variable, idx) => {
+ const regex = new RegExp(`\\[(\\${variable})\\]\\)`, 'g');
+ if (regex.test(expr)) {
+ expr = expr.replace(regex, `[${idx+1}i])`);
+ grafanaVariables.push({
+ variable,
+ defaultWindow: `${idx+1}i`,
+ })
+ }
+ });
+ const response = await datasource.prettifyRequest(expr);
const { data, status } = response
if (data?.status === ResponseStatus.Success) {
- onChange({ ...query, expr: data.query });
+ let { query } = data;
+ if (grafanaVariables.length > 0) {
+ grafanaVariables.forEach(grafanaVariable => {
+ const regex = new RegExp(`\\[(${grafanaVariable.defaultWindow})\\]\\)`, 'g');
+ query = query.replace(regex, `[${grafanaVariable.variable}])`);
+ });
+ }
+ onChange({ ...query, expr: query });
} else {
console.error(`Error requesting /prettify-query, status: ${status}`)
}