Graphite: Fix Graphite series interpolation (#104471)
* Fix graphite series interpolation * Trigger build
This commit is contained in:
@@ -5,9 +5,9 @@ import { createFetchResponse } from 'test/helpers/createFetchResponse';
|
||||
import {
|
||||
AbstractLabelMatcher,
|
||||
AbstractLabelOperator,
|
||||
getFrameDisplayName,
|
||||
dateTime,
|
||||
DataQueryRequest,
|
||||
dateTime,
|
||||
getFrameDisplayName,
|
||||
MetricFindValue,
|
||||
} from '@grafana/data';
|
||||
import { BackendSrvRequest } from '@grafana/runtime';
|
||||
@@ -373,62 +373,116 @@ describe('graphiteDatasource', () => {
|
||||
|
||||
describe('building graphite params', () => {
|
||||
it('should return empty array if no targets', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{}],
|
||||
});
|
||||
const originalTargetMap = { A: '' };
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{}],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should uri escape targets', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: 'prod1.{test,test2}' }, { target: 'prod2.count' }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: 'prod1.{test,test2}',
|
||||
B: 'prod2.count',
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: 'prod1.{test,test2}' }, { target: 'prod2.count' }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results).toContain('target=prod1.%7Btest%2Ctest2%7D');
|
||||
});
|
||||
|
||||
it('should replace target placeholder', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: 'series1' }, { target: 'series2' }, { target: 'asPercent(#A,#B)' }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: 'series1',
|
||||
B: 'series2',
|
||||
C: 'asPercent(#A,#B)',
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: 'series1' }, { target: 'series2' }, { target: 'asPercent(#A,#B)' }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results[2]).toBe('target=asPercent(series1%2Cseries2)');
|
||||
});
|
||||
|
||||
it('should replace target placeholder for hidden series', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [
|
||||
{ target: 'series1', hide: true },
|
||||
{ target: 'sumSeries(#A)', hide: true },
|
||||
{ target: 'asPercent(#A,#B)' },
|
||||
],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: 'series1',
|
||||
B: 'sumSeries(#A)',
|
||||
C: 'asPercent(#A,#B)',
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [
|
||||
{ target: 'series1', hide: true },
|
||||
{ target: 'sumSeries(#A)', hide: true },
|
||||
{ target: 'asPercent(#A,#B)' },
|
||||
],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results[0]).toBe('target=' + encodeURIComponent('asPercent(series1,sumSeries(series1))'));
|
||||
});
|
||||
|
||||
it('should replace target placeholder when nesting query references', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: 'series1' }, { target: 'sumSeries(#A)' }, { target: 'asPercent(#A,#B)' }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: 'series1',
|
||||
B: 'sumSeries(#A)',
|
||||
C: 'asPercent(#A,#B)',
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: 'series1' }, { target: 'sumSeries(#A)' }, { target: 'asPercent(#A,#B)' }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results[2]).toBe('target=' + encodeURIComponent('asPercent(series1,sumSeries(series1))'));
|
||||
});
|
||||
|
||||
it('should fix wrong minute interval parameters', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: "summarize(prod.25m.count, '25m', 'sum')" }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: "summarize(prod.25m.count, '25m', 'sum')",
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: "summarize(prod.25m.count, '25m', 'sum')" }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results[0]).toBe('target=' + encodeURIComponent("summarize(prod.25m.count, '25min', 'sum')"));
|
||||
});
|
||||
|
||||
it('should fix wrong month interval parameters', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: "summarize(prod.5M.count, '5M', 'sum')" }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: "summarize(prod.5M.count, '5M', 'sum')",
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: "summarize(prod.5M.count, '5M', 'sum')" }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results[0]).toBe('target=' + encodeURIComponent("summarize(prod.5M.count, '5mon', 'sum')"));
|
||||
});
|
||||
|
||||
it('should ignore empty targets', () => {
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: 'series1' }, { target: '' }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: 'series1',
|
||||
B: '',
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: 'series1' }, { target: '' }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results.length).toBe(2);
|
||||
});
|
||||
|
||||
@@ -442,9 +496,15 @@ describe('graphiteDatasource', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: 'my.$metric.*' }],
|
||||
});
|
||||
const originalTargetMap = {
|
||||
A: 'my.$metric.*',
|
||||
};
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: 'my.$metric.*' }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
expect(results).toStrictEqual(['target=my.b.*', 'format=json']);
|
||||
});
|
||||
|
||||
@@ -456,10 +516,13 @@ describe('graphiteDatasource', () => {
|
||||
current: { value: ['a', 'b'] },
|
||||
},
|
||||
]);
|
||||
|
||||
const results = ctx.ds.buildGraphiteParams({
|
||||
targets: [{ target: 'my.[[metric]].*' }],
|
||||
});
|
||||
const originalTargetMap = { A: 'my.[[metric]].*' };
|
||||
const results = ctx.ds.buildGraphiteParams(
|
||||
{
|
||||
targets: [{ target: 'my.[[metric]].*' }],
|
||||
},
|
||||
originalTargetMap
|
||||
);
|
||||
|
||||
expect(results).toStrictEqual(['target=my.%7Ba%2Cb%7D.*', 'format=json']);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { each, indexOf, isArray, isString, map as _map } from 'lodash';
|
||||
import { map as _map, each, indexOf, isArray, isString } from 'lodash';
|
||||
import { lastValueFrom, merge, Observable, of, OperatorFunction, pipe, throwError } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@@ -13,13 +13,13 @@ import {
|
||||
DataSourceWithQueryExportSupport,
|
||||
dateMath,
|
||||
dateTime,
|
||||
getSearchFilterScopedVar,
|
||||
MetricFindValue,
|
||||
QueryResultMetaStat,
|
||||
ScopedVars,
|
||||
TimeRange,
|
||||
TimeZone,
|
||||
toDataFrame,
|
||||
getSearchFilterScopedVar,
|
||||
} from '@grafana/data';
|
||||
import { BackendSrvRequest, FetchResponse, getBackendSrv } from '@grafana/runtime';
|
||||
import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version';
|
||||
@@ -210,12 +210,19 @@ export class GraphiteDatasource
|
||||
return merge(...streams);
|
||||
}
|
||||
|
||||
// Use this object to map the original refID of the query to our sanitised one
|
||||
const refIds: { [key: string]: string } = {};
|
||||
// Use this object to map the sanitised refID to the original
|
||||
const formattedRefIdsMap: { [key: string]: string } = {};
|
||||
// Use this object to map the original refID to the original target
|
||||
const originalTargetMap: { [key: string]: string } = {};
|
||||
for (const target of options.targets) {
|
||||
// Sanitise the refID otherwise the Graphite query will fail
|
||||
const formattedRefId = target.refId.replaceAll(' ', '_');
|
||||
refIds[formattedRefId] = target.refId;
|
||||
formattedRefIdsMap[formattedRefId] = target.refId;
|
||||
// Track the original target to ensure if we need to interpolate a series, we interpolate using the original target
|
||||
// rather than the target wrapped in aliasSub e.g.:
|
||||
// Suppose a query has three targets: A: metric1 B: sumSeries(#A) and C: asPercent(#A, #B)
|
||||
// We want the targets to be interpolated to: A: aliasSub(metric1, "(^.*$)", "\\1 A"), B: aliasSub(sumSeries(metric1), "(^.*$)", "\\1 B") and C: asPercent(metric1, sumSeries(metric1))
|
||||
originalTargetMap[target.refId] = target.target || '';
|
||||
// Use aliasSub to include the refID in the response series name. This allows us to set the refID on the frame.
|
||||
const updatedTarget = `aliasSub(${target.target}, "(^.*$)", "\\1 ${formattedRefId}")`;
|
||||
target.target = updatedTarget;
|
||||
@@ -231,7 +238,7 @@ export class GraphiteDatasource
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
};
|
||||
|
||||
const params = this.buildGraphiteParams(graphOptions, options.scopedVars);
|
||||
const params = this.buildGraphiteParams(graphOptions, originalTargetMap, options.scopedVars);
|
||||
if (params.length === 0) {
|
||||
return of({ data: [] });
|
||||
}
|
||||
@@ -255,7 +262,9 @@ export class GraphiteDatasource
|
||||
httpOptions.requestId = this.name + '.panelId.' + options.panelId;
|
||||
}
|
||||
|
||||
return this.doGraphiteRequest(httpOptions).pipe(map((result) => this.convertResponseToDataFrames(result, refIds)));
|
||||
return this.doGraphiteRequest(httpOptions).pipe(
|
||||
map((result) => this.convertResponseToDataFrames(result, formattedRefIdsMap))
|
||||
);
|
||||
}
|
||||
|
||||
addTracingHeaders(
|
||||
@@ -975,7 +984,7 @@ export class GraphiteDatasource
|
||||
);
|
||||
}
|
||||
|
||||
buildGraphiteParams(options: any, scopedVars?: ScopedVars): string[] {
|
||||
buildGraphiteParams(options: any, originalTargetMap: { [key: string]: string }, scopedVars?: ScopedVars): string[] {
|
||||
const graphiteOptions = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout'];
|
||||
const cleanOptions = [],
|
||||
targets: Record<string, string> = {};
|
||||
@@ -1006,7 +1015,8 @@ export class GraphiteDatasource
|
||||
}
|
||||
|
||||
function nestedSeriesRegexReplacer(match: string, g1: string | number) {
|
||||
return targets[g1] || match;
|
||||
// Recursively replace all nested series references
|
||||
return originalTargetMap[g1].replace(regex, nestedSeriesRegexReplacer) || match;
|
||||
}
|
||||
|
||||
for (i = 0; i < options.targets.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user