diff --git a/public/app/plugins/datasource/tempo/resultTransformer.test.ts b/public/app/plugins/datasource/tempo/resultTransformer.test.ts index b82a7f500a7..01909d72a34 100644 --- a/public/app/plugins/datasource/tempo/resultTransformer.test.ts +++ b/public/app/plugins/datasource/tempo/resultTransformer.test.ts @@ -1,6 +1,7 @@ import { FieldType, MutableDataFrame } from '@grafana/data'; -import { createTableFrame, transformToOTLP } from './resultTransformer'; -import { otlpDataFrame, otlpResponse } from './testResponse'; +import { createTableFrame, transformToOTLP, transformFromOTLP } from './resultTransformer'; +import { otlpDataFrameToResponse, otlpDataFrameFromResponse, otlpResponse } from './testResponse'; +import { collectorTypes } from '@opentelemetry/exporter-collector'; describe('transformTraceList()', () => { const lokiDataFrame = new MutableDataFrame({ @@ -39,7 +40,17 @@ describe('transformTraceList()', () => { describe('transformToOTLP()', () => { test('transforms dataframe to OTLP format', () => { - const otlp = transformToOTLP(otlpDataFrame); + const otlp = transformToOTLP(otlpDataFrameToResponse); expect(otlp).toMatchObject(otlpResponse); }); }); + +describe('transformFromOTLP()', () => { + test('transforms OTLP format to dataFrame', () => { + const res = transformFromOTLP( + (otlpResponse.batches as unknown) as collectorTypes.opentelemetryProto.trace.v1.ResourceSpans[], + false + ); + expect(res.data[0]).toMatchObject(otlpDataFrameFromResponse); + }); +}); diff --git a/public/app/plugins/datasource/tempo/resultTransformer.ts b/public/app/plugins/datasource/tempo/resultTransformer.ts index 8e034a310af..7a26bf29c37 100644 --- a/public/app/plugins/datasource/tempo/resultTransformer.ts +++ b/public/app/plugins/datasource/tempo/resultTransformer.ts @@ -114,15 +114,25 @@ export function transformTraceList( // Don't forget to change the backend code when the id representation changed function transformBase64IDToHexString(base64: string) { - const buffer = Buffer.from(base64, 'base64'); - const id = buffer.toString('hex'); - return id.length > 16 ? id.slice(16) : id; + const raw = atob(base64); + let result = ''; + for (let i = 0; i < raw.length; i++) { + const hex = raw.charCodeAt(i).toString(16); + result += hex.length === 2 ? hex : '0' + hex; + } + + return result.length > 16 ? result.slice(16) : result; } function transformHexStringToBase64ID(hex: string) { - const buffer = Buffer.from(hex, 'hex'); - const id = buffer.toString('base64'); - return id; + const hexArray = hex.match(/\w{2}/g) || []; + return btoa( + hexArray + .map(function (a) { + return String.fromCharCode(parseInt(a, 16)); + }) + .join('') + ); } function getAttributeValue(value: collectorTypes.opentelemetryProto.common.v1.AnyValue): any { diff --git a/public/app/plugins/datasource/tempo/testResponse.ts b/public/app/plugins/datasource/tempo/testResponse.ts index e8e2710955e..55649879212 100644 --- a/public/app/plugins/datasource/tempo/testResponse.ts +++ b/public/app/plugins/datasource/tempo/testResponse.ts @@ -1849,7 +1849,170 @@ export const bigResponse = new MutableDataFrame({ ], }); -export const otlpDataFrame = new MutableDataFrame({ +export const otlpDataFrameFromResponse = new MutableDataFrame({ + meta: { + preferredVisualisationType: 'trace', + custom: { + traceFormat: 'otlp', + }, + }, + creator: jest.fn(), + fields: [ + { + name: 'traceID', + type: 'string', + config: {}, + labels: undefined, + values: ['60ba2abb44f13eae'], + state: { + displayName: 'traceID', + }, + }, + { + name: 'spanID', + type: 'string', + config: {}, + labels: undefined, + values: ['726b5e30102fc0d0'], + state: { + displayName: 'spanID', + }, + }, + { + name: 'parentSpanID', + type: 'string', + config: {}, + labels: undefined, + values: ['398f0f21a3db99ae'], + state: { + displayName: 'parentSpanID', + }, + }, + { + name: 'operationName', + type: 'string', + config: {}, + labels: undefined, + values: ['HTTP GET - root'], + state: { + displayName: 'operationName', + }, + }, + { + name: 'serviceName', + type: 'string', + config: {}, + labels: undefined, + values: ['db'], + state: { + displayName: 'serviceName', + }, + }, + { + name: 'serviceTags', + type: 'other', + config: {}, + labels: undefined, + values: [ + [ + { + key: 'service.name', + value: 'db', + }, + { + key: 'job', + value: 'tns/db', + }, + { + key: 'opencensus.exporterversion', + value: 'Jaeger-Go-2.22.1', + }, + { + key: 'host.name', + value: '63d16772b4a2', + }, + { + key: 'ip', + value: '0.0.0.0', + }, + { + key: 'client-uuid', + value: '39fb01637a579639', + }, + ], + ], + state: { + displayName: 'serviceTags', + }, + }, + { + name: 'startTime', + type: 'number', + config: {}, + labels: undefined, + values: [1627471657255.809], + state: { + displayName: 'startTime', + }, + }, + { + name: 'duration', + type: 'number', + config: {}, + labels: undefined, + values: [0.459008], + state: { + displayName: 'duration', + }, + }, + { + name: 'logs', + type: 'other', + config: {}, + labels: undefined, + values: [[]], + state: { + displayName: 'logs', + }, + }, + { + name: 'tags', + type: 'other', + config: {}, + labels: undefined, + values: [ + [ + { + key: 'http.status_code', + value: 200, + }, + { + key: 'http.method', + value: 'GET', + }, + { + key: 'http.url', + value: '/', + }, + { + key: 'component', + value: 'net/http', + }, + { + key: 'span.kind', + value: 'producer', + }, + ], + ], + state: { + displayName: 'tags', + }, + }, + ], + length: 1, +} as any); + +export const otlpDataFrameToResponse = new MutableDataFrame({ meta: { preferredVisualisationType: 'trace', custom: {