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:
Piotr Jamróz
2023-03-29 13:09:27 +02:00
committed by GitHub
parent 7bbe255150
commit 383148bcd1
3 changed files with 117 additions and 67 deletions

View File

@@ -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 */

View File

@@ -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 };
}

View File

@@ -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,
}); });
} }
}); });