import { css } from '@emotion/css'; import { useId } from '@react-aria/utils'; import pluralize from 'pluralize'; import { useCallback, useMemo } from 'react'; import { useAsync } from 'react-use'; import { DataQuery, GrafanaTheme2, SelectableValue, DataTopic, QueryEditorProps } from '@grafana/data'; import { OperationsEditorRow } from '@grafana/plugin-ui'; import { Field, Select, useStyles2, Spinner, RadioButtonGroup, Stack, InlineSwitch } from '@grafana/ui'; import config from 'app/core/config'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { filterPanelDataToQuery } from 'app/features/query/components/QueryEditorRow'; import { MIXED_DATASOURCE_NAME } from '../mixed/MixedDataSource'; import { SHARED_DASHBOARD_QUERY } from './constants'; import { DashboardDatasource } from './datasource'; import { DashboardQuery, ResultInfo } from './types'; function getQueryDisplayText(query: DataQuery): string { return JSON.stringify(query); } function isPanelInEdit(panelId: number, panelInEditId?: number) { let idToCompareWith = panelInEditId; if (window.__grafanaSceneContext && window.__grafanaSceneContext instanceof DashboardScene) { idToCompareWith = window.__grafanaSceneContext.state.editPanel?.getPanelId(); } return panelId === idToCompareWith; } interface Props extends QueryEditorProps {} const topics = [ { label: 'All data', value: false }, { label: 'Annotations', value: true, description: 'Include annotations as regular data' }, ]; export const INVALID_PANEL_DESCRIPTION = 'Contains a shared dashboard query'; export function DashboardQueryEditor({ data, query, onChange, onRunQuery }: Props) { const { value: defaultDatasource } = useAsync(() => getDatasourceSrv().get()); const panel = useMemo(() => { const dashboard = getDashboardSrv().getCurrent(); return dashboard?.getPanelById(query.panelId ?? -124134); }, [query.panelId]); const { value: results, loading: loadingResults } = useAsync(async (): Promise => { if (!panel || !data) { 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(data, query.refId) ?? data; return { refId: query.refId, query: fmt(query), name: ds.name, img: ds.meta.info.logos.small, data: queryData.series, error: queryData.error, }; }) ); }, [data, panel]); const onUpdateQuery = useCallback( (query: DashboardQuery) => { onChange(query); onRunQuery(); }, [onChange, onRunQuery] ); const onPanelChanged = useCallback( (id: number) => { onUpdateQuery({ ...query, panelId: id, }); }, [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 isMixedDSWithDashboardQueries = (panel: PanelModel) => { return ( panel.datasource?.uid === MIXED_DATASOURCE_NAME && panel.targets.some((t) => t.datasource?.uid === SHARED_DASHBOARD_QUERY) ); }; const getPanelDescription = useCallback( (panel: PanelModel): string => { const datasource = panel.datasource ?? defaultDatasource; const dsname = getDatasourceSrv().getInstanceSettings(datasource)?.name; const queryCount = panel.targets.length; return `${queryCount} ${pluralize('query', queryCount)} to ${dsname}`; }, [defaultDatasource] ); const dashboard = getDashboardSrv().getCurrent(); const showTransforms = Boolean(query.withTransforms || panel?.transformations?.length); const panels: Array> = useMemo( () => dashboard?.panels .filter( (panel) => config.panels[panel.type] && panel.targets && !isPanelInEdit(panel.id, dashboard.panelInEdit?.id) ) .map((panel) => { let description = getPanelDescription(panel); let isDisabled = false; if (panel.datasource?.uid === SHARED_DASHBOARD_QUERY || isMixedDSWithDashboardQueries(panel)) { description = INVALID_PANEL_DESCRIPTION; isDisabled = true; } return { value: panel.id, label: panel.title ?? 'Panel ' + panel.id, imgUrl: config.panels[panel.type].info.logos.small, description, isDisabled, }; }) ?? [], [dashboard, getPanelDescription] ); const styles = useStyles2(getStyles); const selectId = useId(); if (!dashboard) { return null; } if (panels.length < 1) { return (

This dashboard does not have any other panels. Add queries to other panels and try again.

); } const selected = panels.find((panel) => panel.value === query.panelId); return (