diff --git a/public/app/plugins/datasource/jaeger/datasource.test.ts b/public/app/plugins/datasource/jaeger/datasource.test.ts index 85cbc37c758..4a33ec28c20 100644 --- a/public/app/plugins/datasource/jaeger/datasource.test.ts +++ b/public/app/plugins/datasource/jaeger/datasource.test.ts @@ -89,6 +89,18 @@ describe('JaegerDatasource', () => { expect(field.values.length).toBe(2); }); + it('should fail on invalid json file upload', async () => { + const ds = new JaegerDatasource(defaultSettings); + ds.uploadedJson = JSON.stringify({ key: 'value', arr: [] }); + const response = await lastValueFrom( + ds.query({ + targets: [{ queryType: 'upload', refId: 'A' }], + } as any) + ); + expect(response.error?.message).toBeDefined(); + expect(response.data.length).toBe(0); + }); + it('should return search results when the query type is search', async () => { const mock = setupFetchMock({ data: [testResponse] }); const ds = new JaegerDatasource(defaultSettings, timeSrvStub); diff --git a/public/app/plugins/datasource/jaeger/datasource.ts b/public/app/plugins/datasource/jaeger/datasource.ts index 5975d57b533..b1fccb96792 100644 --- a/public/app/plugins/datasource/jaeger/datasource.ts +++ b/public/app/plugins/datasource/jaeger/datasource.ts @@ -58,8 +58,13 @@ export class JaegerDatasource extends DataSourceApi { if (!this.uploadedJson) { return of({ data: [] }); } - const traceData = JSON.parse(this.uploadedJson as string).data[0]; - return of({ data: [createTraceFrame(traceData), ...createGraphFrames(traceData)] }); + + try { + const traceData = JSON.parse(this.uploadedJson as string).data[0]; + return of({ data: [createTraceFrame(traceData), ...createGraphFrames(traceData)] }); + } catch (error) { + return of({ error: { message: 'JSON is not valid Jaeger format' }, data: [] }); + } } let jaegerQuery = pick(target, ['operation', 'service', 'tags', 'minDuration', 'maxDuration', 'limit']); diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index 805597f9465..d7ff615fa98 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -119,6 +119,18 @@ describe('Tempo data source', () => { expect(field.values.length).toBe(6); }); + it('should fail on invalid json file upload', async () => { + const ds = new TempoDatasource(defaultSettings); + ds.uploadedJson = JSON.stringify(mockInvalidJson); + const response = await lastValueFrom( + ds.query({ + targets: [{ queryType: 'upload', refId: 'A' }], + } as any) + ); + expect(response.error?.message).toBeDefined(); + expect(response.data.length).toBe(0); + }); + it('should build search query correctly', () => { const ds = new TempoDatasource(defaultSettings); const tempoQuery: TempoQuery = { @@ -229,3 +241,36 @@ const secondsPromMetric = new MutableDataFrame({ { name: 'Value #tempo_service_graph_request_server_seconds_sum', values: [10, 40] }, ], }); + +const mockInvalidJson = { + batches: [ + { + resource: { + attributes: [], + }, + instrumentation_library_spans: [ + { + instrumentation_library: {}, + spans: [ + { + trace_id: 'AAAAAAAAAABguiq7RPE+rg==', + span_id: 'cmteMBAvwNA=', + parentSpanId: 'OY8PIaPbma4=', + name: 'HTTP GET - root', + kind: 'SPAN_KIND_SERVER', + startTimeUnixNano: '1627471657255809000', + endTimeUnixNano: '1627471657256268000', + attributes: [ + { key: 'http.status_code', value: { intValue: '200' } }, + { key: 'http.method', value: { stringValue: 'GET' } }, + { key: 'http.url', value: { stringValue: '/' } }, + { key: 'component', value: { stringValue: 'net/http' } }, + ], + status: {}, + }, + ], + }, + ], + }, + ], +}; diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index 61d9c5f9bca..29325c471a7 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -115,7 +115,7 @@ export class TempoDatasource extends DataSourceWithBackend { expect(field.type).toBe(FieldType.string); expect(field.values.length).toBe(3); }); + + it('should fail on invalid json file upload', async () => { + const ds = new ZipkinDatasource(defaultSettings); + ds.uploadedJson = JSON.stringify({ key: 'value', arr: [] }); + const response = await lastValueFrom( + ds.query({ + targets: [{ queryType: 'upload', refId: 'A' }], + } as any) + ); + expect(response.error?.message).toBeDefined(); + expect(response.data.length).toBe(0); + }); }); describe('metadataRequest', () => { diff --git a/public/app/plugins/datasource/zipkin/datasource.ts b/public/app/plugins/datasource/zipkin/datasource.ts index cc839d95ee8..2a3bca07b13 100644 --- a/public/app/plugins/datasource/zipkin/datasource.ts +++ b/public/app/plugins/datasource/zipkin/datasource.ts @@ -28,8 +28,13 @@ export class ZipkinDatasource extends DataSourceApi { if (!this.uploadedJson) { return of({ data: [] }); } - const traceData = JSON.parse(this.uploadedJson as string); - return of(responseToDataQueryResponse({ data: traceData })); + + try { + const traceData = JSON.parse(this.uploadedJson as string); + return of(responseToDataQueryResponse({ data: traceData })); + } catch (error) { + return of({ error: { message: 'JSON is not valid Zipkin format' }, data: [] }); + } } if (target.query) {