137 lines
4.1 KiB
TypeScript
137 lines
4.1 KiB
TypeScript
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;
|
|
}
|