Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix an issue with prettify query #169

Merged
merged 4 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
104 changes: 104 additions & 0 deletions src/components/PrettifyQuery.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<PrettifyQuery
datasource={datasource}
query={{ expr: got, refId: 'A' }}
onChange={mockCallback}
/>);
});

const btn = await screen.findByRole('button');

await act(async () => {
btn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
});
});
});
40 changes: 37 additions & 3 deletions src/components/PrettifyQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Props> = ({
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;
dmitryk-dk marked this conversation as resolved.
Show resolved Hide resolved
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}`)
}
Expand Down
Loading