mirror of
https://github.com/grafana/grafana.git
synced 2024-11-27 11:20:27 -06:00
Glue: Show data links only for fully interpolated correlations (#59052)
* Create data link filters for Explore * Add comments and make the code more explicit
This commit is contained in:
parent
57fbe264ea
commit
454c8841a4
@ -1,5 +1,6 @@
|
||||
import {
|
||||
ArrayVector,
|
||||
DataFrame,
|
||||
DataLink,
|
||||
dateTime,
|
||||
Field,
|
||||
@ -7,9 +8,11 @@ import {
|
||||
InterpolateFunction,
|
||||
LinkModel,
|
||||
TimeRange,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { setTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { initTemplateSrv } from '../../../../test/helpers/initTemplateSrv';
|
||||
import { setContextSrv } from '../../../core/services/context_srv';
|
||||
import { setLinkSrv } from '../../panel/panellinks/link_srv';
|
||||
|
||||
@ -17,18 +20,13 @@ import { getFieldLinksForExplore } from './links';
|
||||
|
||||
describe('getFieldLinksForExplore', () => {
|
||||
beforeEach(() => {
|
||||
setTemplateSrv({
|
||||
replace(target, scopedVars, format) {
|
||||
return target ?? '';
|
||||
},
|
||||
getVariables() {
|
||||
return [];
|
||||
},
|
||||
containsTemplate() {
|
||||
return false;
|
||||
},
|
||||
updateTimeRange(timeRange: TimeRange) {},
|
||||
});
|
||||
setTemplateSrv(
|
||||
initTemplateSrv('key', [
|
||||
{ type: 'custom', name: 'emptyVar', current: { value: null } },
|
||||
{ type: 'custom', name: 'num', current: { value: 1 } },
|
||||
{ type: 'custom', name: 'test', current: { value: 'foo' } },
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it('returns correct link model for external link', () => {
|
||||
@ -36,7 +34,12 @@ describe('getFieldLinksForExplore', () => {
|
||||
title: 'external',
|
||||
url: 'http://regionalhost',
|
||||
});
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: 0, splitOpenFn: jest.fn(), range });
|
||||
const links = getFieldLinksForExplore({
|
||||
field,
|
||||
rowIndex: ROW_WITH_TEXT_VALUE.index,
|
||||
splitOpenFn: jest.fn(),
|
||||
range,
|
||||
});
|
||||
|
||||
expect(links[0].href).toBe('http://regionalhost');
|
||||
expect(links[0].title).toBe('external');
|
||||
@ -47,7 +50,12 @@ describe('getFieldLinksForExplore', () => {
|
||||
title: '',
|
||||
url: 'http://regionalhost',
|
||||
});
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: 0, splitOpenFn: jest.fn(), range });
|
||||
const links = getFieldLinksForExplore({
|
||||
field,
|
||||
rowIndex: ROW_WITH_TEXT_VALUE.index,
|
||||
splitOpenFn: jest.fn(),
|
||||
range,
|
||||
});
|
||||
|
||||
expect(links[0].href).toBe('http://regionalhost');
|
||||
expect(links[0].title).toBe('regionalhost');
|
||||
@ -69,7 +77,7 @@ describe('getFieldLinksForExplore', () => {
|
||||
},
|
||||
});
|
||||
const splitfn = jest.fn();
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: 0, splitOpenFn: splitfn, range });
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, splitOpenFn: splitfn, range });
|
||||
|
||||
expect(links[0].href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
@ -102,7 +110,7 @@ describe('getFieldLinksForExplore', () => {
|
||||
},
|
||||
false
|
||||
);
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: 0, range });
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range });
|
||||
|
||||
expect(links[0].href).toBe('http://regionalhost');
|
||||
expect(links[0].title).toBe('external');
|
||||
@ -121,11 +129,42 @@ describe('getFieldLinksForExplore', () => {
|
||||
},
|
||||
false
|
||||
);
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: 0, range });
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range });
|
||||
expect(links).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('returns internal links when target contains defined template variables', () => {
|
||||
const { field, range, dataFrame } = setup({
|
||||
title: '',
|
||||
url: '',
|
||||
internal: {
|
||||
query: { query: 'query_1-${__data.fields.flux-dimensions}' },
|
||||
datasourceUid: 'uid_1',
|
||||
datasourceName: 'test_ds',
|
||||
},
|
||||
});
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range, dataFrame });
|
||||
expect(links).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('returns no internal links when target contains empty template variables', () => {
|
||||
const { field, range, dataFrame } = setup({
|
||||
title: '',
|
||||
url: '',
|
||||
internal: {
|
||||
query: { query: 'query_1-${__data.fields.flux-dimensions}' },
|
||||
datasourceUid: 'uid_1',
|
||||
datasourceName: 'test_ds',
|
||||
},
|
||||
});
|
||||
const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_NULL_VALUE.index, range, dataFrame });
|
||||
expect(links).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
const ROW_WITH_TEXT_VALUE = { value: 'foo', index: 0 };
|
||||
const ROW_WITH_NULL_VALUE = { value: null, index: 1 };
|
||||
|
||||
function setup(link: DataLink, hasAccess = true) {
|
||||
setLinkSrv({
|
||||
getDataLinkUIModel(link: DataLink, replaceVariables: InterpolateFunction | undefined, origin: any): LinkModel<any> {
|
||||
@ -148,15 +187,19 @@ function setup(link: DataLink, hasAccess = true) {
|
||||
hasAccessToExplore: () => hasAccess,
|
||||
} as any);
|
||||
|
||||
const field: Field<string> = {
|
||||
const field: Field<string | null> = {
|
||||
name: 'flux-dimensions',
|
||||
type: FieldType.string,
|
||||
values: new ArrayVector([]),
|
||||
values: new ArrayVector([ROW_WITH_TEXT_VALUE.value, ROW_WITH_NULL_VALUE.value]),
|
||||
config: {
|
||||
links: [link],
|
||||
},
|
||||
};
|
||||
|
||||
const dataFrame: DataFrame = toDataFrame({
|
||||
fields: [field],
|
||||
});
|
||||
|
||||
const range: TimeRange = {
|
||||
from: dateTime('2020-10-14T00:00:00'),
|
||||
to: dateTime('2020-10-14T01:00:00'),
|
||||
@ -166,5 +209,5 @@ function setup(link: DataLink, hasAccess = true) {
|
||||
},
|
||||
};
|
||||
|
||||
return { range, field };
|
||||
return { range, field, dataFrame };
|
||||
}
|
||||
|
@ -10,12 +10,44 @@ import {
|
||||
DataFrame,
|
||||
getFieldDisplayValuesProxy,
|
||||
SplitOpen,
|
||||
DataLink,
|
||||
} from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
import { getLinkSrv } from '../../panel/panellinks/link_srv';
|
||||
|
||||
type DataLinkFilter = (link: DataLink, scopedVars: ScopedVars) => boolean;
|
||||
|
||||
const dataLinkHasRequiredPermissions = (link: DataLink) => {
|
||||
return !link.internal || contextSrv.hasAccessToExplore();
|
||||
};
|
||||
|
||||
const dataLinkHasAllVariablesDefined = (link: DataLink, scopedVars: ScopedVars) => {
|
||||
let hasAllRequiredVarDefined = true;
|
||||
|
||||
if (link.internal) {
|
||||
let stringifiedQuery = '';
|
||||
try {
|
||||
stringifiedQuery = JSON.stringify(link.internal.query || {});
|
||||
// Hook into format function to verify if all values are non-empty
|
||||
// Format function is run on all existing field values allowing us to check it's value is non-empty
|
||||
getTemplateSrv().replace(stringifiedQuery, scopedVars, (f: string) => {
|
||||
hasAllRequiredVarDefined = hasAllRequiredVarDefined && !!f;
|
||||
return '';
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return hasAllRequiredVarDefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fixed list of filters used in Explore. DataLinks that do not pass all the filters will not
|
||||
* be passed back to the visualization.
|
||||
*/
|
||||
const DATA_LINK_FILTERS: DataLinkFilter[] = [dataLinkHasAllVariablesDefined, dataLinkHasRequiredPermissions];
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -56,13 +88,9 @@ export const getFieldLinksForExplore = (options: {
|
||||
}
|
||||
|
||||
if (field.config.links) {
|
||||
const links = [];
|
||||
|
||||
if (!contextSrv.hasAccessToExplore()) {
|
||||
links.push(...field.config.links.filter((l) => !l.internal));
|
||||
} else {
|
||||
links.push(...field.config.links);
|
||||
}
|
||||
const links = field.config.links.filter((link) => {
|
||||
return DATA_LINK_FILTERS.every((filter) => filter(link, scopedVars));
|
||||
});
|
||||
|
||||
return links.map((link) => {
|
||||
if (!link.internal) {
|
||||
|
Loading…
Reference in New Issue
Block a user