Revert "Tempo: convert to backend data source (#31618)" (#31702)

This reverts commit 862f09376f.
This commit is contained in:
Zoltán Bedi
2021-03-04 21:20:26 +01:00
committed by GitHub
parent 53d4acdad2
commit c15d1f498a
6 changed files with 265 additions and 374 deletions

View File

@@ -1,39 +1,82 @@
import { DataSourceInstanceSettings, FieldType, MutableDataFrame, PluginType } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv';
import { of } from 'rxjs';
import { createFetchResponse } from 'test/helpers/createFetchResponse';
import { TempoDatasource } from './datasource';
jest.mock('../../../../../packages/grafana-runtime/src/services/backendSrv.ts', () => ({
getBackendSrv: () => backendSrv,
}));
jest.mock('../../../../../packages/grafana-runtime/src/utils/queryResponse.ts', () => ({
toDataQueryResponse: (resp: any) => resp,
}));
describe('Tempo data source', () => {
beforeEach(() => {
jest.clearAllMocks();
});
import { TempoDatasource, TempoQuery } from './datasource';
import { DataQueryRequest, DataSourceInstanceSettings, FieldType, PluginType, dateTime } from '@grafana/data';
import { BackendSrv, BackendSrvRequest, getBackendSrv, setBackendSrv } from '@grafana/runtime';
describe('JaegerDatasource', () => {
it('returns trace when queried', async () => {
const responseDataFrame = new MutableDataFrame({ fields: [{ name: 'trace', values: ['{}'] }] });
setupBackendSrv([responseDataFrame]);
const ds = new TempoDatasource(defaultSettings);
await expect(ds.query({ targets: [{ query: '12345' }] } as any)).toEmitValuesWith((response) => {
const field = response[0].data[0].fields[0];
await withMockedBackendSrv(makeBackendSrvMock('12345'), async () => {
const ds = new TempoDatasource(defaultSettings);
const response = await ds.query(defaultQuery).toPromise();
const field = response.data[0].fields[0];
expect(field.name).toBe('trace');
expect(field.type).toBe(FieldType.trace);
expect(field.values.get(0)).toEqual({
traceId: '12345',
});
});
});
it('returns trace when traceId with special characters is queried', async () => {
await withMockedBackendSrv(makeBackendSrvMock('a/b'), async () => {
const ds = new TempoDatasource(defaultSettings);
const query = {
...defaultQuery,
targets: [
{
query: 'a/b',
refId: '1',
},
],
};
const response = await ds.query(query).toPromise();
const field = response.data[0].fields[0];
expect(field.name).toBe('trace');
expect(field.type).toBe(FieldType.trace);
expect(field.values.get(0)).toEqual({
traceId: 'a/b',
});
});
});
it('returns empty response if trace id is not specified', async () => {
const ds = new TempoDatasource(defaultSettings);
const response = await ds
.query({
...defaultQuery,
targets: [],
})
.toPromise();
const field = response.data[0].fields[0];
expect(field.name).toBe('trace');
expect(field.type).toBe(FieldType.trace);
expect(field.values.length).toBe(0);
});
});
function setupBackendSrv(response: any) {
const defaultMock = () => of(createFetchResponse(response));
function makeBackendSrvMock(traceId: string) {
return {
datasourceRequest(options: BackendSrvRequest): Promise<any> {
expect(options.url.substr(options.url.length - 17, options.url.length)).toBe(
`/api/traces/${encodeURIComponent(traceId)}`
);
return Promise.resolve({
data: {
data: [
{
traceId,
},
],
},
});
},
} as any;
}
const fetchMock = jest.spyOn(backendSrv, 'fetch');
fetchMock.mockImplementation(defaultMock);
async function withMockedBackendSrv(srv: BackendSrv, fn: () => Promise<void>) {
const oldSrv = getBackendSrv();
setBackendSrv(srv);
await fn();
setBackendSrv(oldSrv);
}
const defaultSettings: DataSourceInstanceSettings = {
@@ -51,3 +94,26 @@ const defaultSettings: DataSourceInstanceSettings = {
},
jsonData: {},
};
const defaultQuery: DataQueryRequest<TempoQuery> = {
requestId: '1',
dashboardId: 0,
interval: '0',
intervalMs: 10,
panelId: 0,
scopedVars: {},
range: {
from: dateTime().subtract(1, 'h'),
to: dateTime(),
raw: { from: '1h', to: 'now' },
},
timezone: 'browser',
app: 'explore',
startTime: 0,
targets: [
{
query: '12345',
refId: '1',
},
],
};

View File

@@ -1,63 +1,121 @@
import {
DataFrame,
DataQuery,
dateMath,
DateTime,
MutableDataFrame,
DataSourceApi,
DataSourceInstanceSettings,
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
DataQuery,
FieldType,
MutableDataFrame,
} from '@grafana/data';
import { DataSourceWithBackend } from '@grafana/runtime';
import { Observable } from 'rxjs';
import { getBackendSrv, BackendSrvRequest } from '@grafana/runtime';
import { Observable, from, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { serializeParams } from 'app/core/utils/fetch';
export type TempoQuery = {
query: string;
} & DataQuery;
export class TempoDatasource extends DataSourceWithBackend<TempoQuery> {
constructor(instanceSettings: DataSourceInstanceSettings) {
export class TempoDatasource extends DataSourceApi<TempoQuery> {
constructor(private instanceSettings: DataSourceInstanceSettings, private readonly timeSrv: TimeSrv = getTimeSrv()) {
super(instanceSettings);
}
query(options: DataQueryRequest<TempoQuery>): Observable<DataQueryResponse> {
return super.query(options).pipe(
map((response) => {
if (response.error) {
return response;
}
async metadataRequest(url: string, params?: Record<string, any>): Promise<any> {
const res = await this._request(url, params, { hideFromInspector: true }).toPromise();
return res.data.data;
}
return {
data: [
new MutableDataFrame({
fields: [
{
name: 'trace',
type: FieldType.trace,
values: [JSON.parse((response.data as DataFrame[])[0].fields[0].values.get(0))],
query(options: DataQueryRequest<TempoQuery>): Observable<DataQueryResponse> {
// At this moment we expect only one target. In case we somehow change the UI to be able to show multiple
// traces at one we need to change this.
const id = options.targets[0]?.query;
if (id) {
return this._request(`/api/traces/${encodeURIComponent(id)}`).pipe(
map((response) => {
return {
data: [
new MutableDataFrame({
fields: [
{
name: 'trace',
type: FieldType.trace,
values: response?.data?.data || [],
},
],
meta: {
preferredVisualisationType: 'trace',
},
],
meta: {
preferredVisualisationType: 'trace',
}),
],
};
})
);
} else {
return of({
data: [
new MutableDataFrame({
fields: [
{
name: 'trace',
type: FieldType.trace,
values: [],
},
}),
],
};
})
);
],
meta: {
preferredVisualisationType: 'trace',
},
}),
],
});
}
}
async testDatasource(): Promise<any> {
const response = await super.query({ targets: [{ query: '', refId: 'A' }] } as any).toPromise();
if (!response.error?.message?.startsWith('failed to get trace')) {
return { status: 'error', message: 'Data source is not working' };
try {
await this._request(`/api/traces/random`).toPromise();
} catch (e) {
// If all went well this request will get back with 400 - Bad request
if (e?.status !== 400) {
throw e;
}
}
return { status: 'success', message: 'Data source is working' };
}
getTimeRange(): { start: number; end: number } {
const range = this.timeSrv.timeRange();
return {
start: getTime(range.from, false),
end: getTime(range.to, true),
};
}
getQueryDisplayText(query: TempoQuery) {
return query.query;
}
private _request(apiUrl: string, data?: any, options?: Partial<BackendSrvRequest>): Observable<Record<string, any>> {
// Hack for proxying metadata requests
const baseUrl = `/api/datasources/proxy/${this.instanceSettings.id}`;
const params = data ? serializeParams(data) : '';
const url = `${baseUrl}${apiUrl}${params.length ? `?${params}` : ''}`;
const req = {
...options,
url,
};
return from(getBackendSrv().datasourceRequest(req));
}
}
function getTime(date: string | DateTime, roundUp: boolean) {
if (typeof date === 'string') {
date = dateMath.parse(date, roundUp)!;
}
return date.valueOf() * 1000;
}