mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Add click tracking to data links (#65221)
* Add click tracking to data links * Use constants, mock window.open call * Add comment about possibly missing reporting data * Remove superfluous if
This commit is contained in:
parent
940768cf76
commit
55947cee8f
@ -1,7 +1,9 @@
|
||||
import {
|
||||
ArrayVector,
|
||||
CoreApp,
|
||||
DataFrame,
|
||||
DataLink,
|
||||
DataLinkConfigOrigin,
|
||||
dateTime,
|
||||
Field,
|
||||
FieldType,
|
||||
@ -10,7 +12,7 @@ import {
|
||||
TimeRange,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { setTemplateSrv } from '@grafana/runtime';
|
||||
import { setTemplateSrv, reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import { initTemplateSrv } from '../../../../test/helpers/initTemplateSrv';
|
||||
import { ContextSrv, setContextSrv } from '../../../core/services/context_srv';
|
||||
@ -18,6 +20,11 @@ import { setLinkSrv } from '../../panel/panellinks/link_srv';
|
||||
|
||||
import { getFieldLinksForExplore, getVariableUsageInfo } from './links';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
reportInteraction: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('explore links utils', () => {
|
||||
describe('getFieldLinksForExplore', () => {
|
||||
beforeEach(() => {
|
||||
@ -28,6 +35,12 @@ describe('explore links utils', () => {
|
||||
{ type: 'custom', name: 'test', current: { value: 'foo' } },
|
||||
])
|
||||
);
|
||||
|
||||
jest.spyOn(window, 'open').mockImplementation();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('returns correct link model for external link', () => {
|
||||
@ -44,6 +57,15 @@ describe('explore links utils', () => {
|
||||
|
||||
expect(links[0].href).toBe('http://regionalhost');
|
||||
expect(links[0].title).toBe('external');
|
||||
expect(links[0].onClick).toBeDefined();
|
||||
|
||||
links[0].onClick!({});
|
||||
|
||||
expect(reportInteraction).toBeCalledWith('grafana_data_link_clicked', {
|
||||
app: CoreApp.Explore,
|
||||
internal: false,
|
||||
origin: DataLinkConfigOrigin.Datasource,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns generates title for external link', () => {
|
||||
@ -106,6 +128,12 @@ describe('explore links utils', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(reportInteraction).toBeCalledWith('grafana_data_link_clicked', {
|
||||
app: CoreApp.Explore,
|
||||
internal: true,
|
||||
origin: DataLinkConfigOrigin.Datasource,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns correct link model for external link when user does not have access to explore', () => {
|
||||
@ -251,6 +279,7 @@ describe('explore links utils', () => {
|
||||
const transformationLink: DataLink = {
|
||||
title: '',
|
||||
url: '',
|
||||
origin: DataLinkConfigOrigin.Correlations,
|
||||
internal: {
|
||||
query: { query: 'http_requests{app=${application} env=${environment}}' },
|
||||
datasourceUid: 'uid_1',
|
||||
@ -281,6 +310,17 @@ describe('explore links utils', () => {
|
||||
'{"range":{"from":"now-1h","to":"now"},"datasource":"uid_1","queries":[{"query":"http_requests{app=foo env=dev}"}]}'
|
||||
)}`
|
||||
);
|
||||
|
||||
if (links[0][0].onClick) {
|
||||
links[0][0].onClick({});
|
||||
}
|
||||
|
||||
expect(reportInteraction).toBeCalledWith('grafana_data_link_clicked', {
|
||||
app: CoreApp.Explore,
|
||||
internal: true,
|
||||
origin: DataLinkConfigOrigin.Correlations,
|
||||
});
|
||||
|
||||
expect(links[1]).toHaveLength(1);
|
||||
expect(links[1][0].href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
|
@ -12,8 +12,12 @@ import {
|
||||
SplitOpen,
|
||||
DataLink,
|
||||
DisplayValue,
|
||||
DataLinkConfigOrigin,
|
||||
CoreApp,
|
||||
SplitOpenOptions,
|
||||
} from '@grafana/data';
|
||||
import { getTemplateSrv, VariableInterpolation } from '@grafana/runtime';
|
||||
import { getTemplateSrv, reportInteraction, VariableInterpolation } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { getTransformationVars } from 'app/features/correlations/transformations';
|
||||
|
||||
@ -42,6 +46,8 @@ export interface ExploreFieldLinkModel extends LinkModel<Field> {
|
||||
variables?: VariableInterpolation[];
|
||||
}
|
||||
|
||||
const DATA_LINK_USAGE_KEY = 'grafana_data_link_clicked';
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -115,6 +121,30 @@ export const getFieldLinksForExplore = (options: {
|
||||
if (!linkModel.title) {
|
||||
linkModel.title = getTitleFromHref(linkModel.href);
|
||||
}
|
||||
|
||||
// Take over the onClick to report the click, then either call the original onClick or navigate to the URL
|
||||
// Note: it is likely that an external link that opens in the same tab will not be reported, as the browser redirect might cancel reporting the interaction
|
||||
const origOnClick = linkModel.onClick;
|
||||
|
||||
linkModel.onClick = (...args) => {
|
||||
reportInteraction(DATA_LINK_USAGE_KEY, {
|
||||
origin: link.origin || DataLinkConfigOrigin.Datasource,
|
||||
app: CoreApp.Explore,
|
||||
internal: false,
|
||||
});
|
||||
|
||||
if (origOnClick) {
|
||||
origOnClick?.apply(...args);
|
||||
} else {
|
||||
// for external links without an onClick, we want to duplicate default href behavior since onClick stops it
|
||||
if (linkModel.target === '_blank') {
|
||||
window.open(linkModel.href);
|
||||
} else {
|
||||
window.location.href = linkModel.href;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return linkModel;
|
||||
} else {
|
||||
let internalLinkSpecificVars: ScopedVars = {};
|
||||
@ -147,6 +177,16 @@ export const getFieldLinksForExplore = (options: {
|
||||
variables = variableData.variables;
|
||||
}
|
||||
|
||||
const splitFnWithTracking = (options?: SplitOpenOptions<DataQuery>) => {
|
||||
reportInteraction(DATA_LINK_USAGE_KEY, {
|
||||
origin: link.origin || DataLinkConfigOrigin.Datasource,
|
||||
app: CoreApp.Explore,
|
||||
internal: true,
|
||||
});
|
||||
|
||||
splitOpenFn?.(options);
|
||||
};
|
||||
|
||||
if (variableData.allVariablesDefined) {
|
||||
const internalLink = mapInternalLinkToExplore({
|
||||
link,
|
||||
@ -154,7 +194,7 @@ export const getFieldLinksForExplore = (options: {
|
||||
scopedVars: allVars,
|
||||
range,
|
||||
field,
|
||||
onClickFn: splitOpenFn,
|
||||
onClickFn: (options) => splitFnWithTracking(options),
|
||||
replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()),
|
||||
});
|
||||
return { ...internalLink, variables: variables };
|
||||
|
Loading…
Reference in New Issue
Block a user