mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Glue: Fix creating duplicated data links created by Correlations (#65490)
* Ensure correlations do not create duplicated data links * Remove correlation links before processing each correlation * Improve test coverage
This commit is contained in:
@@ -12,6 +12,15 @@ export interface DataLinkClickEvent<T = any> {
|
|||||||
e?: any; // mouse|react event
|
e?: any; // mouse|react event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Links can be created by data source plugins or correlations.
|
||||||
|
* Origin is set in DataLink object and indicates where the link was created.
|
||||||
|
*/
|
||||||
|
export enum DataLinkConfigOrigin {
|
||||||
|
Datasource = 'Datasource',
|
||||||
|
Correlations = 'Correlations',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link configuration. The values may contain variables that need to be
|
* Link configuration. The values may contain variables that need to be
|
||||||
* processed before showing the link to user.
|
* processed before showing the link to user.
|
||||||
@@ -39,6 +48,8 @@ export interface DataLink<T extends DataQuery = any> {
|
|||||||
// more custom onClick behaviour if needed.
|
// more custom onClick behaviour if needed.
|
||||||
// @internal and subject to change in future releases
|
// @internal and subject to change in future releases
|
||||||
internal?: InternalDataLink<T>;
|
internal?: InternalDataLink<T>;
|
||||||
|
|
||||||
|
origin?: DataLinkConfigOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
|||||||
@@ -5,78 +5,40 @@ import { attachCorrelationsToDataFrames } from './utils';
|
|||||||
|
|
||||||
describe('correlations utils', () => {
|
describe('correlations utils', () => {
|
||||||
it('attaches correlations defined in the configuration', () => {
|
it('attaches correlations defined in the configuration', () => {
|
||||||
const loki = { uid: 'loki-uid', name: 'loki' } as DataSourceInstanceSettings;
|
const { testDataFrames, correlations, refIdMap, prometheus, elastic } = setup();
|
||||||
const elastic = { uid: 'elastic-uid', name: 'elastic' } as DataSourceInstanceSettings;
|
|
||||||
const prometheus = { uid: 'prometheus-uid', name: 'prometheus' } as DataSourceInstanceSettings;
|
|
||||||
|
|
||||||
const refIdMap = {
|
|
||||||
'Loki Query': loki.uid,
|
|
||||||
'Elastic Query': elastic.uid,
|
|
||||||
'Prometheus Query': prometheus.uid,
|
|
||||||
};
|
|
||||||
|
|
||||||
const testDataFrames: DataFrame[] = [
|
|
||||||
toDataFrame({
|
|
||||||
name: 'Loki Logs',
|
|
||||||
refId: 'Loki Query',
|
|
||||||
fields: [
|
|
||||||
{ name: 'line', values: [] },
|
|
||||||
{ name: 'traceId', values: [] },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
toDataFrame({
|
|
||||||
name: 'Elastic Logs',
|
|
||||||
refId: 'Elastic Query',
|
|
||||||
fields: [
|
|
||||||
{ name: 'line', values: [] },
|
|
||||||
{ name: 'traceId', values: [] },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
toDataFrame({
|
|
||||||
name: 'Prometheus Metrics',
|
|
||||||
refId: 'Prometheus Query',
|
|
||||||
fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3, 4, 5] }],
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const correlations: CorrelationData[] = [
|
|
||||||
{
|
|
||||||
uid: 'loki-to-prometheus',
|
|
||||||
label: 'logs to metrics',
|
|
||||||
source: loki,
|
|
||||||
target: prometheus,
|
|
||||||
config: { type: 'query', field: 'traceId', target: { expr: 'target Prometheus query' } },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uid: 'prometheus-to-elastic',
|
|
||||||
label: 'metrics to logs',
|
|
||||||
source: prometheus,
|
|
||||||
target: elastic,
|
|
||||||
config: { type: 'query', field: 'value', target: { expr: 'target Elastic query' } },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
attachCorrelationsToDataFrames(testDataFrames, correlations, refIdMap);
|
attachCorrelationsToDataFrames(testDataFrames, correlations, refIdMap);
|
||||||
|
|
||||||
// Loki line (no links)
|
// Loki line (no links)
|
||||||
expect(testDataFrames[0].fields[0].config.links).toBeUndefined();
|
expect(testDataFrames[0].fields[0].config.links).toHaveLength(0);
|
||||||
// Loki traceId (linked to Prometheus)
|
// Loki traceId (linked to Prometheus and Elastic)
|
||||||
expect(testDataFrames[0].fields[1].config.links).toHaveLength(1);
|
expect(testDataFrames[0].fields[1].config.links).toHaveLength(2);
|
||||||
expect(testDataFrames[0].fields[1].config.links![0]).toMatchObject({
|
expect(testDataFrames[0].fields[1].config.links).toMatchObject([
|
||||||
title: 'logs to metrics',
|
{
|
||||||
internal: {
|
title: 'logs to metrics',
|
||||||
datasourceUid: prometheus.uid,
|
internal: {
|
||||||
datasourceName: prometheus.name,
|
datasourceUid: prometheus.uid,
|
||||||
query: {
|
datasourceName: prometheus.name,
|
||||||
expr: 'target Prometheus query',
|
query: {
|
||||||
|
expr: 'target Prometheus query',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
title: 'logs to logs',
|
||||||
|
internal: {
|
||||||
|
datasourceUid: elastic.uid,
|
||||||
|
datasourceName: elastic.name,
|
||||||
|
query: {
|
||||||
|
expr: 'target Elastic query',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
// Elastic line (no links)
|
// Elastic line (no links)
|
||||||
expect(testDataFrames[1].fields[0].config.links).toBeUndefined();
|
expect(testDataFrames[1].fields[0].config.links).toHaveLength(0);
|
||||||
// Elastic traceId (no links)
|
// Elastic traceId (no links)
|
||||||
expect(testDataFrames[1].fields[0].config.links).toBeUndefined();
|
expect(testDataFrames[1].fields[0].config.links).toHaveLength(0);
|
||||||
|
|
||||||
// Prometheus value (linked to Elastic)
|
// Prometheus value (linked to Elastic)
|
||||||
expect(testDataFrames[2].fields[0].config.links).toHaveLength(1);
|
expect(testDataFrames[2].fields[0].config.links).toHaveLength(1);
|
||||||
@@ -91,4 +53,80 @@ describe('correlations utils', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not create duplicates when attaching links to the same data frame', () => {
|
||||||
|
const { testDataFrames, correlations, refIdMap } = setup();
|
||||||
|
attachCorrelationsToDataFrames(testDataFrames, correlations, refIdMap);
|
||||||
|
attachCorrelationsToDataFrames(testDataFrames, correlations, refIdMap);
|
||||||
|
|
||||||
|
// Loki traceId (linked to Prometheus and Elastic)
|
||||||
|
expect(testDataFrames[0].fields[1].config.links).toHaveLength(2);
|
||||||
|
// Elastic line (no links)
|
||||||
|
expect(testDataFrames[1].fields[0].config.links).toHaveLength(0);
|
||||||
|
// Prometheus value (linked to Elastic)
|
||||||
|
expect(testDataFrames[2].fields[0].config.links).toHaveLength(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
const loki = { uid: 'loki-uid', name: 'loki' } as DataSourceInstanceSettings;
|
||||||
|
const elastic = { uid: 'elastic-uid', name: 'elastic' } as DataSourceInstanceSettings;
|
||||||
|
const prometheus = { uid: 'prometheus-uid', name: 'prometheus' } as DataSourceInstanceSettings;
|
||||||
|
|
||||||
|
const refIdMap = {
|
||||||
|
'Loki Query': loki.uid,
|
||||||
|
'Elastic Query': elastic.uid,
|
||||||
|
'Prometheus Query': prometheus.uid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const testDataFrames: DataFrame[] = [
|
||||||
|
toDataFrame({
|
||||||
|
name: 'Loki Logs',
|
||||||
|
refId: 'Loki Query',
|
||||||
|
fields: [
|
||||||
|
{ name: 'line', values: [] },
|
||||||
|
{ name: 'traceId', values: [] },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
toDataFrame({
|
||||||
|
name: 'Elastic Logs',
|
||||||
|
refId: 'Elastic Query',
|
||||||
|
fields: [
|
||||||
|
{ name: 'line', values: [] },
|
||||||
|
{ name: 'traceId', values: [] },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
toDataFrame({
|
||||||
|
name: 'Prometheus Metrics',
|
||||||
|
refId: 'Prometheus Query',
|
||||||
|
fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3, 4, 5] }],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const correlations: CorrelationData[] = [
|
||||||
|
{
|
||||||
|
uid: 'loki-to-prometheus',
|
||||||
|
label: 'logs to metrics',
|
||||||
|
source: loki,
|
||||||
|
target: prometheus,
|
||||||
|
config: { type: 'query', field: 'traceId', target: { expr: 'target Prometheus query' } },
|
||||||
|
},
|
||||||
|
// Test multiple correlations attached to the same field
|
||||||
|
{
|
||||||
|
uid: 'loki-to-elastic',
|
||||||
|
label: 'logs to logs',
|
||||||
|
source: loki,
|
||||||
|
target: elastic,
|
||||||
|
config: { type: 'query', field: 'traceId', target: { expr: 'target Elastic query' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: 'prometheus-to-elastic',
|
||||||
|
label: 'metrics to logs',
|
||||||
|
source: prometheus,
|
||||||
|
target: elastic,
|
||||||
|
config: { type: 'query', field: 'value', target: { expr: 'target Elastic query' } },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return { testDataFrames, correlations, refIdMap, prometheus, elastic };
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DataFrame } from '@grafana/data';
|
import { DataFrame, DataLinkConfigOrigin } from '@grafana/data';
|
||||||
|
|
||||||
import { CorrelationData } from './useCorrelations';
|
import { CorrelationData } from './useCorrelations';
|
||||||
|
|
||||||
@@ -31,10 +31,10 @@ export const attachCorrelationsToDataFrames = (
|
|||||||
|
|
||||||
const decorateDataFrameWithInternalDataLinks = (dataFrame: DataFrame, correlations: CorrelationData[]) => {
|
const decorateDataFrameWithInternalDataLinks = (dataFrame: DataFrame, correlations: CorrelationData[]) => {
|
||||||
dataFrame.fields.forEach((field) => {
|
dataFrame.fields.forEach((field) => {
|
||||||
|
field.config.links = field.config.links?.filter((link) => link.origin !== DataLinkConfigOrigin.Correlations) || [];
|
||||||
correlations.map((correlation) => {
|
correlations.map((correlation) => {
|
||||||
if (correlation.config?.field === field.name) {
|
if (correlation.config?.field === field.name) {
|
||||||
field.config.links = field.config.links || [];
|
field.config.links!.push({
|
||||||
field.config.links.push({
|
|
||||||
internal: {
|
internal: {
|
||||||
query: correlation.config?.target,
|
query: correlation.config?.target,
|
||||||
datasourceUid: correlation.target.uid,
|
datasourceUid: correlation.target.uid,
|
||||||
@@ -43,6 +43,7 @@ const decorateDataFrameWithInternalDataLinks = (dataFrame: DataFrame, correlatio
|
|||||||
},
|
},
|
||||||
url: '',
|
url: '',
|
||||||
title: correlation.label || correlation.target.name,
|
title: correlation.label || correlation.target.name,
|
||||||
|
origin: DataLinkConfigOrigin.Correlations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user