mirror of https://github.com/grafana/grafana
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>pull/56712/head
parent
95b9fa3346
commit
ae927eab73
@ -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', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -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, |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
Loading…
Reference in new issue