123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import { useCallback } from 'react';
- import {
- Field,
- LinkModel,
- TimeRange,
- mapInternalLinkToExplore,
- InterpolateFunction,
- ScopedVars,
- DataFrame,
- getFieldDisplayValuesProxy,
- SplitOpen,
- } from '@grafana/data';
- import { getTemplateSrv } from '@grafana/runtime';
- import { contextSrv } from 'app/core/services/context_srv';
- import { getLinkSrv } from '../../panel/panellinks/link_srv';
- /**
- * Get links from the field of a dataframe and in addition check if there is associated
- * metadata with datasource in which case we will add onClick to open the link in new split window. This assumes
- * that we just supply datasource name and field value and Explore split window will know how to render that
- * appropriately. This is for example used for transition from log with traceId to trace datasource to show that
- * trace.
- */
- export const getFieldLinksForExplore = (options: {
- field: Field;
- rowIndex: number;
- splitOpenFn?: SplitOpen;
- range: TimeRange;
- vars?: ScopedVars;
- dataFrame?: DataFrame;
- }): Array<LinkModel<Field>> => {
- const { field, vars, splitOpenFn, range, rowIndex, dataFrame } = options;
- const scopedVars: any = { ...(vars || {}) };
- scopedVars['__value'] = {
- value: {
- raw: field.values.get(rowIndex),
- },
- text: 'Raw value',
- };
- // If we have a dataFrame we can allow referencing other columns and their values in the interpolation.
- if (dataFrame) {
- scopedVars['__data'] = {
- value: {
- name: dataFrame.name,
- refId: dataFrame.refId,
- fields: getFieldDisplayValuesProxy({
- frame: dataFrame,
- rowIndex,
- }),
- },
- text: 'Data',
- };
- }
- if (field.config.links) {
- const links = [];
- if (!contextSrv.hasAccessToExplore()) {
- links.push(...field.config.links.filter((l) => !l.internal));
- } else {
- links.push(...field.config.links);
- }
- return links.map((link) => {
- if (!link.internal) {
- const replace: InterpolateFunction = (value, vars) =>
- getTemplateSrv().replace(value, { ...vars, ...scopedVars });
- const linkModel = getLinkSrv().getDataLinkUIModel(link, replace, field);
- if (!linkModel.title) {
- linkModel.title = getTitleFromHref(linkModel.href);
- }
- return linkModel;
- } else {
- return mapInternalLinkToExplore({
- link,
- internalLink: link.internal,
- scopedVars: scopedVars,
- range,
- field,
- onClickFn: splitOpenFn,
- replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()),
- });
- }
- });
- }
- return [];
- };
- function getTitleFromHref(href: string): string {
- // The URL constructor needs the url to have protocol
- if (href.indexOf('://') < 0) {
- // Doesn't really matter what protocol we use.
- href = `http://${href}`;
- }
- let title;
- try {
- const parsedUrl = new URL(href);
- title = parsedUrl.hostname;
- } catch (_e) {
- // Should be good enough fallback, user probably did not input valid url.
- title = href;
- }
- return title;
- }
- /**
- * Hook that returns a function that can be used to retrieve all the links for a row. This returns all the links from
- * all the fields so is useful for visualisation where the whole row is represented as single clickable item like a
- * service map.
- */
- export function useLinks(range: TimeRange, splitOpenFn?: SplitOpen) {
- return useCallback(
- (dataFrame: DataFrame, rowIndex: number) => {
- return dataFrame.fields.flatMap((f) => {
- if (f.config?.links && f.config?.links.length) {
- return getFieldLinksForExplore({
- field: f,
- rowIndex: rowIndex,
- range,
- dataFrame,
- splitOpenFn,
- });
- } else {
- return [];
- }
- });
- },
- [range, splitOpenFn]
- );
- }
|