mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
DashboardQuery: Expand query options (#53998)
This commit is contained in:
parent
4dc0d49025
commit
17b2fb04e8
@ -6062,9 +6062,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/dashboard/runSharedRequest.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
|
536
devenv/dev-dashboards/transforms/reuse.json
Normal file
536
devenv/dev-dashboards/transforms/reuse.json
Normal file
@ -0,0 +1,536 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 1394,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"content": "Dashboard queries allow re-using the same results from one panel in another panel context.\n\nThis dashboard shows a single panel that makes a real query and applies transformations. The other panels, all use the same results rather than make their own query requests.",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto",
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 18,
|
||||
"w": 7,
|
||||
"x": 0,
|
||||
"y": 3
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"footer": {
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "flight_info_by_state.csv",
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
},
|
||||
{
|
||||
"csvFileName": "population_by_state.csv",
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Raw data -- with outer join",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "seriesToColumns",
|
||||
"options": {
|
||||
"byField": "State",
|
||||
"mode": "outer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {
|
||||
"1980 population_by_state.csv": true,
|
||||
"2000 population_by_state.csv": true,
|
||||
"DestLocation flight_info_by_state.csv": true,
|
||||
"Lat flight_info_by_state.csv": true,
|
||||
"Lng flight_info_by_state.csv": true,
|
||||
"Price flight_info_by_state.csv": true
|
||||
},
|
||||
"indexByName": {},
|
||||
"renameByName": {
|
||||
"2020 population_by_state.csv": "2020 population",
|
||||
"Count flight_info_by_state.csv": "Flight count",
|
||||
"Price flight_info_by_state.csv": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto",
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 11,
|
||||
"x": 7,
|
||||
"y": 3
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"footer": {
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Reused data (without transform)",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 3
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"name": "Layer 0",
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showMeasure": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"showLegend": true,
|
||||
"style": {
|
||||
"color": {
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"opacity": 0.4,
|
||||
"rotation": {
|
||||
"fixed": 0,
|
||||
"max": 360,
|
||||
"min": -360,
|
||||
"mode": "mod"
|
||||
},
|
||||
"size": {
|
||||
"field": "Flight count",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
},
|
||||
"symbol": {
|
||||
"fixed": "img/icons/marker/circle.svg",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"textConfig": {
|
||||
"fontSize": 12,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"textAlign": "center",
|
||||
"textBaseline": "middle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "lookup"
|
||||
},
|
||||
"name": "Flight count",
|
||||
"tooltip": true,
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "details"
|
||||
},
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 35.70008,
|
||||
"lon": -93.558296,
|
||||
"zoom": 3.09
|
||||
}
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A",
|
||||
"withTransforms": true
|
||||
}
|
||||
],
|
||||
"title": "Reused data (without transform)",
|
||||
"type": "geomap"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto",
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 11,
|
||||
"x": 7,
|
||||
"y": 12
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"footer": {
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A",
|
||||
"withTransforms": true
|
||||
}
|
||||
],
|
||||
"title": "Reused data (with transform)",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 12
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"name": "Layer 0",
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showMeasure": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"showLegend": true,
|
||||
"style": {
|
||||
"color": {
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"opacity": 0.4,
|
||||
"rotation": {
|
||||
"fixed": 0,
|
||||
"max": 360,
|
||||
"min": -360,
|
||||
"mode": "mod"
|
||||
},
|
||||
"size": {
|
||||
"field": "2020 population",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
},
|
||||
"symbol": {
|
||||
"fixed": "img/icons/marker/circle.svg",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"textConfig": {
|
||||
"fontSize": 12,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"textAlign": "center",
|
||||
"textBaseline": "middle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "lookup"
|
||||
},
|
||||
"name": "2022 Population",
|
||||
"tooltip": true,
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "details"
|
||||
},
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 35.70008,
|
||||
"lon": -93.558296,
|
||||
"zoom": 3.09
|
||||
}
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A",
|
||||
"withTransforms": true
|
||||
}
|
||||
],
|
||||
"title": "Reused data (with transform)",
|
||||
"type": "geomap"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 37,
|
||||
"style": "dark",
|
||||
"tags": ["devenv"],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Reuse dashboard queries",
|
||||
"uid": "fYGWTVW4k"
|
||||
}
|
@ -31,6 +31,6 @@ function setup() {
|
||||
describe('SupportSnapshot', () => {
|
||||
it('Can render', async () => {
|
||||
setup();
|
||||
expect(await screen.findByRole('button', { name: 'Dashboard (2.94 KiB)' })).toBeInTheDocument();
|
||||
expect(await screen.findByRole('button', { name: 'Dashboard (2.97 KiB)' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -118,15 +118,19 @@ export async function getDebugDashboard(panel: PanelModel, rand: Randomize, time
|
||||
],
|
||||
};
|
||||
|
||||
if (data.annotations?.length) {
|
||||
const anno: DataFrameJSON[] = [];
|
||||
for (const f of frames) {
|
||||
if (f.schema?.meta?.dataTopic) {
|
||||
delete f.schema.meta.dataTopic;
|
||||
anno.push(f);
|
||||
}
|
||||
}
|
||||
if (saveModel.transformations?.length) {
|
||||
const last = dashboard.panels[dashboard.panels.length - 1];
|
||||
last.title = last.title + ' (after transformations)';
|
||||
|
||||
const before = cloneDeep(last);
|
||||
before.id = 100;
|
||||
before.title = 'Data (before transformations)';
|
||||
before.gridPos.w = 24; // full width
|
||||
before.targets[0].withTransforms = false;
|
||||
dashboard.panels.push(before);
|
||||
}
|
||||
|
||||
if (data.annotations?.length) {
|
||||
dashboard.panels.push({
|
||||
id: 7,
|
||||
gridPos: {
|
||||
@ -138,17 +142,22 @@ export async function getDebugDashboard(panel: PanelModel, rand: Randomize, time
|
||||
type: 'table',
|
||||
title: 'Annotations',
|
||||
datasource: {
|
||||
type: 'grafana',
|
||||
uid: 'grafana',
|
||||
type: 'datasource',
|
||||
uid: '-- Dashboard --',
|
||||
},
|
||||
options: {
|
||||
showTypeIcons: true,
|
||||
},
|
||||
targets: [
|
||||
{
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: '-- Dashboard --',
|
||||
},
|
||||
panelId: 2,
|
||||
withTransforms: true,
|
||||
topic: DataTopic.Annotations,
|
||||
refId: 'A',
|
||||
rawFrameContent: JSON.stringify(anno),
|
||||
scenarioId: 'raw_frame',
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -287,6 +296,7 @@ const embeddedDataTemplate: any = {
|
||||
uid: '-- Dashboard --',
|
||||
},
|
||||
panelId: 2,
|
||||
withTransforms: true,
|
||||
refId: 'A',
|
||||
},
|
||||
],
|
||||
|
@ -216,7 +216,7 @@ export class PanelQueryRunner {
|
||||
} = options;
|
||||
|
||||
if (isSharedDashboardQuery(datasource)) {
|
||||
this.pipeToSubject(runSharedRequest(options), panelId);
|
||||
this.pipeToSubject(runSharedRequest(options, queries[0]), panelId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4,15 +4,14 @@ import pluralize from 'pluralize';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { DataQuery, GrafanaTheme2, PanelData, SelectableValue } from '@grafana/data';
|
||||
import { InlineField, Select, useStyles2, VerticalGroup } from '@grafana/ui';
|
||||
import { DataQuery, GrafanaTheme2, PanelData, SelectableValue, DataTopic } from '@grafana/data';
|
||||
import { Field, Select, useStyles2, VerticalGroup, Spinner, Switch, RadioButtonGroup, Icon } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { filterPanelDataToQuery } from 'app/features/query/components/QueryEditorRow';
|
||||
|
||||
import { DashboardQueryRow } from './DashboardQueryRow';
|
||||
import { DashboardQuery, ResultInfo, SHARED_DASHBOARD_QUERY } from './types';
|
||||
|
||||
function getQueryDisplayText(query: DataQuery): string {
|
||||
@ -26,25 +25,30 @@ interface Props {
|
||||
onRunQueries: () => void;
|
||||
}
|
||||
|
||||
const topics = [
|
||||
{ label: 'All data', value: false },
|
||||
{ label: 'Annotations', value: true, description: 'Include annotations as regular data' },
|
||||
];
|
||||
|
||||
export function DashboardQueryEditor({ panelData, queries, onChange, onRunQueries }: Props) {
|
||||
const { value: defaultDatasource } = useAsync(() => getDatasourceSrv().get());
|
||||
const { value: results, loading: loadingResults } = useAsync(async (): Promise<ResultInfo[]> => {
|
||||
const query = queries[0] as DashboardQuery;
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
const panel = dashboard?.getPanelById(query.panelId ?? -124134);
|
||||
const query = queries[0] as DashboardQuery;
|
||||
|
||||
const panel = useMemo(() => {
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
return dashboard?.getPanelById(query.panelId ?? -124134);
|
||||
}, [query.panelId]);
|
||||
|
||||
const { value: results, loading: loadingResults } = useAsync(async (): Promise<ResultInfo[]> => {
|
||||
if (!panel) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const mainDS = await getDatasourceSrv().get(panel.datasource);
|
||||
return Promise.all(
|
||||
panel.targets.map(async (query) => {
|
||||
const ds = query.datasource ? await getDatasourceSrv().get(query.datasource) : mainDS;
|
||||
const fmt = ds.getQueryDisplayText || getQueryDisplayText;
|
||||
|
||||
const queryData = filterPanelDataToQuery(panelData, query.refId) ?? panelData;
|
||||
|
||||
return {
|
||||
refId: query.refId,
|
||||
query: fmt(query),
|
||||
@ -54,21 +58,41 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie
|
||||
};
|
||||
})
|
||||
);
|
||||
}, [panelData, queries]);
|
||||
}, [panelData, panel]);
|
||||
|
||||
const query = queries[0] as DashboardQuery;
|
||||
const onUpdateQuery = useCallback(
|
||||
(query: DashboardQuery) => {
|
||||
onChange([query]);
|
||||
onRunQueries();
|
||||
},
|
||||
[onChange, onRunQueries]
|
||||
);
|
||||
|
||||
const onPanelChanged = useCallback(
|
||||
(id: number) => {
|
||||
onChange([
|
||||
{
|
||||
...query,
|
||||
panelId: id,
|
||||
} as DashboardQuery,
|
||||
]);
|
||||
onRunQueries();
|
||||
onUpdateQuery({
|
||||
...query,
|
||||
panelId: id,
|
||||
});
|
||||
},
|
||||
[query, onChange, onRunQueries]
|
||||
[query, onUpdateQuery]
|
||||
);
|
||||
|
||||
const onTransformToggle = useCallback(() => {
|
||||
onUpdateQuery({
|
||||
...query,
|
||||
withTransforms: !query.withTransforms,
|
||||
});
|
||||
}, [query, onUpdateQuery]);
|
||||
|
||||
const onTopicChanged = useCallback(
|
||||
(t: boolean) => {
|
||||
onUpdateQuery({
|
||||
...query,
|
||||
topic: t ? DataTopic.Annotations : undefined,
|
||||
});
|
||||
},
|
||||
[query, onUpdateQuery]
|
||||
);
|
||||
|
||||
const getPanelDescription = useCallback(
|
||||
@ -119,10 +143,11 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie
|
||||
const selected = panels.find((panel) => panel.value === query.panelId);
|
||||
// Same as current URL, but different panelId
|
||||
const editURL = `d/${dashboard.uid}/${dashboard.title}?&editPanel=${query.panelId}`;
|
||||
const showTransforms = Boolean(query.withTransforms || panel?.transformations?.length);
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineField label="Use results from panel" grow>
|
||||
<Field label="Source" description="Use the same results as panel">
|
||||
<Select
|
||||
inputId={selectId}
|
||||
placeholder="Choose panel"
|
||||
@ -131,19 +156,45 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie
|
||||
value={selected}
|
||||
onChange={(item) => onPanelChanged(item.value!)}
|
||||
/>
|
||||
</InlineField>
|
||||
</Field>
|
||||
|
||||
{results && !loadingResults && (
|
||||
<div className={styles.results}>
|
||||
{query.panelId && (
|
||||
<VerticalGroup spacing="sm">
|
||||
{results.map((target, i) => (
|
||||
<DashboardQueryRow editURL={editURL} target={target} key={`DashboardQueryRow-${i}`} />
|
||||
))}
|
||||
</VerticalGroup>
|
||||
{loadingResults ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
{results && Boolean(results.length) && (
|
||||
<Field label="Queries">
|
||||
<VerticalGroup spacing="sm">
|
||||
{results.map((target, i) => (
|
||||
<div className={styles.queryEditorRowHeader} key={`DashboardQueryRow-${i}`}>
|
||||
<div>
|
||||
<img src={target.img} width={16} />
|
||||
<span className={styles.refId}>{target.refId}:</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href={editURL}>
|
||||
{target.query}
|
||||
|
||||
<Icon name="external-link-alt" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</VerticalGroup>
|
||||
</Field>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showTransforms && (
|
||||
<Field label="Transform" description="Apply panel transformations from the source panel">
|
||||
<Switch value={Boolean(query.withTransforms)} onChange={onTransformToggle} />
|
||||
</Field>
|
||||
)}
|
||||
|
||||
<Field label="Data">
|
||||
<RadioButtonGroup options={topics} value={query.topic === DataTopic.Annotations} onChange={onTopicChanged} />
|
||||
</Field>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -156,5 +207,16 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
noQueriesText: css({
|
||||
padding: theme.spacing(1.25),
|
||||
}),
|
||||
refId: css({
|
||||
padding: theme.spacing(1.25),
|
||||
}),
|
||||
queryEditorRowHeader: css`
|
||||
label: queryEditorRowHeader;
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
flex-flow: row wrap;
|
||||
background: ${theme.colors.background.secondary};
|
||||
align-items: center;
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { ReactElement } from 'react';
|
||||
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { Icon, useStyles } from '@grafana/ui';
|
||||
|
||||
import { ResultInfo } from './types';
|
||||
|
||||
interface Props {
|
||||
editURL: string;
|
||||
target: ResultInfo;
|
||||
}
|
||||
|
||||
export function DashboardQueryRow({ editURL, target }: Props): ReactElement {
|
||||
const style = useStyles(getStyles);
|
||||
|
||||
return (
|
||||
<div className={style.queryEditorRowHeader}>
|
||||
<div>
|
||||
<img src={target.img} width={16} className={style.logo} />
|
||||
<span>{`${target.refId}:`}</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href={editURL}>
|
||||
{target.query}
|
||||
|
||||
<Icon name="external-link-alt" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme) {
|
||||
return {
|
||||
logo: css`
|
||||
label: logo;
|
||||
margin-right: ${theme.spacing.sm};
|
||||
`,
|
||||
queryEditorRowHeader: css`
|
||||
label: queryEditorRowHeader;
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
flex-flow: row wrap;
|
||||
background: ${theme.colors.bg2};
|
||||
align-items: center;
|
||||
`,
|
||||
};
|
||||
}
|
@ -1,8 +1,32 @@
|
||||
import { DataSourceApi } from '@grafana/data';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { isSharedDashboardQuery } from './runSharedRequest';
|
||||
import { DataSourceApi, DataTopic, LoadingState, PanelData } from '@grafana/data';
|
||||
import { getDashboardSrv, setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
|
||||
import { isSharedDashboardQuery, runSharedRequest } from './runSharedRequest';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
describe('SharedQueryRunner', () => {
|
||||
let panelData: PanelData = {} as any;
|
||||
const origDashbaordSrv = getDashboardSrv();
|
||||
|
||||
beforeEach(() => {
|
||||
setDashboardSrv({
|
||||
getCurrent: () => ({
|
||||
getPanelById: () => ({
|
||||
getQueryRunner: () => ({
|
||||
getData: () => of(panelData),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
} as any);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setDashboardSrv(origDashbaordSrv);
|
||||
});
|
||||
|
||||
it('should identify shared queries', () => {
|
||||
expect(isSharedDashboardQuery('-- Dashboard --')).toBe(true);
|
||||
|
||||
@ -20,4 +44,56 @@ describe('SharedQueryRunner', () => {
|
||||
ds.meta!.name = 'something else';
|
||||
expect(isSharedDashboardQuery(ds)).toBe(false);
|
||||
});
|
||||
|
||||
it('can filter annotation data', (done) => {
|
||||
// Get the data
|
||||
panelData = {
|
||||
state: LoadingState.Done,
|
||||
series: [{ refId: 'A', fields: [], length: 0 }],
|
||||
annotations: [{ refId: 'X', fields: [], length: 0 }],
|
||||
timeRange: panelData.timeRange,
|
||||
};
|
||||
|
||||
runSharedRequest({ queries: [{ panelId: 1 }] } as any, {
|
||||
refId: 'Q',
|
||||
}).subscribe((v) => {
|
||||
expect(v).toBe(panelData);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can move annotations to the series topic', (done) => {
|
||||
// Get the data
|
||||
panelData = {
|
||||
state: LoadingState.Done,
|
||||
series: [{ refId: 'A', fields: [], length: 0 }],
|
||||
annotations: [{ refId: 'X', fields: [], length: 0 }],
|
||||
timeRange: panelData.timeRange,
|
||||
};
|
||||
|
||||
runSharedRequest({ queries: [{ panelId: 1 }] } as any, {
|
||||
refId: 'Q',
|
||||
topic: DataTopic.Annotations,
|
||||
}).subscribe((v) => {
|
||||
try {
|
||||
expect(v).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"annotations": undefined,
|
||||
"series": Array [
|
||||
Object {
|
||||
"fields": Array [],
|
||||
"length": 0,
|
||||
"refId": "X",
|
||||
},
|
||||
],
|
||||
"state": "Done",
|
||||
"timeRange": undefined,
|
||||
}
|
||||
`);
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
getDefaultTimeRange,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
DataTopic,
|
||||
} from '@grafana/data';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { QueryRunnerOptions } from 'app/features/query/state/PanelQueryRunner';
|
||||
@ -31,7 +32,7 @@ export function isSharedDashboardQuery(datasource: string | DataSourceRef | Data
|
||||
return datasource.uid === SHARED_DASHBOARD_QUERY;
|
||||
}
|
||||
|
||||
export function runSharedRequest(options: QueryRunnerOptions): Observable<PanelData> {
|
||||
export function runSharedRequest(options: QueryRunnerOptions, query: DashboardQuery): Observable<PanelData> {
|
||||
return new Observable<PanelData>((subscriber) => {
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
const listenToPanelId = getPanelIdFromQuery(options.queries);
|
||||
@ -49,11 +50,24 @@ export function runSharedRequest(options: QueryRunnerOptions): Observable<PanelD
|
||||
}
|
||||
|
||||
const listenToRunner = listenToPanel.getQueryRunner();
|
||||
const subscription = listenToRunner.getData({ withTransforms: false, withFieldConfig: false }).subscribe({
|
||||
next: (data: PanelData) => {
|
||||
subscriber.next(data);
|
||||
},
|
||||
});
|
||||
const subscription = listenToRunner
|
||||
.getData({
|
||||
withTransforms: Boolean(query?.withTransforms),
|
||||
withFieldConfig: false,
|
||||
})
|
||||
.subscribe({
|
||||
next: (data: PanelData) => {
|
||||
// Use annotation data for series
|
||||
if (query?.topic === DataTopic.Annotations) {
|
||||
data = {
|
||||
...data,
|
||||
series: data.annotations ?? [],
|
||||
annotations: undefined, // remove annotations
|
||||
};
|
||||
}
|
||||
subscriber.next(data);
|
||||
},
|
||||
});
|
||||
|
||||
// If we are in fullscreen the other panel will not execute any queries
|
||||
// So we have to trigger it from here
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { DataFrame, DataQuery, DataQueryError } from '@grafana/data';
|
||||
import { DataFrame, DataQuery, DataQueryError, DataTopic } from '@grafana/data';
|
||||
|
||||
export const SHARED_DASHBOARD_QUERY = '-- Dashboard --';
|
||||
|
||||
export interface DashboardQuery extends DataQuery {
|
||||
panelId?: number;
|
||||
withTransforms?: boolean;
|
||||
topic?: DataTopic;
|
||||
}
|
||||
|
||||
export type ResultInfo = {
|
||||
|
Loading…
Reference in New Issue
Block a user