The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/trails/MetricSelect/AddToExplorationsButton.tsx

136 lines
4.1 KiB

import { DataFrame, TimeRange } from '@grafana/data';
import { usePluginLinks } from '@grafana/runtime';
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectState, SceneQueryRunner } from '@grafana/scenes';
import { DataQuery, DataSourceRef } from '@grafana/schema';
import { IconButton } from '@grafana/ui';
import MimirLogo from '../../../plugins/datasource/prometheus/img/mimir_logo.svg';
import { VAR_DATASOURCE_EXPR } from '../shared';
export const explorationsPluginId = 'grafana-explorations-app';
export const extensionPointId = 'grafana-explore-metrics/exploration/v1';
export const addToExplorationsButtonLabel = 'add panel to exploration';
export interface AddToExplorationButtonState extends SceneObjectState {
frame?: DataFrame;
dsUid?: string;
labelName?: string;
fieldName?: string;
context?: ExtensionContext;
queries: DataQuery[];
}
interface ExtensionContext {
timeRange: TimeRange;
queries: DataQuery[];
datasource: DataSourceRef;
origin: string;
url: string;
type: string;
title: string;
id: string;
logoPath: string;
note?: string;
drillDownLabel?: string;
}
export class AddToExplorationButton extends SceneObjectBase<AddToExplorationButtonState> {
constructor(state: Omit<AddToExplorationButtonState, 'queries'>) {
super({ ...state, queries: [] });
this.addActivationHandler(this._onActivate.bind(this));
}
private _onActivate = () => {
this._subs.add(
this.subscribeToState(() => {
this.getQueries();
this.getContext();
})
);
const datasourceUid = sceneGraph.interpolate(this, VAR_DATASOURCE_EXPR);
this.setState({ dsUid: datasourceUid });
};
private readonly getQueries = () => {
const data = sceneGraph.getData(this);
const queryRunner = sceneGraph.findObject(data, isQueryRunner);
if (isQueryRunner(queryRunner)) {
const filter = this.state.frame ? getFilter(this.state.frame) : null;
const queries = queryRunner.state.queries.map((q) => ({
...q,
expr: sceneGraph.interpolate(queryRunner, q.expr),
legendFormat: filter?.name ? `{{ ${filter.name} }}` : sceneGraph.interpolate(queryRunner, q.legendFormat),
}));
if (JSON.stringify(queries) !== JSON.stringify(this.state.queries)) {
this.setState({ queries });
}
}
};
private readonly getContext = () => {
const { queries, dsUid, labelName, fieldName } = this.state;
const timeRange = sceneGraph.getTimeRange(this);
if (!timeRange || !queries || !dsUid) {
return;
}
const ctx = {
origin: 'Metrics Drilldown',
type: 'timeseries',
queries,
timeRange: { ...timeRange.state.value },
datasource: { uid: dsUid },
url: window.location.href,
id: `${JSON.stringify(queries)}${labelName}${fieldName}`,
title: `${labelName}${fieldName ? ` > ${fieldName}` : ''}`,
logoPath: MimirLogo,
drillDownLabel: fieldName,
};
if (JSON.stringify(ctx) !== JSON.stringify(this.state.context)) {
this.setState({ context: ctx });
}
};
public static Component = ({ model }: SceneComponentProps<AddToExplorationButton>) => {
const { context } = model.useState();
const { links } = usePluginLinks({ extensionPointId, context, limitPerPlugin: 1 });
const link = links.find((link) => link.pluginId === explorationsPluginId);
if (!link) {
return null;
}
return (
<IconButton
tooltip={link.description}
aria-label={addToExplorationsButtonLabel} // this is overriden by the `tooltip`
key={link.id}
name={link.icon ?? 'panel-add'}
onClick={(e) => {
if (link.onClick) {
link.onClick(e);
}
}}
/>
);
};
}
const getFilter = (frame: DataFrame) => {
const filterNameAndValueObj = frame.fields[1]?.labels ?? {};
const keys = Object.keys(filterNameAndValueObj);
if (keys.length !== 1) {
return;
}
const name = keys[0];
return { name, value: filterNameAndValueObj[name] };
};
function isQueryRunner(o: unknown): o is SceneQueryRunner {
return o instanceof SceneQueryRunner;
}