123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- import { chain } from 'lodash';
- import {
- DataFrame,
- DataLink,
- DataLinkBuiltInVars,
- deprecationWarning,
- Field,
- FieldType,
- getFieldDisplayName,
- InterpolateFunction,
- KeyValue,
- LinkModel,
- locationUtil,
- ScopedVars,
- textUtil,
- urlUtil,
- VariableOrigin,
- VariableSuggestion,
- VariableSuggestionsScope,
- } from '@grafana/data';
- import { getTemplateSrv } from '@grafana/runtime';
- import { getConfig } from 'app/core/config';
- import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
- import { getVariablesUrlParams } from '../../variables/getAllVariableValuesForUrl';
- const timeRangeVars = [
- {
- value: `${DataLinkBuiltInVars.keepTime}`,
- label: 'Time range',
- documentation: 'Adds current time range',
- origin: VariableOrigin.BuiltIn,
- },
- {
- value: `${DataLinkBuiltInVars.timeRangeFrom}`,
- label: 'Time range: from',
- documentation: "Adds current time range's from value",
- origin: VariableOrigin.BuiltIn,
- },
- {
- value: `${DataLinkBuiltInVars.timeRangeTo}`,
- label: 'Time range: to',
- documentation: "Adds current time range's to value",
- origin: VariableOrigin.BuiltIn,
- },
- ];
- const seriesVars = [
- {
- value: `${DataLinkBuiltInVars.seriesName}`,
- label: 'Name',
- documentation: 'Name of the series',
- origin: VariableOrigin.Series,
- },
- ];
- const valueVars = [
- {
- value: `${DataLinkBuiltInVars.valueNumeric}`,
- label: 'Numeric',
- documentation: 'Numeric representation of selected value',
- origin: VariableOrigin.Value,
- },
- {
- value: `${DataLinkBuiltInVars.valueText}`,
- label: 'Text',
- documentation: 'Text representation of selected value',
- origin: VariableOrigin.Value,
- },
- {
- value: `${DataLinkBuiltInVars.valueRaw}`,
- label: 'Raw',
- documentation: 'Raw value',
- origin: VariableOrigin.Value,
- },
- ];
- const buildLabelPath = (label: string) => {
- return label.includes('.') || label.trim().includes(' ') ? `["${label}"]` : `.${label}`;
- };
- export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [
- ...getTemplateSrv()
- .getVariables()
- .map((variable) => ({
- value: variable.name as string,
- label: variable.name,
- origin: VariableOrigin.Template,
- })),
- {
- value: `${DataLinkBuiltInVars.includeVars}`,
- label: 'All variables',
- documentation: 'Adds current variables',
- origin: VariableOrigin.Template,
- },
- ...timeRangeVars,
- ];
- const getFieldVars = (dataFrames: DataFrame[]) => {
- const all = [];
- for (const df of dataFrames) {
- for (const f of df.fields) {
- if (f.labels) {
- for (const k of Object.keys(f.labels)) {
- all.push(k);
- }
- }
- }
- }
- const labels = chain(all).flatten().uniq().value();
- return [
- {
- value: `${DataLinkBuiltInVars.fieldName}`,
- label: 'Name',
- documentation: 'Field name of the clicked datapoint (in ms epoch)',
- origin: VariableOrigin.Field,
- },
- ...labels.map((label) => ({
- value: `__field.labels${buildLabelPath(label)}`,
- label: `labels.${label}`,
- documentation: `${label} label value`,
- origin: VariableOrigin.Field,
- })),
- ];
- };
- export const getDataFrameVars = (dataFrames: DataFrame[]) => {
- let numeric: Field | undefined = undefined;
- let title: Field | undefined = undefined;
- const suggestions: VariableSuggestion[] = [];
- const keys: KeyValue<true> = {};
- if (dataFrames.length !== 1) {
- // It's not possible to access fields of other dataframes. So if there are multiple dataframes we need to skip these suggestions.
- // Also return early if there are no dataFrames.
- return [];
- }
- const frame = dataFrames[0];
- for (const field of frame.fields) {
- const displayName = getFieldDisplayName(field, frame, dataFrames);
- if (keys[displayName]) {
- continue;
- }
- suggestions.push({
- value: `__data.fields${buildLabelPath(displayName)}`,
- label: `${displayName}`,
- documentation: `Formatted value for ${displayName} on the same row`,
- origin: VariableOrigin.Fields,
- });
- keys[displayName] = true;
- if (!numeric && field.type === FieldType.number) {
- numeric = { ...field, name: displayName };
- }
- if (!title && field.config.displayName && field.config.displayName !== field.name) {
- title = { ...field, name: displayName };
- }
- }
- if (suggestions.length) {
- suggestions.push({
- value: `__data.fields[0]`,
- label: `Select by index`,
- documentation: `Enter the field order`,
- origin: VariableOrigin.Fields,
- });
- }
- if (numeric) {
- suggestions.push({
- value: `__data.fields${buildLabelPath(numeric.name)}.numeric`,
- label: `Show numeric value`,
- documentation: `the numeric field value`,
- origin: VariableOrigin.Fields,
- });
- suggestions.push({
- value: `__data.fields${buildLabelPath(numeric.name)}.text`,
- label: `Show text value`,
- documentation: `the text value`,
- origin: VariableOrigin.Fields,
- });
- }
- if (title) {
- suggestions.push({
- value: `__data.fields${buildLabelPath(title.name)}`,
- label: `Select by title`,
- documentation: `Use the title to pick the field`,
- origin: VariableOrigin.Fields,
- });
- }
- return suggestions;
- };
- export const getDataLinksVariableSuggestions = (
- dataFrames: DataFrame[],
- scope?: VariableSuggestionsScope
- ): VariableSuggestion[] => {
- const valueTimeVar = {
- value: `${DataLinkBuiltInVars.valueTime}`,
- label: 'Time',
- documentation: 'Time value of the clicked datapoint (in ms epoch)',
- origin: VariableOrigin.Value,
- };
- const includeValueVars = scope === VariableSuggestionsScope.Values;
- return includeValueVars
- ? [
- ...seriesVars,
- ...getFieldVars(dataFrames),
- ...valueVars,
- valueTimeVar,
- ...getDataFrameVars(dataFrames),
- ...getPanelLinksVariableSuggestions(),
- ]
- : [
- ...seriesVars,
- ...getFieldVars(dataFrames),
- ...getDataFrameVars(dataFrames),
- ...getPanelLinksVariableSuggestions(),
- ];
- };
- export const getCalculationValueDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
- const fieldVars = getFieldVars(dataFrames);
- const valueCalcVar = {
- value: `${DataLinkBuiltInVars.valueCalc}`,
- label: 'Calculation name',
- documentation: 'Name of the calculation the value is a result of',
- origin: VariableOrigin.Value,
- };
- return [...seriesVars, ...fieldVars, ...valueVars, valueCalcVar, ...getPanelLinksVariableSuggestions()];
- };
- export interface LinkService {
- getDataLinkUIModel: <T>(link: DataLink, replaceVariables: InterpolateFunction | undefined, origin: T) => LinkModel<T>;
- getAnchorInfo: (link: any) => any;
- getLinkUrl: (link: any) => string;
- }
- export class LinkSrv implements LinkService {
- getLinkUrl(link: any) {
- let url = locationUtil.assureBaseUrl(getTemplateSrv().replace(link.url || ''));
- let params: { [key: string]: any } = {};
- if (link.keepTime) {
- const range = getTimeSrv().timeRangeForUrl();
- params['from'] = range.from;
- params['to'] = range.to;
- }
- if (link.includeVars) {
- params = {
- ...params,
- ...getVariablesUrlParams(),
- };
- }
- url = urlUtil.appendQueryToUrl(url, urlUtil.toUrlParams(params));
- return getConfig().disableSanitizeHtml ? url : textUtil.sanitizeUrl(url);
- }
- getAnchorInfo(link: any) {
- const templateSrv = getTemplateSrv();
- const info: any = {};
- info.href = this.getLinkUrl(link);
- info.title = templateSrv.replace(link.title || '');
- info.tooltip = templateSrv.replace(link.tooltip || '');
- return info;
- }
- /**
- * Returns LinkModel which is basically a DataLink with all values interpolated through the templateSrv.
- */
- getDataLinkUIModel = <T>(
- link: DataLink,
- replaceVariables: InterpolateFunction | undefined,
- origin: T
- ): LinkModel<T> => {
- let href = link.url;
- if (link.onBuildUrl) {
- href = link.onBuildUrl({
- origin,
- replaceVariables,
- });
- }
- const info: LinkModel<T> = {
- href: locationUtil.assureBaseUrl(href.replace(/\n/g, '')),
- title: link.title ?? '',
- target: link.targetBlank ? '_blank' : undefined,
- origin,
- };
- if (replaceVariables) {
- info.href = replaceVariables(info.href);
- info.title = replaceVariables(link.title);
- }
- if (link.onClick) {
- info.onClick = (e) => {
- link.onClick!({
- origin,
- replaceVariables,
- e,
- });
- };
- }
- info.href = getConfig().disableSanitizeHtml ? info.href : textUtil.sanitizeUrl(info.href);
- return info;
- };
- /**
- * getPanelLinkAnchorInfo method is left for plugins compatibility reasons
- *
- * @deprecated Drilldown links should be generated using getDataLinkUIModel method
- */
- getPanelLinkAnchorInfo(link: DataLink, scopedVars: ScopedVars) {
- deprecationWarning('link_srv.ts', 'getPanelLinkAnchorInfo', 'getDataLinkUIModel');
- const replace: InterpolateFunction = (value, vars, fmt) =>
- getTemplateSrv().replace(value, { ...scopedVars, ...vars }, fmt);
- return this.getDataLinkUIModel(link, replace, {});
- }
- }
- let singleton: LinkService | undefined;
- export function setLinkSrv(srv: LinkService) {
- singleton = srv;
- }
- export function getLinkSrv(): LinkService {
- if (!singleton) {
- singleton = new LinkSrv();
- }
- return singleton;
- }
|