From 08c24e413339d09e260218e3baf93061a4e9f7d1 Mon Sep 17 00:00:00 2001 From: Darren Janeczek <38694490+darrenjaneczek@users.noreply.github.com> Date: Fri, 22 Mar 2024 10:05:20 -0400 Subject: [PATCH] datatrails: feat: metadata for metric panels (#84872) * feat: metadata for metric panels * fix: missing import? * fix: async await to get description first - also add description to metric-name-only panels * fix: change description, remove from main viz * fix: remove import --- .../trails/ActionTabs/MetricOverviewScene.tsx | 21 +----- .../AutomaticMetricQueries/previewPanel.ts | 3 +- public/app/features/trails/DataTrail.tsx | 23 ++++++ .../app/features/trails/MetricSelectScene.tsx | 15 +++- .../trails/helpers/MetricDatasourceHelper.ts | 74 +++++++++++++++++++ 5 files changed, 113 insertions(+), 23 deletions(-) create mode 100644 public/app/features/trails/helpers/MetricDatasourceHelper.ts diff --git a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx index 3c5eef35f14..7dfd896d2a7 100644 --- a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx +++ b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx @@ -10,9 +10,7 @@ import { } from '@grafana/scenes'; import { Stack, Text, TextLink } from '@grafana/ui'; -import PrometheusLanguageProvider from '../../../plugins/datasource/prometheus/language_provider'; import { PromMetricsMetadataItem } from '../../../plugins/datasource/prometheus/types'; -import { getDatasourceSrv } from '../../plugins/datasource_srv'; import { ALL_VARIABLE_VALUE } from '../../variables/constants'; import { StatusWrapper } from '../StatusWrapper'; import { TRAILS_ROUTE, VAR_DATASOURCE_EXPR, VAR_GROUP_BY } from '../shared'; @@ -58,25 +56,12 @@ export class MetricOverviewScene extends SceneObjectBase) => { diff --git a/public/app/features/trails/AutomaticMetricQueries/previewPanel.ts b/public/app/features/trails/AutomaticMetricQueries/previewPanel.ts index ee23944b4c2..7b77811f2c5 100644 --- a/public/app/features/trails/AutomaticMetricQueries/previewPanel.ts +++ b/public/app/features/trails/AutomaticMetricQueries/previewPanel.ts @@ -8,12 +8,13 @@ import { getColorByIndex } from '../utils'; import { getAutoQueriesForMetric } from './AutoQueryEngine'; -export function getPreviewPanelFor(metric: string, index: number, currentFilterCount: number) { +export function getPreviewPanelFor(metric: string, index: number, currentFilterCount: number, description?: string) { const autoQuery = getAutoQueriesForMetric(metric); const vizPanel = autoQuery.preview .vizBuilder() .setColor({ mode: 'fixed', fixedColor: getColorByIndex(index) }) + .setDescription(description) .setHeaderActions(new SelectMetricAction({ metric, title: 'Select' })) .build(); diff --git a/public/app/features/trails/DataTrail.tsx b/public/app/features/trails/DataTrail.tsx index 43685a32c63..04b7493dd6a 100644 --- a/public/app/features/trails/DataTrail.tsx +++ b/public/app/features/trails/DataTrail.tsx @@ -18,7 +18,9 @@ import { SceneRefreshPicker, SceneTimePicker, SceneTimeRange, + SceneVariable, SceneVariableSet, + VariableDependencyConfig, VariableValueSelectors, } from '@grafana/scenes'; import { useStyles2 } from '@grafana/ui'; @@ -29,6 +31,7 @@ import { MetricScene } from './MetricScene'; import { MetricSelectScene } from './MetricSelectScene'; import { MetricsHeader } from './MetricsHeader'; import { getTrailStore } from './TrailStore/TrailStore'; +import { MetricDatasourceHelper } from './helpers/MetricDatasourceHelper'; import { MetricSelectedEvent, trailDS, VAR_DATASOURCE, VAR_FILTERS } from './shared'; import { getUrlForTrail } from './utils'; @@ -111,6 +114,26 @@ export class DataTrail extends SceneObjectBase { }; } + protected _variableDependency = new VariableDependencyConfig(this, { + variableNames: [VAR_DATASOURCE], + onReferencedVariableValueChanged: async (variable: SceneVariable) => { + const { name } = variable.state; + if (name === VAR_DATASOURCE) { + this.datasourceHelper.reset(); + } + }, + }); + + private datasourceHelper = new MetricDatasourceHelper(this); + + public getMetricMetadata(metric?: string) { + return this.datasourceHelper.getMetricMetadata(metric); + } + + public getCurrentMetricMetadata() { + return this.getMetricMetadata(this.state.metric); + } + private goBackToStep(step: DataTrailHistoryStep) { if (!this.state.embedded) { getUrlSyncManager().cleanUp(this); diff --git a/public/app/features/trails/MetricSelectScene.tsx b/public/app/features/trails/MetricSelectScene.tsx index 0c2c467806b..af2474c2e5a 100644 --- a/public/app/features/trails/MetricSelectScene.tsx +++ b/public/app/features/trails/MetricSelectScene.tsx @@ -26,6 +26,7 @@ import { getPreviewPanelFor } from './AutomaticMetricQueries/previewPanel'; import { MetricScene } from './MetricScene'; import { SelectMetricAction } from './SelectMetricAction'; import { StatusWrapper } from './StatusWrapper'; +import { getMetricDescription } from './helpers/MetricDatasourceHelper'; import { sortRelatedMetrics } from './relatedMetrics'; import { getVariablesWithMetricConstant, trailDS, VAR_DATASOURCE, VAR_FILTERS_EXPR, VAR_METRIC_NAMES } from './shared'; import { getFilters, getTrailFor } from './utils'; @@ -202,7 +203,7 @@ export class MetricSelectScene extends SceneObjectBase { this.previewCache = metricsMap; } - private buildLayout() { + private async buildLayout() { // Temp hack when going back to select metric scene and variable updates if (this.ignoreNextUpdate) { this.ignoreNextUpdate = false; @@ -225,6 +226,8 @@ export class MetricSelectScene extends SceneObjectBase { const children: SceneFlexItem[] = []; + const trail = getTrailFor(this); + const metricsList = this.sortedPreviewMetrics(); // Get the current filters to determine the count of them @@ -234,13 +237,16 @@ export class MetricSelectScene extends SceneObjectBase { for (let index = 0; index < metricsList.length; index++) { const metric = metricsList[index]; + const metadata = await trail.getMetricMetadata(metric.name); + const description = getMetricDescription(metadata); if (this.state.showPreviews) { if (metric.itemRef && metric.isPanel) { children.push(metric.itemRef.resolve()); continue; } - const panel = getPreviewPanelFor(metric.name, index, currentFilterCount); + const panel = getPreviewPanelFor(metric.name, index, currentFilterCount, description); + metric.itemRef = panel.getRef(); metric.isPanel = true; children.push(panel); @@ -249,7 +255,7 @@ export class MetricSelectScene extends SceneObjectBase { $variables: new SceneVariableSet({ variables: getVariablesWithMetricConstant(metric.name), }), - body: getCardPanelFor(metric.name), + body: getCardPanelFor(metric.name, description), }); metric.itemRef = panel.getRef(); metric.isPanel = false; @@ -357,9 +363,10 @@ function getMetricNamesVariableSet() { }); } -function getCardPanelFor(metric: string) { +function getCardPanelFor(metric: string, description?: string) { return PanelBuilders.text() .setTitle(metric) + .setDescription(description) .setHeaderActions(new SelectMetricAction({ metric, title: 'Select' })) .setOption('content', '') .build(); diff --git a/public/app/features/trails/helpers/MetricDatasourceHelper.ts b/public/app/features/trails/helpers/MetricDatasourceHelper.ts new file mode 100644 index 00000000000..48aa4f9c3d1 --- /dev/null +++ b/public/app/features/trails/helpers/MetricDatasourceHelper.ts @@ -0,0 +1,74 @@ +import { DataSourceApi } from '@grafana/data'; +import { PromMetricsMetadata, PromMetricsMetadataItem } from '@grafana/prometheus'; +import { getDataSourceSrv } from '@grafana/runtime'; + +import PrometheusLanguageProvider from '../../../plugins/datasource/prometheus/language_provider'; +import { DataTrail } from '../DataTrail'; +import { VAR_DATASOURCE_EXPR } from '../shared'; + +export class MetricDatasourceHelper { + constructor(trail: DataTrail) { + this._trail = trail; + } + + public reset() { + this._datasource = undefined; + this._metricsMetadata = undefined; + } + + private _trail: DataTrail; + + private _datasource?: Promise; + + private async getDatasource() { + if (!this._datasource) { + this._datasource = getDataSourceSrv().get(VAR_DATASOURCE_EXPR, { __sceneObject: { value: this._trail } }); + } + + const ds = await this._datasource; + return ds; + } + + private _metricsMetadata?: Promise; + + private async _getMetricsMetadata() { + const ds = await this.getDatasource(); + + if (ds.languageProvider instanceof PrometheusLanguageProvider) { + if (!ds.languageProvider.metricsMetadata) { + await ds.languageProvider.start(); + } + + return ds.languageProvider.metricsMetadata!; + } + return undefined; + } + + public async getMetricMetadata(metric?: string) { + if (!metric) { + return undefined; + } + if (!this._metricsMetadata) { + this._metricsMetadata = this._getMetricsMetadata(); + } + + const metadata = await this._metricsMetadata; + return metadata?.[metric]; + } +} + +export function getMetricDescription(metadata?: PromMetricsMetadataItem) { + if (!metadata) { + return undefined; + } + + const { type, help, unit } = metadata; + + const lines = [ + help, // + type && `**Type:** *${type}*`, + unit && `**Unit:** ${unit}`, + ]; + + return lines.join('\n\n'); +}