From 53ead9904d0b51f648c2da0f75c1c6c414bde792 Mon Sep 17 00:00:00 2001 From: Darren Janeczek <38694490+darrenjaneczek@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:05:48 -0400 Subject: [PATCH] datatrails: interpolate adhoc variables and datasource variables when opening "explore metrics" from dashboard panels (#86252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: interpolate "explore metrics" from panels --------- Co-authored-by: Darren Janeczek * fix: remove support for legacy dashboard - simplify code - take advantage of scenes dashboard async and datasource api object --------- Co-authored-by: Torkel Ödegaard --- .../scene/PanelMenuBehavior.tsx | 2 +- .../features/dashboard/utils/getPanelMenu.ts | 5 - .../Integrations/dashboardIntegration.ts | 91 +++++++++---------- .../app/features/trails/Integrations/utils.ts | 29 +----- 4 files changed, 45 insertions(+), 82 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx index 26d726f3d224..f499bd752a95 100644 --- a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx @@ -168,7 +168,7 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { } if (config.featureToggles.exploreMetrics) { - addDataTrailPanelAction(dashboard, panel, items); + await addDataTrailPanelAction(dashboard, panel, items); } if (exploreMenuItem) { diff --git a/public/app/features/dashboard/utils/getPanelMenu.ts b/public/app/features/dashboard/utils/getPanelMenu.ts index 6dda0cc8b930..f1deeff64dbe 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.ts @@ -25,7 +25,6 @@ import { DashboardInteractions } from 'app/features/dashboard-scene/utils/intera import { InspectTab } from 'app/features/inspector/types'; import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard'; import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils'; -import { addDataTrailPanelAction } from 'app/features/trails/Integrations/dashboardIntegration'; import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { dispatch, store } from 'app/store/store'; @@ -164,10 +163,6 @@ export function getPanelMenu( }); } - if (config.featureToggles.exploreMetrics) { - addDataTrailPanelAction(dashboard, panel, menu); - } - const inspectMenu: PanelMenuItem[] = []; // Only show these inspect actions for data plugins diff --git a/public/app/features/trails/Integrations/dashboardIntegration.ts b/public/app/features/trails/Integrations/dashboardIntegration.ts index 82b2c437b57d..12fa49cc771a 100644 --- a/public/app/features/trails/Integrations/dashboardIntegration.ts +++ b/public/app/features/trails/Integrations/dashboardIntegration.ts @@ -1,55 +1,55 @@ -import { isString } from 'lodash'; - -import { PanelMenuItem, PanelModel } from '@grafana/data'; +import { PanelMenuItem } from '@grafana/data'; +import { PromQuery } from '@grafana/prometheus'; import { getDataSourceSrv } from '@grafana/runtime'; import { SceneTimeRangeLike, VizPanel } from '@grafana/scenes'; -import { DataSourceRef } from '@grafana/schema'; +import { DataQuery, DataSourceRef } from '@grafana/schema'; +import { getQueryRunnerFor } from 'app/features/dashboard-scene/utils/utils'; -import { DashboardModel } from '../../dashboard/state'; import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; import { MetricScene } from '../MetricScene'; import { reportExploreMetrics } from '../interactions'; import { DataTrailEmbedded, DataTrailEmbeddedState } from './DataTrailEmbedded'; -import { SceneDrawerAsScene, launchSceneDrawerInGlobalModal } from './SceneDrawer'; +import { SceneDrawerAsScene } from './SceneDrawer'; import { QueryMetric, getQueryMetrics } from './getQueryMetrics'; -import { - createAdHocFilters, - getPanelType, - getQueryMetricLabel, - getQueryRunner, - getTimeRangeFromDashboard, -} from './utils'; +import { createAdHocFilters, getQueryMetricLabel, getTimeRangeFromDashboard } from './utils'; -export function addDataTrailPanelAction( - dashboard: DashboardScene | DashboardModel, - panel: VizPanel | PanelModel, - items: PanelMenuItem[] -) { - const panelType = getPanelType(panel); - if (panelType !== 'timeseries') { +export async function addDataTrailPanelAction(dashboard: DashboardScene, panel: VizPanel, items: PanelMenuItem[]) { + if (panel.state.pluginId !== 'timeseries') { return; } - const queryRunner = getQueryRunner(panel); - if (!queryRunner) { + const queryRunner = getQueryRunnerFor(panel); + if (queryRunner == null) { return; } - const ds = getDataSourceSrv().getInstanceSettings(queryRunner.state.datasource); + const { queries, datasource, data } = queryRunner.state; - if (ds?.meta.id !== 'prometheus') { + if (datasource == null) { return; } - const queries = queryRunner.state.queries.map((q) => q.expr).filter(isString); + if (datasource.type !== 'prometheus') { + return; + } - const queryMetrics = getQueryMetrics(queries); + const dataSourceApi = await getDataSourceSrv().get(datasource); + + if (dataSourceApi.interpolateVariablesInQueries == null) { + return; + } + + const interpolated = dataSourceApi + .interpolateVariablesInQueries(queries, { __sceneObject: { value: panel } }, data?.request?.filters) + .filter(isPromQuery); + + const queryMetrics = getQueryMetrics(interpolated.map((q) => q.expr)); const subMenu: PanelMenuItem[] = queryMetrics.map((item) => { return { text: getQueryMetricLabel(item), - onClick: createClickHandler(item, dashboard, ds), + onClick: createClickHandler(item, dashboard, dataSourceApi), }; }); @@ -88,11 +88,7 @@ function getEmbeddedTrailsState( return state; } -function createCommonEmbeddedTrailStateProps( - item: QueryMetric, - dashboard: DashboardScene | DashboardModel, - ds: DataSourceRef -) { +function createCommonEmbeddedTrailStateProps(item: QueryMetric, dashboard: DashboardScene, ds: DataSourceRef) { const timeRange = getTimeRangeFromDashboard(dashboard); const trailState = getEmbeddedTrailsState(item, timeRange, ds.uid); const embeddedTrail: DataTrailEmbedded = new DataTrailEmbedded(trailState); @@ -111,21 +107,18 @@ function createCommonEmbeddedTrailStateProps( return commonProps; } -function createClickHandler(item: QueryMetric, dashboard: DashboardScene | DashboardModel, ds: DataSourceRef) { - if (dashboard instanceof DashboardScene) { - return () => { - const commonProps = createCommonEmbeddedTrailStateProps(item, dashboard, ds); - const drawerScene = new SceneDrawerAsScene({ - ...commonProps, - onDismiss: () => dashboard.closeModal(), - }); - reportExploreMetrics('exploration_started', { cause: 'dashboard_panel' }); - dashboard.showModal(drawerScene); - }; - } else { - return () => { - reportExploreMetrics('exploration_started', { cause: 'dashboard_panel' }); - launchSceneDrawerInGlobalModal(createCommonEmbeddedTrailStateProps(item, dashboard, ds)); - }; - } +function createClickHandler(item: QueryMetric, dashboard: DashboardScene, ds: DataSourceRef) { + return () => { + const commonProps = createCommonEmbeddedTrailStateProps(item, dashboard, ds); + const drawerScene = new SceneDrawerAsScene({ + ...commonProps, + onDismiss: () => dashboard.closeModal(), + }); + reportExploreMetrics('exploration_started', { cause: 'dashboard_panel' }); + dashboard.showModal(drawerScene); + }; +} + +export function isPromQuery(model: DataQuery): model is PromQuery { + return 'expr' in model; } diff --git a/public/app/features/trails/Integrations/utils.ts b/public/app/features/trails/Integrations/utils.ts index f5faf434c02b..372d81ae999a 100644 --- a/public/app/features/trails/Integrations/utils.ts +++ b/public/app/features/trails/Integrations/utils.ts @@ -1,9 +1,5 @@ -import { PanelModel } from '@grafana/data'; import { QueryBuilderLabelFilter } from '@grafana/prometheus/src/querybuilder/shared/types'; -import { SceneQueryRunner, SceneTimeRange, VizPanel } from '@grafana/scenes'; -import { DashboardModel } from 'app/features/dashboard/state'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; -import { getQueryRunnerFor } from 'app/features/dashboard-scene/utils/utils'; import { QueryMetric } from './getQueryMetrics'; @@ -12,22 +8,8 @@ export function isEquals(labelFilter: QueryBuilderLabelFilter) { return labelFilter.op === '='; } -export function getQueryRunner(panel: VizPanel | PanelModel) { - if (panel instanceof VizPanel) { - return getQueryRunnerFor(panel); - } - - return new SceneQueryRunner({ datasource: panel.datasource || undefined, queries: panel.targets || [] }); -} - -export function getTimeRangeFromDashboard(dashboard: DashboardScene | DashboardModel) { - if (dashboard instanceof DashboardScene) { - return dashboard.state.$timeRange!.clone(); - } - if (dashboard instanceof DashboardModel) { - return new SceneTimeRange({ ...dashboard.time }); - } - return new SceneTimeRange(); +export function getTimeRangeFromDashboard(dashboard: DashboardScene) { + return dashboard.state.$timeRange!.clone(); } export function getQueryMetricLabel({ metric, labelFilters }: QueryMetric) { @@ -43,10 +25,3 @@ export function getQueryMetricLabel({ metric, labelFilters }: QueryMetric) { export function createAdHocFilters(labels: QueryBuilderLabelFilter[]) { return labels?.map((label) => ({ key: label.label, value: label.value, operator: label.op })); } - -export function getPanelType(panel: VizPanel | PanelModel) { - if (panel instanceof VizPanel) { - return panel.state.pluginId; - } - return panel.type; -}