Graphite: Ensure frames have refId set (#99911)

Ensure frames have refIDs

- Sanitise the refID
- Alias the query target with the refID
- Set the appropriate refID on the created frames
- Update tests
This commit is contained in:
Andreas Christou
2025-02-03 17:43:28 +00:00
committed by GitHub
parent 66aec9ec1f
commit 8a259ecafa
2 changed files with 40 additions and 15 deletions
@@ -55,6 +55,10 @@ describe('graphiteDatasource', () => {
describe('convertResponseToDataFrames', () => {
it('should transform regular result', () => {
const refIDMap = {
refIDA: 'A',
refIDB: 'B',
};
const result = ctx.ds.convertResponseToDataFrames(
createFetchResponse({
meta: {
@@ -65,7 +69,7 @@ describe('graphiteDatasource', () => {
},
series: [
{
target: 'seriesA',
target: 'seriesA refIDA',
datapoints: [
[100, 200],
[101, 201],
@@ -85,7 +89,7 @@ describe('graphiteDatasource', () => {
],
},
{
target: 'seriesB',
target: 'seriesB refIDB',
meta: [
{
'aggnum-norm': 1,
@@ -105,7 +109,8 @@ describe('graphiteDatasource', () => {
],
},
],
})
}),
refIDMap
);
expect(result.data.length).toBe(2);
@@ -113,24 +118,28 @@ describe('graphiteDatasource', () => {
expect(getFrameDisplayName(result.data[1])).toBe('seriesB');
expect(result.data[0].length).toBe(2);
expect(result.data[0].meta.notices.length).toBe(1);
expect(result.data[0].refId).toBe('A');
expect(result.data[0].meta.notices[0].text).toBe('Data is rolled up, aggregated over 2h using Average function');
expect(result.data[1].meta.notices).toBeUndefined();
expect(result.data[1].refId).toBe('B');
});
});
describe('When querying graphite with one target using query editor target spec', () => {
const query = {
panelId: 3,
dashboardId: 5,
range: { from: dateTime('2022-04-01T00:00:00'), to: dateTime('2022-07-01T00:00:00') },
targets: [{ target: 'prod1.count' }, { target: 'prod2.count' }],
maxDataPoints: 500,
};
let response: unknown;
let requestOptions: BackendSrvRequest;
beforeEach(() => {
const query = {
panelId: 3,
dashboardId: 5,
range: { from: dateTime('2022-04-01T00:00:00'), to: dateTime('2022-07-01T00:00:00') },
targets: [
{ target: 'prod1.count', refId: 'A' },
{ target: 'prod2.count', refId: 'B' },
],
maxDataPoints: 500,
};
fetchMock.mockImplementation((options) => {
requestOptions = options;
return of(
@@ -164,8 +173,8 @@ describe('graphiteDatasource', () => {
it('should query correctly', () => {
const params = requestOptions.data.split('&');
expect(params).toContain('target=prod1.count');
expect(params).toContain('target=prod2.count');
expect(params).toContain(`target=${encodeURIComponent(`aliasSub(prod1.count, "(^.*$)", "\\1 A")`)}`);
expect(params).toContain(`target=${encodeURIComponent(`aliasSub(prod2.count, "(^.*$)", "\\1 B")`)}`);
expect(params).toContain('from=1648789200');
expect(params).toContain('until=1656655200');
});
@@ -209,6 +209,17 @@ 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 } = {};
for (const target of options.targets) {
// Sanitise the refID otherwise the Graphite query will fail
const formattedRefId = target.refId.replaceAll(' ', '_');
refIds[formattedRefId] = target.refId;
// 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;
}
// handle the queries here
const graphOptions = {
from: this.translateTime(options.range.from, false, options.timezone),
@@ -243,7 +254,7 @@ export class GraphiteDatasource
httpOptions.requestId = this.name + '.panelId.' + options.panelId;
}
return this.doGraphiteRequest(httpOptions).pipe(map(this.convertResponseToDataFrames));
return this.doGraphiteRequest(httpOptions).pipe(map((result) => this.convertResponseToDataFrames(result, refIds)));
}
addTracingHeaders(
@@ -267,7 +278,7 @@ export class GraphiteDatasource
}
}
convertResponseToDataFrames = (result: FetchResponse): DataQueryResponse => {
convertResponseToDataFrames = (result: FetchResponse, refIdMap: { [key: string]: string }): DataQueryResponse => {
const data: DataFrame[] = [];
if (!result || !result.data) {
return { data };
@@ -282,6 +293,9 @@ export class GraphiteDatasource
for (let i = 0; i < series.length; i++) {
const s = series[i];
// Retrieve the original refID of the query
const [target, refId] = s.target.split(' ');
s.target = target;
// Disables Grafana own series naming
s.title = s.target;
@@ -291,6 +305,8 @@ export class GraphiteDatasource
}
const frame = toDataFrame(s);
// Set the refID value on the frame
frame.refId = refIdMap[refId];
// Metrictank metadata
if (s.meta) {