The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
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.
grafana/public/app/plugins/datasource/prometheus/result_transformer.test.ts

1075 lines
34 KiB

import {
cacheFieldDisplayNames,
createDataFrame,
DataQueryRequest,
DataQueryResponse,
FieldType,
PreferredVisualisationType,
} from '@grafana/data';
import { parseSampleValue, sortSeriesByLabel, transformDFToTable, transformV2 } from './result_transformer';
import { PromQuery } from './types';
Prometheus: Add support for Exemplars (#28057) * Fix typos * Query exemplars API * Add link to traceID * Update exemplar to show more information Reduce exemplars density * Fix typos * Query exemplars API * Add link to traceID * Update exemplar to show more information Reduce exemplars density * Update GraphNG legend type * Show new graph component in Explore * Add exemplar annotation a design update * Graph panel not to show red line annotation Exemplar plugin to use y value * Address review comments * Density filter for exemplars * Update schema of exemplars * Density filter with y-value sampling * Enforce axis scales to include 0 * Changes after merge with master * Show metrics when there is no result * Decorators tests fix * ExemplarMarker to receive component prop * Remove context menu from explore graph * Add color to graph * Update explore graph panel * Update graph config to use default values * Fix data source tests * Do not show exemplars outside of graph * Add exemplars switch * Fix typos * Add exemplars query only when enabled * Show graph in explore without filling it * Update exemplars plugin y value scale selection * Update tests * Add data source picker for internal linking * Increase pointSize for better visibility * Fix explore e2e test * Fix data link title variable interpolation * Use new switch component in PromExemplarField * Move FieldLink component to new file * Convert exemplar to datalink * Add legend toggling logic to Explore * Add legend toggling to Explore * Address Ivana's feedback * Address Andrej's comments * Address Gio's feedback * Add tests for result_transformer * Fix eslint issues * Change sampler formula for better readability Co-authored-by: David Kaltschmidt <david@leia.lan> Co-authored-by: David Kaltschmidt <david@leia.fritz.box> Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com>
5 years ago
jest.mock('@grafana/runtime', () => ({
getTemplateSrv: () => ({
replace: (str: string) => str,
}),
getDataSourceSrv: () => {
return {
getInstanceSettings: (uid: string) => {
const uids = ['Tempo', 'jaeger'];
return uids.find((u) => u === uid) ? { name: uid } : undefined;
Prometheus: Add support for Exemplars (#28057) * Fix typos * Query exemplars API * Add link to traceID * Update exemplar to show more information Reduce exemplars density * Fix typos * Query exemplars API * Add link to traceID * Update exemplar to show more information Reduce exemplars density * Update GraphNG legend type * Show new graph component in Explore * Add exemplar annotation a design update * Graph panel not to show red line annotation Exemplar plugin to use y value * Address review comments * Density filter for exemplars * Update schema of exemplars * Density filter with y-value sampling * Enforce axis scales to include 0 * Changes after merge with master * Show metrics when there is no result * Decorators tests fix * ExemplarMarker to receive component prop * Remove context menu from explore graph * Add color to graph * Update explore graph panel * Update graph config to use default values * Fix data source tests * Do not show exemplars outside of graph * Add exemplars switch * Fix typos * Add exemplars query only when enabled * Show graph in explore without filling it * Update exemplars plugin y value scale selection * Update tests * Add data source picker for internal linking * Increase pointSize for better visibility * Fix explore e2e test * Fix data link title variable interpolation * Use new switch component in PromExemplarField * Move FieldLink component to new file * Convert exemplar to datalink * Add legend toggling logic to Explore * Add legend toggling to Explore * Address Ivana's feedback * Address Andrej's comments * Address Gio's feedback * Add tests for result_transformer * Fix eslint issues * Change sampler formula for better readability Co-authored-by: David Kaltschmidt <david@leia.lan> Co-authored-by: David Kaltschmidt <david@leia.fritz.box> Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com>
5 years ago
},
};
},
config: {
featureToggles: {
prometheusDataplane: true,
},
},
Prometheus: Add support for Exemplars (#28057) * Fix typos * Query exemplars API * Add link to traceID * Update exemplar to show more information Reduce exemplars density * Fix typos * Query exemplars API * Add link to traceID * Update exemplar to show more information Reduce exemplars density * Update GraphNG legend type * Show new graph component in Explore * Add exemplar annotation a design update * Graph panel not to show red line annotation Exemplar plugin to use y value * Address review comments * Density filter for exemplars * Update schema of exemplars * Density filter with y-value sampling * Enforce axis scales to include 0 * Changes after merge with master * Show metrics when there is no result * Decorators tests fix * ExemplarMarker to receive component prop * Remove context menu from explore graph * Add color to graph * Update explore graph panel * Update graph config to use default values * Fix data source tests * Do not show exemplars outside of graph * Add exemplars switch * Fix typos * Add exemplars query only when enabled * Show graph in explore without filling it * Update exemplars plugin y value scale selection * Update tests * Add data source picker for internal linking * Increase pointSize for better visibility * Fix explore e2e test * Fix data link title variable interpolation * Use new switch component in PromExemplarField * Move FieldLink component to new file * Convert exemplar to datalink * Add legend toggling logic to Explore * Add legend toggling to Explore * Address Ivana's feedback * Address Andrej's comments * Address Gio's feedback * Add tests for result_transformer * Fix eslint issues * Change sampler formula for better readability Co-authored-by: David Kaltschmidt <david@leia.lan> Co-authored-by: David Kaltschmidt <david@leia.fritz.box> Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com>
5 years ago
}));
describe('Prometheus Result Transformer', () => {
describe('parse variants of "+Inf" and "-Inf" strings', () => {
it('+Inf', () => {
expect(parseSampleValue('+Inf')).toEqual(Number.POSITIVE_INFINITY);
});
it('Inf', () => {
expect(parseSampleValue('Inf')).toEqual(Number.POSITIVE_INFINITY);
});
it('inf', () => {
expect(parseSampleValue('inf')).toEqual(Number.POSITIVE_INFINITY);
});
it('+Infinity', () => {
expect(parseSampleValue('+Infinity')).toEqual(Number.POSITIVE_INFINITY);
});
it('+infinity', () => {
expect(parseSampleValue('+infinity')).toEqual(Number.POSITIVE_INFINITY);
});
it('infinity', () => {
expect(parseSampleValue('infinity')).toEqual(Number.POSITIVE_INFINITY);
});
it('-Inf', () => {
expect(parseSampleValue('-Inf')).toEqual(Number.NEGATIVE_INFINITY);
});
it('-inf', () => {
expect(parseSampleValue('-inf')).toEqual(Number.NEGATIVE_INFINITY);
});
it('-Infinity', () => {
expect(parseSampleValue('-Infinity')).toEqual(Number.NEGATIVE_INFINITY);
});
it('-infinity', () => {
expect(parseSampleValue('-infinity')).toEqual(Number.NEGATIVE_INFINITY);
});
});
describe('sortSeriesByLabel() should use frame.fields[1].state?.displayName when available', () => {
let frames = [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 2, 3] },
{
type: FieldType.number,
values: [4, 5, 6],
config: {
displayNameFromDS: '2',
},
labels: {
offset_days: '2',
},
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 2, 3] },
{
type: FieldType.number,
values: [7, 8, 9],
config: {
displayNameFromDS: '1',
},
labels: {
offset_days: '1',
},
},
],
}),
];
it('sorts by displayNameFromDS', () => {
cacheFieldDisplayNames(frames);
let sorted = frames.slice().sort(sortSeriesByLabel);
expect(sorted[0]).toEqual(frames[1]);
expect(sorted[1]).toEqual(frames[0]);
});
});
describe('transformV2', () => {
it('results with time_series format should be enriched with preferredVisualisationType', () => {
const request = {
targets: [
{
format: 'time_series',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
{
fields: [],
length: 2,
name: 'ALERTS',
refId: 'A',
},
],
} as unknown as DataQueryResponse;
const series = transformV2(response, request, {});
expect(series).toEqual({
data: [{ fields: [], length: 2, meta: { preferredVisualisationType: 'graph' }, name: 'ALERTS', refId: 'A' }],
state: 'Done',
});
});
it('dataplane handling, adds displayNameFromDs from calculateFieldDisplayName() when __name__ is the field name when legendFormat is auto', () => {
const request = {
targets: [
{
format: 'time_series',
refId: 'A',
legendFormat: '__auto',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
{
fields: [
{
name: 'Time',
type: 'time',
values: [1],
typeInfo: { frame: 'time.Time' },
},
{
name: 'up',
labels: { __name__: 'up' },
config: {},
values: [1],
},
],
length: 1,
refId: 'A',
meta: {
type: 'timeseries-multi',
typeVersion: [0, 1],
},
},
],
} as unknown as DataQueryResponse;
const series = transformV2(response, request, {});
expect(series).toEqual({
data: [
{
fields: [
{
name: 'Time',
type: 'time',
values: [1],
typeInfo: { frame: 'time.Time' },
},
{
config: { displayNameFromDS: 'up' },
labels: { __name__: 'up' },
name: 'up',
values: [1],
},
],
length: 1,
meta: {
type: 'timeseries-multi',
typeVersion: [0, 1],
preferredVisualisationType: 'graph',
},
refId: 'A',
},
],
state: 'Done',
});
});
it('results with table format should be transformed to table dataFrames', () => {
const request = {
targets: [
{
format: 'table',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: 'value1', label2: 'value2' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, request, {});
expect(series.data[0].fields[0].name).toEqual('Time');
expect(series.data[0].fields[1].name).toEqual('label1');
expect(series.data[0].fields[2].name).toEqual('label2');
expect(series.data[0].fields[3].name).toEqual('Value');
expect(series.data[0].meta?.preferredVisualisationType).toEqual('rawPrometheus');
});
it('results with table format and multiple data frames should be transformed to 1 table dataFrame', () => {
const request = {
targets: [
{
format: 'table',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: 'value1', label2: 'value2' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [2, 3, 7] },
{
name: 'value',
type: FieldType.number,
values: [2, 3, 7],
labels: { label3: 'value3', label4: 'value4' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, request, {});
expect(series.data.length).toEqual(1);
expect(series.data[0].fields[0].name).toEqual('Time');
expect(series.data[0].fields[1].name).toEqual('label1');
expect(series.data[0].fields[2].name).toEqual('label2');
expect(series.data[0].fields[3].name).toEqual('label3');
expect(series.data[0].fields[4].name).toEqual('label4');
expect(series.data[0].fields[5].name).toEqual('Value');
expect(series.data[0].meta?.preferredVisualisationType).toEqual('rawPrometheus' as PreferredVisualisationType);
});
it('results with table and time_series format should be correctly transformed', () => {
const options = {
targets: [
{
format: 'table',
refId: 'A',
},
{
format: 'time_series',
refId: 'B',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: 'value1', label2: 'value2' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: 'value1', label2: 'value2' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(2);
expect(series.data[0].meta?.preferredVisualisationType).toEqual('graph');
expect(series.data[1].fields.length).toEqual(4);
expect(series.data[1].meta?.preferredVisualisationType).toEqual('rawPrometheus' as PreferredVisualisationType);
});
// Heatmap frames can either have a name of the metric, or if there is no metric, a name of "Value"
it('results with heatmap format (no metric name) should be correctly transformed', () => {
const options = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(4);
expect(series.data[0].fields[1].values).toEqual([10, 10, 0]);
expect(series.data[0].fields[2].values).toEqual([10, 0, 30]);
expect(series.data[0].fields[3].values).toEqual([10, 0, 10]);
expect(series.data[0].fields[1].name).toEqual('1');
expect(series.data[0].fields[2].name).toEqual('2');
expect(series.data[0].fields[3].name).toEqual('+Inf');
});
it('results with heatmap format (with metric name) should be correctly transformed', () => {
const options = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2', __name__: 'metric_name' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(4);
expect(series.data[0].fields[1].values).toEqual([10, 10, 0]);
expect(series.data[0].fields[2].values).toEqual([10, 0, 30]);
expect(series.data[0].fields[3].values).toEqual([10, 0, 10]);
expect(series.data[0].fields[1].name).toEqual('1');
expect(series.data[0].fields[2].name).toEqual('2');
expect(series.data[0].fields[3].name).toEqual('+Inf');
});
it('results with heatmap format (no metric name) from multiple queries should be correctly transformed', () => {
const options = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
{
format: 'heatmap',
refId: 'B',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(4);
expect(series.data[0].fields[1].values).toEqual([10, 10, 0]);
expect(series.data[0].fields[2].values).toEqual([10, 0, 30]);
expect(series.data[0].fields[3].values).toEqual([10, 0, 10]);
});
it('results with heatmap format (with metric name) from multiple queries should be correctly transformed', () => {
const options = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
{
format: 'heatmap',
refId: 'B',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2', __name__: 'metric_name' },
},
],
}),
createDataFrame({
refId: 'B',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'metric_name',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf', __name__: 'metric_name' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(4);
expect(series.data[0].fields[1].values).toEqual([10, 10, 0]);
expect(series.data[0].fields[2].values).toEqual([10, 0, 30]);
expect(series.data[0].fields[3].values).toEqual([10, 0, 10]);
});
it('results with heatmap format and multiple histograms should be grouped and de-accumulated by non-le labels', () => {
const options = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
// 10
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1', additionalProperty: '10' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [20, 10, 30],
labels: { le: '2', additionalProperty: '10' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 40],
labels: { le: '+Inf', additionalProperty: '10' },
},
],
}),
// 20
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [0, 10, 10],
labels: { le: '1', additionalProperty: '20' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [20, 10, 40],
labels: { le: '2', additionalProperty: '20' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 60],
labels: { le: '+Inf', additionalProperty: '20' },
},
],
}),
// 30
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 30, 60],
labels: { le: '1', additionalProperty: '30' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [30, 40, 60],
labels: { le: '2', additionalProperty: '30' },
},
],
}),
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [40, 40, 60],
labels: { le: '+Inf', additionalProperty: '30' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(4);
expect(series.data[0].fields[1].values).toEqual([10, 10, 0]);
expect(series.data[0].fields[2].values).toEqual([10, 0, 30]);
expect(series.data[0].fields[3].values).toEqual([10, 0, 10]);
expect(series.data[1].fields[1].values).toEqual([0, 10, 10]);
expect(series.data[1].fields[2].values).toEqual([20, 0, 30]);
expect(series.data[1].fields[3].values).toEqual([10, 0, 20]);
expect(series.data[2].fields[1].values).toEqual([30, 30, 60]);
expect(series.data[2].fields[2].values).toEqual([0, 10, 0]);
expect(series.data[2].fields[3].values).toEqual([10, 0, 0]);
});
it('Retains exemplar frames when data returned is a heatmap', () => {
const options = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1' },
},
],
}),
createDataFrame({
refId: 'A',
name: 'exemplar',
meta: {
custom: {
resultType: 'exemplar',
},
},
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4, 3, 2, 1] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 40, 90, 14, 21],
labels: { le: '6' },
},
{
name: 'Test',
type: FieldType.string,
values: ['hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow'],
labels: { le: '6' },
},
],
}),
],
} as unknown as DataQueryResponse;
const series = transformV2(response, options, {});
expect(series.data[0].fields.length).toEqual(2);
expect(series.data.length).toEqual(2);
expect(series.data[1].fields[2].values).toEqual(['hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow']);
expect(series.data[1].fields.length).toEqual(3);
});
it('should not add a link with an error when exemplarTraceIdDestinations is not configured properly', () => {
const response = {
state: 'Done',
data: [
createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'Value',
type: FieldType.number,
values: [10, 10, 0],
labels: { le: '1' },
},
],
}),
createDataFrame({
refId: 'A',
name: 'exemplar',
meta: {
custom: {
resultType: 'exemplar',
},
},
fields: [
{ name: 'Time', type: FieldType.time, values: [6, 5, 4, 3, 2, 1] },
{
name: 'Value',
type: FieldType.number,
values: [30, 10, 40, 90, 14, 21],
labels: { le: '6' },
},
{
name: 'traceID',
type: FieldType.string,
values: ['unknown'],
labels: { le: '6' },
},
],
}),
],
} as unknown as DataQueryResponse;
const request = {
targets: [
{
format: 'heatmap',
refId: 'A',
},
],
} as unknown as DataQueryRequest<PromQuery>;
const testOptions: any = {
exemplarTraceIdDestinations: [
{
name: 'traceID',
datasourceUid: 'unknown',
},
],
};
const series = transformV2(response, request, testOptions);
expect(series.data[1].fields.length).toEqual(3);
expect(series.data[1].name).toEqual('exemplar');
const traceField = series.data[1].fields.find((f) => f.name === 'traceID');
expect(traceField).toBeDefined();
expect(traceField!.config.links?.length).toBe(0);
});
});
describe('transformDFToTable', () => {
it('transforms dataFrame with response length 1 to table dataFrame', () => {
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: 'value1', label2: 'value2' },
},
],
});
const tableDf = transformDFToTable([df])[0];
expect(tableDf.fields.length).toBe(4);
expect(tableDf.fields[0].name).toBe('Time');
expect(tableDf.fields[1].name).toBe('label1');
expect(tableDf.fields[1].values[0]).toBe('value1');
expect(tableDf.fields[2].name).toBe('label2');
expect(tableDf.fields[2].values[0]).toBe('value2');
expect(tableDf.fields[3].name).toBe('Value');
});
it('transforms dataFrame with response length 2 to table dataFrame', () => {
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: 'value1', label2: 'value2' },
},
],
});
const tableDf = transformDFToTable([df])[0];
expect(tableDf.fields.length).toBe(4);
expect(tableDf.fields[0].name).toBe('Time');
expect(tableDf.fields[1].name).toBe('label1');
expect(tableDf.fields[1].values[0]).toBe('value1');
expect(tableDf.fields[2].name).toBe('label2');
expect(tableDf.fields[2].values[0]).toBe('value2');
expect(tableDf.fields[3].name).toBe('Value');
});
// Queries do not always return results
it('transforms dataFrame and empty dataFrame mock responses to table dataFrames', () => {
const value1 = 'value1';
const value2 = 'value2';
const dataframes = [
createDataFrame({
refId: 'A',
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: value1, label2: value2 },
},
],
}),
createDataFrame({
refId: 'B',
fields: [],
}),
];
const transformedTableDataFrames = transformDFToTable(dataframes);
// Expect the first query to still return valid results
expect(transformedTableDataFrames[0].fields.length).toBe(4);
expect(transformedTableDataFrames[0].fields[0].name).toBe('Time');
expect(transformedTableDataFrames[0].fields[1].name).toBe('label1');
expect(transformedTableDataFrames[0].fields[1].values[0]).toBe(value1);
expect(transformedTableDataFrames[0].fields[2].name).toBe('label2');
expect(transformedTableDataFrames[0].fields[2].values[0]).toBe(value2);
expect(transformedTableDataFrames[0].fields[3].name).toBe('Value #A');
// Expect the invalid/empty results not to throw an error and to return empty arrays
expect(transformedTableDataFrames[1].fields[1].labels).toBe(undefined);
expect(transformedTableDataFrames[1].fields[1].name).toBe('Value #B');
expect(transformedTableDataFrames[1].fields[1].values).toEqual([]);
expect(transformedTableDataFrames[1].fields[0].values).toEqual([]);
});
it('transforms dataframes with metadata resolving from their refIds', () => {
const value1 = 'value1';
const value2 = 'value2';
const executedQueryForRefA = 'Expr: avg_over_time(access_evaluation_duration_bucket[15s])\nStep: 15s';
const executedQueryForRefB = 'Expr: avg_over_time(access_evaluation_duration_bucket[5m])\nStep: 15s';
const dataframes = [
createDataFrame({
refId: 'A',
meta: {
typeVersion: [0, 1],
custom: {
resultType: 'vector',
},
executedQueryString: executedQueryForRefA,
},
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: value1, label2: value2 },
},
],
}),
createDataFrame({
refId: 'B',
meta: {
typeVersion: [0, 1],
custom: {
resultType: 'vector',
},
executedQueryString: executedQueryForRefB,
},
fields: [
{ name: 'time', type: FieldType.time, values: [6, 5, 4] },
{
name: 'value',
type: FieldType.number,
values: [6, 5, 4],
labels: { label1: value1, label2: value2 },
},
],
}),
];
const transformedTableDataFrames = transformDFToTable(dataframes);
expect(transformedTableDataFrames[0].meta).toBeTruthy();
expect(transformedTableDataFrames[1].meta).toBeTruthy();
expect(transformedTableDataFrames[0].meta?.executedQueryString).toEqual(executedQueryForRefA);
expect(transformedTableDataFrames[1].meta?.executedQueryString).toEqual(executedQueryForRefB);
});
});
});