Skip to content

Commit

Permalink
Add support queries prettifying in query editor (#101)
Browse files Browse the repository at this point in the history
* feat: add button for query prettify (#86)

* docs: correct feature description

* style: return eslint rules
  • Loading branch information
Loori-R authored Sep 18, 2023
1 parent 23671d7 commit 18ac527
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 38 deletions.
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
* FEATURE: add datasource settings for limiting the number of metrics during discovery. The proper limits should protect users from slowing down the browser when datasource returns big amounts of discovered metrics in response. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/82).
* FEATURE: add a `prettify query` icon, which when clicked, formats the query. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/86).
* FEATURE: change the style of the buttons `WITH templates` and `Run in vmui` to icons.

* BUGFIX: correctly handle custom query parameters in annotation queries. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/95)
* BUGFIX: fix the duplication of labels in the legend when using expressions. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/93)
Expand Down
53 changes: 53 additions & 0 deletions src/components/PrettifyQuery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { FC, useState, memo } from 'react';

import { IconButton } from "@grafana/ui";

import { PrometheusDatasource } from '../datasource';
import { PromQuery } from "../types";

interface Props {
datasource: PrometheusDatasource;
query: PromQuery;
onChange: (update: PromQuery) => void;
}

enum ResponseStatus {
Success = 'success',
Error = 'error'
}

const PrettifyQuery: FC<Props> = ({
datasource,
query,
onChange
}) => {
const [loading, setLoading] = useState(false)

const handleClickPrettify = async () => {
setLoading(true)
try {
const response = await datasource.prettifyRequest(query.expr)
const { data, status } = response
if (data?.status === ResponseStatus.Success) {
onChange({ ...query, expr: data.query });
} else {
console.error(`Error requesting /prettify-query, status: ${status}`)
}
} catch (e) {
console.error(e)
}
setLoading(false)
}

return (
<IconButton
key="run"
name={loading ? 'fa fa-spinner' : 'brackets-curly'}
tooltip={'Prettify query'}
disabled={loading}
onClick={handleClickPrettify}
/>
);
};

export default memo(PrettifyQuery);
27 changes: 13 additions & 14 deletions src/components/VmuiLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React, { FC, useEffect, useState, memo } from 'react';

import { PanelData, ScopedVars, textUtil, rangeUtil } from '@grafana/data';
import { getBackendSrv } from "@grafana/runtime";
import { Button } from "@grafana/ui";
import { IconButton } from "@grafana/ui";

import { PrometheusDatasource } from '../datasource';
import { PromQuery } from '../types';
Expand Down Expand Up @@ -55,11 +55,11 @@ export const relativeTimeOptionsVMUI = [
}))

const VmuiLink: FC<Props> = ({
panelData,
query,
datasource,
dashboardUID,
}) => {
panelData,
query,
datasource,
dashboardUID,
}) => {
const [href, setHref] = useState('');

useEffect(() => {
Expand Down Expand Up @@ -132,14 +132,13 @@ const VmuiLink: FC<Props> = ({
}, [dashboardUID, datasource, panelData, query]);

return (
<a href={textUtil.sanitizeUrl(href)} target="_blank" rel="noopener noreferrer">
<Button
variant={'primary'}
size="sm"
icon={"external-link-alt"}
>
Run in vmui
</Button>
<a href={textUtil.sanitizeUrl(href)} target="_blank" rel="noopener noreferrer"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
<IconButton
key="vmui"
name="external-link-alt"
tooltip="Run in vmui"
/>
</a>
);
};
Expand Down
14 changes: 6 additions & 8 deletions src/components/WithTemplateConfig/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC, useCallback, useEffect, useState } from 'react'

import { Badge, Button, Modal, useStyles2 } from "@grafana/ui";
import { Badge, Button, IconButton, Modal, useStyles2 } from "@grafana/ui";

import { PrometheusDatasource } from "../../datasource";

Expand Down Expand Up @@ -82,14 +82,12 @@ const WithTemplateConfig: FC<Props> = ({ template, setTemplate, dashboardUID, da

return (
<>
<Button
variant={'secondary'}
size="sm"
<IconButton
key="with_templates"
name="cog"
tooltip="WITH templates"
onClick={handleOpen}
icon={"cog"}
>
WITH templates
</Button>
/>
<Modal
title={`${dashboardFolder} / ${dashboardTitle} / WITH templates`}
isOpen={showTemplates}
Expand Down
18 changes: 18 additions & 0 deletions src/datasource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,24 @@ export class PrometheusDatasource
); // toPromise until we change getTagValues, getTagKeys to Observable
}

async prettifyRequest<T = any>(query: string) {
// If URL includes endpoint that supports POST and GET method, try to use configured method. This might fail as POST is supported only in v2.10+.
try {
return await lastValueFrom(
this._request<T>(`/api/datasources/proxy/${this.id}/prettify-query`, {}, {
method: 'GET',
hideFromInspector: true,
showErrorAlert: false,
params: {
query
}
})
);
} catch (err) {
throw err;
}
}

interpolateQueryExpr(value: string | string[] = [], variable: any) {
// if no multi or include all do not regexEscape
if (!variable.multi && !variable.includeAll) {
Expand Down
24 changes: 8 additions & 16 deletions src/querybuilder/components/PromQueryEditorSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react';

import { CoreApp, LoadingState } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { Button, ConfirmModal, Tooltip } from '@grafana/ui';
import { ConfirmModal, IconButton } from '@grafana/ui';

import PrettifyQuery from "../../components/PrettifyQuery";
import { EditorHeader, EditorRows, FlexItem, InlineSelect, Space } from '../../components/QueryEditor';
import VmuiLink from "../../components/VmuiLink";
import WithTemplateConfig from "../../components/WithTemplateConfig";
Expand Down Expand Up @@ -52,7 +53,6 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
const { onChange, onRunQuery, data, app, datasource } = props;

const [parseModalOpen, setParseModalOpen] = useState(false);
const [dataIsStale, setDataIsStale] = useState(false);
const [trace, setTrace] = useState(false);
const [rawQuery, setRawQuery] = useState(false)
const { flag: explain, setFlag: setExplain } = useFlag(promQueryEditorExplainKey);
Expand Down Expand Up @@ -86,12 +86,7 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
[onChange, query, app]
);

useEffect(() => {
setDataIsStale(false);
}, [data]);

const onChangeInternal = (query: PromQuery) => {
setDataIsStale(true);
onChange(query);
};

Expand Down Expand Up @@ -156,17 +151,14 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
dashboardUID={dashboardUID}
datasource={datasource}
/>
<PrettifyQuery query={query} datasource={datasource} onChange={onChange}/>
<VmuiLink query={query} datasource={datasource} panelData={data} dashboardUID={dashboardUID}/>
{app !== CoreApp.Explore && (
<Tooltip content={"Run queries"}>
<Button
variant={dataIsStale ? 'primary' : 'secondary'}
size="sm"
onClick={onRunQuery}
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : "play"}
disabled={data?.state === LoadingState.Loading}
/>
</Tooltip>
<IconButton
key="run"
name={data?.state === LoadingState.Loading ? 'fa fa-spinner' : "play"}
tooltip="Run queries"
/>
)}
<QueryEditorModeToggle mode={editorMode} onChange={onEditorModeChange}/>
</EditorHeader>
Expand Down

0 comments on commit 18ac527

Please sign in to comment.