/usr/share/grafana/public/app/plugins/datasource/loki
import { cloneDeep } from 'lodash'; import { DataQueryResponse, Field, FieldType, QueryResultMetaNotice, QueryResultMetaStat } from '@grafana/data'; import { cloneQueryResponse, combineResponses } from './mergeResponses'; import { getMockFrames } from './mocks/frames'; describe('cloneQueryResponse', () => { const { logFrameA } = getMockFrames(); const responseA: DataQueryResponse = { data: [logFrameA], }; it('clones query responses', () => { const clonedA = cloneQueryResponse(responseA); expect(clonedA).not.toBe(responseA); expect(clonedA).toEqual(clonedA); }); }); describe('combineResponses', () => { it('combines logs frames', () => { const { logFrameA, logFrameB } = getMockFrames(); 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', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); 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', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('combines metric frames', () => { const { metricFrameA, metricFrameB } = getMockFrames(); 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', }, }, ], length: 4, meta: { type: 'timeseries-multi', notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('combines and identifies new frames in the response', () => { const { metricFrameA, metricFrameB, metricFrameC } = getMockFrames(); const responseA: DataQueryResponse = { data: [metricFrameA], }; const responseB: DataQueryResponse = { data: [metricFrameB, metricFrameC], }; 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', }, }, ], length: 4, meta: { type: 'timeseries-multi', notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, metricFrameC, ], }); }); it('combines frames prioritizing refIds over names', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const dataFrameA = { ...metricFrameA, refId: 'A', name: 'A', }; const dataFrameB = { ...metricFrameB, refId: 'B', name: 'A', }; const responseA: DataQueryResponse = { data: [dataFrameA], }; const responseB: DataQueryResponse = { data: [dataFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [dataFrameA, dataFrameB], }); }); it('combines frames in a new response instance', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const responseA: DataQueryResponse = { data: [metricFrameA], }; const responseB: DataQueryResponse = { data: [metricFrameB], }; expect(combineResponses(null, responseA)).not.toBe(responseA); expect(combineResponses(null, responseB)).not.toBe(responseB); }); it('combine when first param has errors', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const errorA = { message: 'errorA', }; const responseA: DataQueryResponse = { data: [metricFrameA], error: errorA, errors: [errorA], }; const responseB: DataQueryResponse = { data: [metricFrameB], }; const combined = combineResponses(responseA, responseB); expect(combined.data[0].length).toBe(4); expect(combined.error?.message).toBe('errorA'); expect(combined.errors).toHaveLength(1); expect(combined.errors?.[0]?.message).toBe('errorA'); }); it('combine when second param has errors', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const responseA: DataQueryResponse = { data: [metricFrameA], }; const errorB = { message: 'errorB', }; const responseB: DataQueryResponse = { data: [metricFrameB], error: errorB, errors: [errorB], }; const combined = combineResponses(responseA, responseB); expect(combined.data[0].length).toBe(4); expect(combined.error?.message).toBe('errorB'); expect(combined.errors).toHaveLength(1); expect(combined.errors?.[0]?.message).toBe('errorB'); }); it('combine when both frames have errors', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const errorA = { message: 'errorA', }; const errorB = { message: 'errorB', }; const responseA: DataQueryResponse = { data: [metricFrameA], error: errorA, errors: [errorA], }; const responseB: DataQueryResponse = { data: [metricFrameB], error: errorB, errors: [errorB], }; const combined = combineResponses(responseA, responseB); expect(combined.data[0].length).toBe(4); expect(combined.error?.message).toBe('errorA'); expect(combined.errors).toHaveLength(2); expect(combined.errors?.[0]?.message).toBe('errorA'); expect(combined.errors?.[1]?.message).toBe('errorB'); }); it('combines frames with nanoseconds', () => { const { logFrameA, logFrameB } = getMockFrames(); logFrameA.fields[0].nanos = [333333, 444444]; logFrameB.fields[0].nanos = [111111, 222222]; 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], nanos: [111111, 222222, 333333, 444444], }, { 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', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('combines frames without nanoseconds with frames with nanoseconds', () => { const { logFrameA, logFrameB } = getMockFrames(); logFrameA.fields[0].nanos = undefined; logFrameB.fields[0].nanos = [111111, 222222]; 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], nanos: [111111, 222222, 0, 0], }, { 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', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('combines frames with nanoseconds with frames without nanoseconds', () => { const { logFrameA, logFrameB } = getMockFrames(); logFrameA.fields[0].nanos = [111111, 222222]; logFrameB.fields[0].nanos = undefined; 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], nanos: [0, 0, 111111, 222222], }, { 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', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); describe('combine stats', () => { const { metricFrameA } = getMockFrames(); const makeResponse = (stats?: QueryResultMetaStat[]): DataQueryResponse => ({ data: [ { ...metricFrameA, meta: { ...metricFrameA.meta, stats, }, }, ], }); it('two values', () => { const responseA = makeResponse([ { displayName: 'Ingester: total reached', value: 1 }, { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 11 }, ]); const responseB = makeResponse([ { displayName: 'Ingester: total reached', value: 2 }, { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 22 }, ]); expect(combineResponses(responseA, responseB).data[0].meta.stats).toStrictEqual([ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33 }, ]); }); it('one value', () => { const responseA = makeResponse([ { displayName: 'Ingester: total reached', value: 1 }, { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 11 }, ]); const responseB = makeResponse(); expect(combineResponses(responseA, responseB).data[0].meta.stats).toStrictEqual([ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 11 }, ]); expect(combineResponses(responseB, responseA).data[0].meta.stats).toStrictEqual([ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 11 }, ]); }); it('no value', () => { const responseA = makeResponse(); const responseB = makeResponse(); expect(combineResponses(responseA, responseB).data[0].meta.stats).toHaveLength(0); }); }); describe('combine notices', () => { const { metricFrameA } = getMockFrames(); const makeResponse = (notices?: QueryResultMetaNotice[]): DataQueryResponse => ({ data: [ { ...metricFrameA, meta: { ...metricFrameA.meta, notices, }, }, ], }); it('combines notices from both frames', () => { const responseA = makeResponse([{ severity: 'warning', text: 'Warning from frame A' }]); const responseB = makeResponse([{ severity: 'info', text: 'Info from frame B' }]); expect(combineResponses(responseA, responseB).data[0].meta?.notices).toStrictEqual([ { severity: 'warning', text: 'Warning from frame A' }, { severity: 'info', text: 'Info from frame B' }, ]); }); it('deduplicates identical notices', () => { const responseA = makeResponse([{ severity: 'warning', text: 'Same warning' }]); const responseB = makeResponse([{ severity: 'warning', text: 'Same warning' }]); expect(combineResponses(responseA, responseB).data[0].meta?.notices).toStrictEqual([ { severity: 'warning', text: 'Same warning' }, ]); }); it('keeps notices with same text but different severity', () => { const responseA = makeResponse([{ severity: 'warning', text: 'Message' }]); const responseB = makeResponse([{ severity: 'info', text: 'Message' }]); expect(combineResponses(responseA, responseB).data[0].meta?.notices).toStrictEqual([ { severity: 'warning', text: 'Message' }, { severity: 'info', text: 'Message' }, ]); }); it('handles one frame with notices and one without', () => { const responseA = makeResponse([{ severity: 'warning', text: 'Warning message' }]); const responseB = makeResponse(); expect(combineResponses(responseA, responseB).data[0].meta?.notices).toStrictEqual([ { severity: 'warning', text: 'Warning message' }, ]); expect(combineResponses(responseB, responseA).data[0].meta?.notices).toStrictEqual([ { severity: 'warning', text: 'Warning message' }, ]); }); it('returns empty array when neither frame has notices', () => { const responseA = makeResponse(); const responseB = makeResponse(); expect(combineResponses(responseA, responseB).data[0].meta?.notices).toHaveLength(0); }); it('filters out null values from notices arrays', () => { const responseA = makeResponse([ { severity: 'warning', text: 'Valid warning' }, null as unknown as QueryResultMetaNotice, // Simulating the bug scenario ]); const responseB = makeResponse([{ severity: 'info', text: 'Valid info' }]); const result = combineResponses(responseA, responseB).data[0].meta?.notices; expect(result).toStrictEqual([ { severity: 'warning', text: 'Valid warning' }, { severity: 'info', text: 'Valid info' }, ]); expect(result).not.toContainEqual(null); }); }); it('does not combine frames with different refId', () => { const { metricFrameA, metricFrameB } = getMockFrames(); metricFrameA.refId = 'A'; metricFrameB.refId = 'B'; const responseA: DataQueryResponse = { data: [metricFrameA], }; const responseB: DataQueryResponse = { data: [metricFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [metricFrameA, metricFrameB], }); }); it('does not combine frames with different refId', () => { const { metricFrameA, metricFrameB } = getMockFrames(); metricFrameA.name = 'A'; metricFrameB.name = 'B'; const responseA: DataQueryResponse = { data: [metricFrameA], }; const responseB: DataQueryResponse = { data: [metricFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ 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', notices: [], 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', notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); }); describe('mergeFrames', () => { it('combines metric frames', () => { const { metricFrameA, metricFrameB } = getMockFrames(); const responseA: DataQueryResponse = { data: [metricFrameB], }; const responseB: DataQueryResponse = { data: [metricFrameA], }; 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', }, }, ], length: 4, meta: { type: 'timeseries-multi', notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('adds old to new values when combining', () => { const { metricFrameA, metricFrameB } = getMockFrames(); metricFrameB.fields[0].values = [3000000, 3500000, 4000000]; metricFrameB.fields[1].values = [5, 10, 6]; const responseA: DataQueryResponse = { data: [metricFrameA], }; const responseB: DataQueryResponse = { data: [metricFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { fields: [ { config: {}, name: 'Time', type: 'time', values: [3000000, 3500000, 4000000], }, { config: {}, name: 'Value', type: 'number', values: [10, 10, 10], labels: { level: 'debug', }, }, ], length: 3, meta: { type: 'timeseries-multi', notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('combines and identifies new frames in the response', () => { const { metricFrameA, metricFrameB, metricFrameC } = getMockFrames(); const responseA: DataQueryResponse = { data: [metricFrameB], }; const responseB: DataQueryResponse = { data: [metricFrameA, metricFrameC], }; 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', }, }, ], length: 4, meta: { type: 'timeseries-multi', notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, metricFrameC, ], }); }); it('merges logs frames', () => { const { logFrameA, logFrameB } = getMockFrames(); // 3 overlaps with logFrameA logFrameB.fields[0].values = [2, 3]; logFrameB.fields[1].values = ['line4', 'line1']; logFrameB.fields[4].values = ['id4', 'id1']; const responseA: DataQueryResponse = { data: [logFrameA], }; const responseB: DataQueryResponse = { data: [logFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { fields: [ { config: {}, name: 'Time', type: 'time', values: [2, 3, 4], }, { config: {}, name: 'Line', type: 'string', values: ['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', '4000000'], }, { config: {}, name: 'id', type: 'string', values: ['id4', 'id1', 'id2'], }, ], length: 3, meta: { custom: { frameType: 'LabeledTimeValues', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('merges frames with nanoseconds', () => { const { logFrameA, logFrameB } = getMockFrames(); logFrameA.fields[0].values = [3, 4]; logFrameA.fields[0].nanos = [333333, 444444]; // 3 overlaps with logFrameA logFrameB.fields[0].values = [2, 3]; logFrameB.fields[0].nanos = [222222, 333333]; logFrameB.fields[4].values = ['id4', 'id1']; const responseA: DataQueryResponse = { data: [logFrameA], }; const responseB: DataQueryResponse = { data: [logFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { fields: [ { config: {}, name: 'Time', type: 'time', values: [2, 3, 4], nanos: [222222, 333333, 444444], }, { config: {}, name: 'Line', type: 'string', values: ['line3', 'line4', 'line2'], }, { config: {}, name: 'labels', type: 'other', values: [ { otherLabel: 'other value', }, { label: 'value', }, { otherLabel: 'other value', }, ], }, { config: {}, name: 'tsNs', type: 'string', values: ['1000000', '2000000', '4000000'], }, { config: {}, name: 'id', type: 'string', values: ['id4', 'id1', 'id2'], }, ], length: 3, meta: { custom: { frameType: 'LabeledTimeValues', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('receiving existing values do not introduce inconsistencies', () => { const { logFrameA, logFrameAB } = getMockFrames(); const responseA: DataQueryResponse = { data: [cloneDeep(logFrameAB)], }; const responseB: DataQueryResponse = { data: [cloneDeep(logFrameA)], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { ...logFrameAB, meta: { custom: { frameType: 'LabeledTimeValues', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, }, ], }); }); it('considers nanoseconds to merge the frames', () => { const { logFrameA, logFrameB } = getMockFrames(); // Same timestamps but different nanos logFrameA.fields[0].values = [1, 2]; logFrameA.fields[0].nanos = [333333, 444444]; logFrameB.fields[0].values = [1, 2]; logFrameB.fields[0].nanos = [222222, 333333]; const responseA: DataQueryResponse = { data: [logFrameA], }; const responseB: DataQueryResponse = { data: [logFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { fields: [ { config: {}, name: 'Time', type: 'time', values: [1, 1, 2, 2], nanos: [222222, 333333, 333333, 444444], }, { config: {}, name: 'Line', type: 'string', values: ['line3', 'line1', 'line4', 'line2'], }, { config: {}, name: 'labels', type: 'other', values: [ { otherLabel: 'other value', }, { label: 'value', }, { otherLabel: 'other value', }, ], }, { config: {}, name: 'tsNs', type: 'string', values: ['1000000', '3000000', '2000000', '4000000'], }, { config: {}, name: 'id', type: 'string', values: ['id3', 'id1', 'id4', 'id2'], }, ], length: 4, meta: { custom: { frameType: 'LabeledTimeValues', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 33, }, ], }, refId: 'A', }, ], }); }); it('correctly handles empty responses', () => { const { emptyFrame, logFrameB } = getMockFrames(); logFrameB.fields[0].values = [1, 2]; logFrameB.fields[0].nanos = [222222, 333333]; const responseA: DataQueryResponse = { data: [emptyFrame], }; const responseB: DataQueryResponse = { data: [logFrameB], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { ...logFrameB, meta: { custom: { frameType: 'LabeledTimeValues', }, notices: [], stats: [{ displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 22 }], }, length: 2, }, ], }); }); it('merging exactly the same data produces the same data', () => { const { logFrameA } = getMockFrames(); const responseA: DataQueryResponse = { data: [cloneDeep(logFrameA)], }; const responseB: DataQueryResponse = { data: [cloneDeep(logFrameA)], }; expect(combineResponses(responseA, responseB)).toEqual({ data: [ { ...logFrameA, meta: { custom: { frameType: 'LabeledTimeValues', }, notices: [], stats: [ { displayName: 'Summary: total bytes processed', unit: 'decbytes', value: 22, }, ], }, }, ], }); }); });
.
Edit
..
Edit
CHANGELOG.md
Edit
LanguageProvider.test.ts
Edit
LanguageProvider.ts
Edit
LiveStreams.test.ts
Edit
LiveStreams.ts
Edit
LogContextProvider.test.ts
Edit
LogContextProvider.ts
Edit
LokiVariableSupport.test.ts
Edit
LokiVariableSupport.ts
Edit
README.md
Edit
backendResultTransformer.test.ts
Edit
backendResultTransformer.ts
Edit
components
Edit
configuration
Edit
dataquery.cue
Edit
dataquery.gen.ts
Edit
datasource.test.ts
Edit
datasource.ts
Edit
dist
Edit
docs
Edit
getDerivedFields.test.ts
Edit
getDerivedFields.ts
Edit
img
Edit
jest-setup.js
Edit
jest.config.js
Edit
languageUtils.test.ts
Edit
languageUtils.ts
Edit
language_utils.test.ts
Edit
lineParser.test.ts
Edit
lineParser.ts
Edit
liveStreamsResultTransformer.test.ts
Edit
liveStreamsResultTransformer.ts
Edit
logsTimeSplitting.test.ts
Edit
logsTimeSplitting.ts
Edit
makeTableFrames.test.ts
Edit
makeTableFrames.ts
Edit
mergeResponses.test.ts
Edit
mergeResponses.ts
Edit
metricTimeSplitting.test.ts
Edit
metricTimeSplitting.ts
Edit
migrations
Edit
mocks
Edit
modifyQuery.test.ts
Edit
modifyQuery.ts
Edit
module.test.ts
Edit
module.ts
Edit
package.json
Edit
plugin.json
Edit
project.json
Edit
queryHints.test.ts
Edit
queryHints.ts
Edit
querySplitting.test.ts
Edit
querySplitting.ts
Edit
queryUtils.test.ts
Edit
queryUtils.ts
Edit
querybuilder
Edit
responseUtils.test.ts
Edit
responseUtils.ts
Edit
shardQuerySplitting.test.ts
Edit
shardQuerySplitting.ts
Edit
sortDataFrame.test.ts
Edit
sortDataFrame.ts
Edit
streaming.test.ts
Edit
streaming.ts
Edit
syntax.test.ts
Edit
syntax.ts
Edit
tracking.test.ts
Edit
tracking.ts
Edit
tsconfig.json
Edit
types.ts
Edit
webpack.config.ts
Edit