Glue: Enrich query results data frames in Explore with correlations to generate static links from correlations (#56295)

* Attach static generic data link to data frames in Explore

* WIP

* Always load correlations config when the query is run

This will be moved to Wrapper.tsx and called only once Explore is mounted

* remove comment

* Load the config when Explore is loaded

* Clean up

* Check for feature toggle, simplify cod

* Simplify the code

* Remove unused code

* Fix types

* Add a test for attaching links

* Revert package.json changes

* Display title provided in the correlation label

* Add missing mocks

* Fix tests

* Merge branch 'main' into ifrost/integration/attach-generic-data-link

# Conflicts:
#	public/app/features/explore/Wrapper.tsx
#	public/app/features/explore/state/main.ts

* Remove redundant async calls

* Do not block Wrapper before correlations are loaded (only delay the query)

* Test showing results after correlations are loaded

* Post-merge fix

* Use more consistent naming

* Avoid null assertions

Co-authored-by: Elfo404 <me@giordanoricci.com>
This commit is contained in:
Piotr Jamróz
2022-10-11 14:40:55 +02:00
committed by GitHub
co-authored by Elfo404
parent 95b9fa3346
commit ae927eab73
14 changed files with 278 additions and 38 deletions
@@ -0,0 +1,94 @@
import { DataFrame, DataSourceInstanceSettings, FieldType, toDataFrame } from '@grafana/data';
import { CorrelationData } from './useCorrelations';
import { attachCorrelationsToDataFrames } from './utils';
describe('correlations utils', () => {
it('attaches correlations defined in the configuration', () => {
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' } },
},
{
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);
// Loki line (no links)
expect(testDataFrames[0].fields[0].config.links).toBeUndefined();
// Loki traceId (linked to Prometheus)
expect(testDataFrames[0].fields[1].config.links).toHaveLength(1);
expect(testDataFrames[0].fields[1].config.links![0]).toMatchObject({
title: 'logs to metrics',
internal: {
datasourceUid: prometheus.uid,
datasourceName: prometheus.name,
query: {
expr: 'target Prometheus query',
},
},
});
// Elastic line (no links)
expect(testDataFrames[1].fields[0].config.links).toBeUndefined();
// Elastic traceId (no links)
expect(testDataFrames[1].fields[0].config.links).toBeUndefined();
// Prometheus value (linked to Elastic)
expect(testDataFrames[2].fields[0].config.links).toHaveLength(1);
expect(testDataFrames[2].fields[0].config.links![0]).toMatchObject({
title: 'metrics to logs',
internal: {
datasourceUid: elastic.uid,
datasourceName: elastic.name,
query: {
expr: 'target Elastic query',
},
},
});
});
});
+49
View File
@@ -0,0 +1,49 @@
import { DataFrame } from '@grafana/data';
import { CorrelationData } from './useCorrelations';
type DataFrameRefIdToDataSourceUid = Record<string, string>;
/**
* Creates data links from provided CorrelationData object
*
* @param dataFrames list of data frames to be processed
* @param correlations list of of possible correlations that can be applied
* @param dataFrameRefIdToDataSourceUid a map that for provided refId references corresponding data source ui
*/
export const attachCorrelationsToDataFrames = (
dataFrames: DataFrame[],
correlations: CorrelationData[],
dataFrameRefIdToDataSourceUid: DataFrameRefIdToDataSourceUid
): DataFrame[] => {
dataFrames.forEach((dataFrame) => {
const frameRefId = dataFrame.refId;
if (!frameRefId) {
return;
}
const dataSourceUid = dataFrameRefIdToDataSourceUid[frameRefId];
const sourceCorrelations = correlations.filter((correlation) => correlation.source.uid === dataSourceUid);
decorateDataFrameWithInternalDataLinks(dataFrame, sourceCorrelations);
});
return dataFrames;
};
const decorateDataFrameWithInternalDataLinks = (dataFrame: DataFrame, correlations: CorrelationData[]) => {
dataFrame.fields.forEach((field) => {
correlations.map((correlation) => {
if (correlation.config?.field === field.name) {
field.config.links = field.config.links || [];
field.config.links.push({
internal: {
query: correlation.config?.target,
datasourceUid: correlation.target.uid,
datasourceName: correlation.target.name,
},
url: '',
title: correlation.label || correlation.target.name,
});
}
});
});
};