diff --git a/packages/grafana-o11y-ds-frontend/src/combineResponses.test.ts b/packages/grafana-o11y-ds-frontend/src/combineResponses.test.ts index 9438c1e20f8..aa1b8ca07ad 100644 --- a/packages/grafana-o11y-ds-frontend/src/combineResponses.test.ts +++ b/packages/grafana-o11y-ds-frontend/src/combineResponses.test.ts @@ -2,6 +2,7 @@ import { DataFrame, DataFrameType, DataQueryResponse, + Field, FieldType, LoadingState, PanelData, @@ -96,6 +97,84 @@ describe('combineResponses', () => { }); }); + it('combines logs frames with transformed fields', () => { + const { logFrameA, logFrameB } = getMockFrames(); + const { logFrameB: originalLogFrameB } = getMockFrames(); + + // Pseudo shuffle fields + logFrameB.fields.sort((a: Field, b: Field) => (a.name < b.name ? -1 : 1)); + expect(logFrameB.fields).not.toEqual(originalLogFrameB.fields); + + const responseA: DataQueryResponse = { + data: [logFrameA], + }; + const responseB: DataQueryResponse = { + data: [logFrameB], + }; + expect(combineResponses(responseA, responseB)).toEqual({ + data: [ + { + fields: [ + { + config: {}, + name: 'Time', + type: 'time', + values: [1, 2, 3, 4], + }, + { + config: {}, + name: 'Line', + type: 'string', + values: ['line3', 'line4', 'line1', 'line2'], + }, + { + config: {}, + name: 'labels', + type: 'other', + values: [ + { + otherLabel: 'other value', + }, + { + label: 'value', + }, + { + otherLabel: 'other value', + }, + ], + }, + { + config: {}, + name: 'tsNs', + type: 'string', + values: ['1000000', '2000000', '3000000', '4000000'], + }, + { + config: {}, + name: 'id', + type: 'string', + values: ['id3', 'id4', 'id1', 'id2'], + }, + ], + length: 4, + meta: { + custom: { + frameType: 'LabeledTimeValues', + }, + stats: [ + { + displayName: 'Summary: total bytes processed', + unit: 'decbytes', + value: 33, + }, + ], + }, + refId: 'A', + }, + ], + }); + }); + it('combines metric frames', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const responseA: DataQueryResponse = { diff --git a/packages/grafana-o11y-ds-frontend/src/combineResponses.ts b/packages/grafana-o11y-ds-frontend/src/combineResponses.ts index f4133a7fc22..05e290663ed 100644 --- a/packages/grafana-o11y-ds-frontend/src/combineResponses.ts +++ b/packages/grafana-o11y-ds-frontend/src/combineResponses.ts @@ -57,10 +57,23 @@ export function combineResponses(currentResult: DataQueryResponse | null, newRes } function combineFrames(dest: DataFrame, source: DataFrame) { - const totalFields = dest.fields.length; + // `dest` and `source` might have more or less fields, we need to go through all of them + const totalFields = Math.max(dest.fields.length, source.fields.length); for (let i = 0; i < totalFields; i++) { - dest.fields[i].values = [].concat.apply(source.fields[i].values, dest.fields[i].values); - if (source.fields[i].nanos) { + // For now, skip undefined fields that exist in the new frame + if (!dest.fields[i]) { + continue; + } + // 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]; + if (!sourceField) { + continue; + } + dest.fields[i].values = [].concat.apply(sourceField.values, dest.fields[i].values); + if (sourceField.nanos) { const nanos: number[] = dest.fields[i].nanos?.slice() || []; dest.fields[i].nanos = source.fields[i].nanos?.concat(nanos); }