mirror of https://github.com/grafana/grafana
prometheushacktoberfestmetricsmonitoringalertinggrafanagoinfluxdbmysqlpostgresanalyticsdata-visualizationdashboardbusiness-intelligenceelasticsearch
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1282 lines
33 KiB
1282 lines
33 KiB
import { cloneDeep } from 'lodash';
|
|
|
|
import { DataQueryResponse, Field, FieldType, 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',
|
|
},
|
|
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',
|
|
},
|
|
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',
|
|
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',
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
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);
|
|
});
|
|
});
|
|
|
|
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',
|
|
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('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',
|
|
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',
|
|
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',
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
stats: [
|
|
{
|
|
displayName: 'Summary: total bytes processed',
|
|
unit: 'decbytes',
|
|
value: 22,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
});
|
|
});
|
|
|