Combine responses: add support for frames with repeated field names (#90650)

* Combine responses: add support for frames with repeated field names

* Formatting
This commit is contained in:
Matias Chomicki
2024-07-19 14:26:06 +00:00
committed by GitHub
parent 892d5d1b20
commit 66fb4d5f1a
2 changed files with 156 additions and 3 deletions
@@ -525,6 +525,147 @@ describe('combineResponses', () => {
data: [metricFrameA, metricFrameB],
});
});
it('when fields with the same name are present, uses labels to find the right field to combine', () => {
const { metricFrameA, metricFrameB } = getMockFrames();
metricFrameA.fields.push({
name: 'Value',
type: FieldType.number,
config: {},
values: [9, 8],
labels: {
test: 'true',
},
});
metricFrameB.fields.push({
name: 'Value',
type: FieldType.number,
config: {},
values: [11, 10],
labels: {
test: 'true',
},
});
const responseA: DataQueryResponse = {
data: [metricFrameA],
};
const responseB: DataQueryResponse = {
data: [metricFrameB],
};
expect(combineResponses(responseA, responseB)).toEqual({
data: [
{
fields: [
{
config: {},
name: 'Time',
type: 'time',
values: [1000000, 2000000, 3000000, 4000000],
},
{
config: {},
name: 'Value',
type: 'number',
values: [6, 7, 5, 4],
labels: {
level: 'debug',
},
},
{
config: {},
name: 'Value',
type: 'number',
values: [11, 10, 9, 8],
labels: {
test: 'true',
},
},
],
length: 4,
meta: {
type: 'timeseries-multi',
stats: [
{
displayName: 'Summary: total bytes processed',
unit: 'decbytes',
value: 33,
},
],
},
refId: 'A',
},
],
});
});
it('when fields with the same name are present and labels are not present, falls back to indexes', () => {
const { metricFrameA, metricFrameB } = getMockFrames();
delete metricFrameA.fields[1].labels;
delete metricFrameB.fields[1].labels;
metricFrameA.fields.push({
name: 'Value',
type: FieldType.number,
config: {},
values: [9, 8],
});
metricFrameB.fields.push({
name: 'Value',
type: FieldType.number,
config: {},
values: [11, 10],
});
const responseA: DataQueryResponse = {
data: [metricFrameA],
};
const responseB: DataQueryResponse = {
data: [metricFrameB],
};
expect(combineResponses(responseA, responseB)).toEqual({
data: [
{
fields: [
{
config: {},
name: 'Time',
type: 'time',
values: [1000000, 2000000, 3000000, 4000000],
},
{
config: {},
name: 'Value',
type: 'number',
values: [6, 7, 5, 4],
},
{
config: {},
name: 'Value',
type: 'number',
values: [11, 10, 9, 8],
},
],
length: 4,
meta: {
type: 'timeseries-multi',
stats: [
{
displayName: 'Summary: total bytes processed',
unit: 'decbytes',
value: 33,
},
],
},
refId: 'A',
},
],
});
});
});
describe('combinePanelData', () => {
@@ -66,9 +66,7 @@ function combineFrames(dest: DataFrame, source: DataFrame) {
}
// Index is not reliable when frames have disordered fields, or an extra/missing field, so we find them by name.
// If the field has no name, we fallback to the old index version.
const sourceField = dest.fields[i].name
? source.fields.find((f) => f.name === dest.fields[i].name)
: source.fields[i];
const sourceField = findSourceField(dest.fields[i], source.fields, i);
if (!sourceField) {
continue;
}
@@ -85,6 +83,20 @@ function combineFrames(dest: DataFrame, source: DataFrame) {
};
}
function findSourceField(referenceField: Field, sourceFields: Field[], index: number) {
const candidates = sourceFields.filter((f) => f.name === referenceField.name);
if (candidates.length === 1) {
return candidates[0];
}
if (referenceField.labels) {
return candidates.find((candidate) => shallowCompare(referenceField.labels ?? {}, candidate.labels ?? {}));
}
return sourceFields[index];
}
const TOTAL_BYTES_STAT = 'Summary: total bytes processed';
// This is specific for Loki
function getCombinedMetadataStats(