diff --git a/public/app/plugins/datasource/zipkin/datasource.test.ts b/public/app/plugins/datasource/zipkin/datasource.test.ts index de32987694f..4123de7960d 100644 --- a/public/app/plugins/datasource/zipkin/datasource.test.ts +++ b/public/app/plugins/datasource/zipkin/datasource.test.ts @@ -1,11 +1,18 @@ import { lastValueFrom, of } from 'rxjs'; import { createFetchResponse } from 'test/helpers/createFetchResponse'; -import { DataQueryRequest, DataSourceInstanceSettings, DataSourcePluginMeta, FieldType } from '@grafana/data'; +import { + DataFrameView, + DataQueryRequest, + DataSourceInstanceSettings, + DataSourcePluginMeta, + FieldType, +} from '@grafana/data'; import { BackendSrv, TemplateSrv } from '@grafana/runtime'; -import { ZipkinDatasource } from './datasource'; -import mockJson from './mockJsonResponse.json'; +import { addNodeGraphFramesToResponse, ZipkinDatasource } from './datasource'; +import mockJson from './mocks/mockJsonResponse.json'; +import { mockTraceDataFrame } from './mocks/mockTraceDataFrame'; import { ZipkinQuery, ZipkinSpan } from './types'; import { traceFrameFields, zipkinResponse } from './utils/testData'; @@ -82,6 +89,35 @@ describe('ZipkinDatasource', () => { }); }); +describe('addNodeGraphFramesToResponse', () => { + it('transforms basic response into nodes and edges frame', () => { + const responseWithNodeFrames = addNodeGraphFramesToResponse({ data: mockTraceDataFrame }); + let view = new DataFrameView(responseWithNodeFrames.data[1]); + expect(view.get(0)).toMatchObject({ + id: '4322526419282105830', + title: 'service1', + subtitle: 'store.validateQueryTimeRange', + mainstat: '0ms (1.6%)', + secondarystat: '0ms (100%)', + color: 0.016, + }); + expect(view.get(3)).toMatchObject({ + id: '1853508259384889601', + title: 'service1', + subtitle: 'Shipper.Uploads.Query', + mainstat: '0.05ms (18.8%)', + secondarystat: '0.05ms (100%)', + color: 0.188, + }); + }); + + it('handles empty response', () => { + const response = { data: [] }; + const responseWithNodeFrames = addNodeGraphFramesToResponse(response); + expect(responseWithNodeFrames).toBe(response); + }); +}); + function setupBackendSrv(response: ZipkinSpan[]) { const defaultMock = () => of(createFetchResponse(response)); diff --git a/public/app/plugins/datasource/zipkin/datasource.ts b/public/app/plugins/datasource/zipkin/datasource.ts index a6c58314ef0..54ff15dafb1 100644 --- a/public/app/plugins/datasource/zipkin/datasource.ts +++ b/public/app/plugins/datasource/zipkin/datasource.ts @@ -10,8 +10,9 @@ import { createDataFrame, ScopedVars, urlUtil, + toDataFrame, } from '@grafana/data'; -import { NodeGraphOptions, SpanBarOptions } from '@grafana/o11y-ds-frontend'; +import { createNodeGraphFrames, NodeGraphOptions, SpanBarOptions } from '@grafana/o11y-ds-frontend'; import { BackendSrvRequest, config, @@ -60,8 +61,15 @@ export class ZipkinDatasource extends DataSourceWithBackend { + if (this.nodeGraph?.enabled) { + return addNodeGraphFramesToResponse(response); + } + return response; + }) + ); } const query = this.applyTemplateVariables(target, options.scopedVars); return this.request(`${apiPrefix}/trace/${encodeURIComponent(query.query)}`).pipe( @@ -142,6 +150,21 @@ function responseToDataQueryResponse(response: { data: ZipkinSpan[] }, nodeGraph }; } +export function addNodeGraphFramesToResponse(response: DataQueryResponse): DataQueryResponse { + if (!response.data || response.data.length === 0) { + return response; + } + + // This is frame, but it is not typed, so we use toDataFrame to convert it to DataFrame + const frame = toDataFrame(response.data[0]); + const data = [...response.data]; + data.push(...createNodeGraphFrames(frame)); + return { + ...response, + data, + }; +} + const emptyDataQueryResponse = { data: [ createDataFrame({ diff --git a/public/app/plugins/datasource/zipkin/mockJsonResponse.json b/public/app/plugins/datasource/zipkin/mocks/mockJsonResponse.json similarity index 100% rename from public/app/plugins/datasource/zipkin/mockJsonResponse.json rename to public/app/plugins/datasource/zipkin/mocks/mockJsonResponse.json diff --git a/public/app/plugins/datasource/zipkin/mocks/mockTraceDataFrame.ts b/public/app/plugins/datasource/zipkin/mocks/mockTraceDataFrame.ts new file mode 100644 index 00000000000..183c92885c2 --- /dev/null +++ b/public/app/plugins/datasource/zipkin/mocks/mockTraceDataFrame.ts @@ -0,0 +1,234 @@ +import { createDataFrame } from '@grafana/data'; + +export const mockTraceDataFrame = [ + createDataFrame({ + fields: [ + { + name: 'traceID', + values: ['04450900759028499335', '04450900759028499335', '04450900759028499335', '04450900759028499335'], + }, + { + name: 'spanID', + values: ['4322526419282105830', '3095626263385822295', '6397320272727147889', '1853508259384889601'], + }, + { + name: 'parentSpanID', + values: ['3095626263385822295', '3198122728676260175', '7501257416198979329', ''], + }, + { + name: 'operationName', + values: [ + 'store.validateQueryTimeRange', + 'store.validateQuery', + 'cachingIndexClient.cacheFetch', + 'Shipper.Uploads.Query', + ], + }, + { + name: 'serviceName', + values: ['service1', 'service1', 'service1', 'service1'], + }, + { + name: 'serviceTags', + values: [ + [ + { + value: 'service1', + key: 'service.name', + }, + { + value: 'Zipkin-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'service1', + key: 'service.name', + }, + { + value: 'Zipkin-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'service1', + key: 'service.name', + }, + { + value: 'Zipkin-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'service1', + key: 'service.name', + }, + { + value: 'Zipkin-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + ], + }, + { + name: 'startTime', + values: [1619712655875.4539, 1619712655875.4502, 1619712655875.592, 1619712655875.653], + }, + { + name: 'duration', + values: [0.004, 0.016, 0.039, 0.047], + }, + { + name: 'logs', + values: [ + null, + null, + [ + { + timestamp: 1619712655875.631, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 0, + key: 'hits', + }, + { + value: 16, + key: 'misses', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.738, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'index_18746', + key: 'table-name', + }, + { + value: 16, + key: 'query-count', + }, + ], + }, + { + timestamp: 1619712655875.773, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compactor-1619711145.gz', + key: 'queried-db', + }, + ], + }, + { + timestamp: 1619712655875.794, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: '708c78ea08c1-1619516350042748959-1619711100.gz', + key: 'queried-db', + }, + ], + }, + ], + ], + }, + { + name: 'tags', + values: [ + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + ], + }, + ], + }), +];