diff --git a/public/app/plugins/datasource/graphite/datasource.test.ts b/public/app/plugins/datasource/graphite/datasource.test.ts index 849271524eb..a9268dbaed9 100644 --- a/public/app/plugins/datasource/graphite/datasource.test.ts +++ b/public/app/plugins/datasource/graphite/datasource.test.ts @@ -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'); }); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index e3ab7e93dc7..23979d1d546 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -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) {