mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Errors: support errors with frame data from backend responses (#24176)
This commit is contained in:
parent
9e06f9c402
commit
83683d87f8
packages
grafana-data/src/dataframe
grafana-runtime
public/app
@ -1,51 +1,11 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { resultsToDataFrames, grafanaDataFrameToArrowTable, arrowTableToDataFrame } from './ArrowDataFrame';
|
||||
import { grafanaDataFrameToArrowTable, arrowTableToDataFrame } from './ArrowDataFrame';
|
||||
import { toDataFrameDTO, toDataFrame } from './processDataFrame';
|
||||
import { FieldType } from '../types';
|
||||
import { Table } from 'apache-arrow';
|
||||
|
||||
/* eslint-disable */
|
||||
const resp = {
|
||||
results: {
|
||||
'': {
|
||||
refId: '',
|
||||
dataframes: [
|
||||
'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',
|
||||
],
|
||||
series: [] as any[],
|
||||
tables: null as any,
|
||||
frames: null as any,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
describe('GEL Utils', () => {
|
||||
test('should parse output with dataframe', () => {
|
||||
const frames = resultsToDataFrames(resp);
|
||||
for (const frame of frames) {
|
||||
console.log('Frame', frame.refId);
|
||||
for (const field of frame.fields) {
|
||||
console.log(' > ', field.name, field.labels);
|
||||
console.log(' (values)= ', field.values.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
const norm = frames.map(f => toDataFrameDTO(f));
|
||||
expect(norm).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('processEmptyResults', () => {
|
||||
const frames = resultsToDataFrames({
|
||||
results: { '': { refId: '', meta: null, series: null, tables: null, dataframes: null } },
|
||||
});
|
||||
expect(frames.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read/Write arrow Table to DataFrame', () => {
|
||||
test('should parse output with dataframe', () => {
|
||||
const frame = toDataFrame({
|
||||
|
@ -161,20 +161,3 @@ export function grafanaDataFrameToArrowTable(data: DataFrame): Table {
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
export function resultsToDataFrames(rsp: any): DataFrame[] {
|
||||
if (rsp === undefined || rsp.results === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const results = rsp.results as Array<{ dataframes: string[] }>;
|
||||
const frames: DataFrame[] = Object.values(results).flatMap(res => {
|
||||
if (!res.dataframes) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return res.dataframes.map((b: string) => arrowTableToDataFrame(base64StringToArrowTable(b)));
|
||||
});
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
@ -1,108 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GEL Utils should parse output with dataframe 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "Time",
|
||||
"type": "time",
|
||||
"values": Array [
|
||||
1569334575000,
|
||||
1569334580000,
|
||||
1569334585000,
|
||||
1569334590000,
|
||||
1569334595000,
|
||||
1569334600000,
|
||||
1569334605000,
|
||||
1569334610000,
|
||||
1569334615000,
|
||||
1569334620000,
|
||||
1569334625000,
|
||||
1569334630000,
|
||||
1569334635000,
|
||||
],
|
||||
},
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "",
|
||||
"type": "number",
|
||||
"values": Array [
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
3,
|
||||
],
|
||||
},
|
||||
],
|
||||
"meta": undefined,
|
||||
"name": undefined,
|
||||
"refId": "GC",
|
||||
},
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "Time",
|
||||
"type": "time",
|
||||
"values": Array [
|
||||
1569334575000,
|
||||
1569334580000,
|
||||
1569334585000,
|
||||
1569334590000,
|
||||
1569334595000,
|
||||
1569334600000,
|
||||
1569334605000,
|
||||
1569334610000,
|
||||
1569334615000,
|
||||
1569334620000,
|
||||
1569334625000,
|
||||
1569334630000,
|
||||
1569334635000,
|
||||
],
|
||||
},
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "GB-series",
|
||||
"type": "number",
|
||||
"values": Array [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
],
|
||||
},
|
||||
],
|
||||
"meta": undefined,
|
||||
"name": undefined,
|
||||
"refId": "GB",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Read/Write arrow Table to DataFrame should read all types 1`] = `
|
||||
Object {
|
||||
"fields": Array [
|
||||
|
@ -34,6 +34,7 @@
|
||||
"@rollup/plugin-node-resolve": "7.1.1",
|
||||
"@types/rollup-plugin-visualizer": "2.6.0",
|
||||
"@types/systemjs": "^0.20.6",
|
||||
"@types/jest": "23.3.14",
|
||||
"lodash": "4.17.15",
|
||||
"pretty-format": "25.1.0",
|
||||
"rollup": "2.0.6",
|
||||
|
@ -9,3 +9,4 @@ export * from './types';
|
||||
export { loadPluginCss, SystemJS, PluginCssOptions } from './utils/plugin';
|
||||
export { reportMetaAnalytics } from './utils/analytics';
|
||||
export { DataSourceWithBackend, HealthCheckResult, HealthStatus } from './utils/DataSourceWithBackend';
|
||||
export { toDataQueryError, toDataQueryResponse } from './utils/queryResponse';
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { config } from '..';
|
||||
import { getBackendSrv } from '../services';
|
||||
import { toDataQueryResponse } from './queryResponse';
|
||||
|
||||
const ExpressionDatasourceID = '__expr__';
|
||||
|
||||
@ -94,7 +95,11 @@ export class DataSourceWithBackend<
|
||||
requestId,
|
||||
})
|
||||
.then((rsp: any) => {
|
||||
return this.toDataQueryResponse(rsp?.data);
|
||||
return toDataQueryResponse(rsp);
|
||||
})
|
||||
.catch(err => {
|
||||
err.isHandled = true; // Avoid extra popup warning
|
||||
return toDataQueryResponse(err);
|
||||
});
|
||||
|
||||
return from(req);
|
||||
@ -109,16 +114,6 @@ export class DataSourceWithBackend<
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* This makes the arrow library loading async.
|
||||
*/
|
||||
async toDataQueryResponse(rsp: any): Promise<DataQueryResponse> {
|
||||
const { resultsToDataFrames } = await import(
|
||||
/* webpackChunkName: "apache-arrow-util" */ '@grafana/data/src/dataframe/ArrowDataFrame'
|
||||
);
|
||||
return { data: resultsToDataFrames(rsp) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a GET request to the datasource resource path
|
||||
*/
|
||||
|
165
packages/grafana-runtime/src/utils/queryResponse.test.ts
Normal file
165
packages/grafana-runtime/src/utils/queryResponse.test.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import { toDataFrameDTO } from '@grafana/data';
|
||||
|
||||
import { toDataQueryResponse } from './queryResponse';
|
||||
|
||||
/* eslint-disable */
|
||||
const resp = {
|
||||
data: {
|
||||
results: {
|
||||
GC: {
|
||||
dataframes: [
|
||||
'QVJST1cxAACsAQAAEAAAAAAACgAOAAwACwAEAAoAAAAUAAAAAAAAAQMACgAMAAAACAAEAAoAAAAIAAAAUAAAAAIAAAAoAAAABAAAAOD+//8IAAAADAAAAAIAAABHQwAABQAAAHJlZklkAAAAAP///wgAAAAMAAAAAAAAAAAAAAAEAAAAbmFtZQAAAAACAAAAlAAAAAQAAACG////FAAAAGAAAABgAAAAAAADAWAAAAACAAAALAAAAAQAAABQ////CAAAABAAAAAGAAAAbnVtYmVyAAAEAAAAdHlwZQAAAAB0////CAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAAAAAABm////AAACAAAAAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAbAAAAHQAAAAAAAoBdAAAAAIAAAA0AAAABAAAANz///8IAAAAEAAAAAQAAAB0aW1lAAAAAAQAAAB0eXBlAAAAAAgADAAIAAQACAAAAAgAAAAQAAAABAAAAFRpbWUAAAAABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAwAEAAAAVGltZQAAAAC8AAAAFAAAAAAAAAAMABYAFAATAAwABAAMAAAA0AAAAAAAAAAUAAAAAAAAAwMACgAYAAwACAAEAAoAAAAUAAAAWAAAAA0AAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoAAAAAAAAAGgAAAAAAAAAAAAAAAAAAABoAAAAAAAAAGgAAAAAAAAAAAAAAAIAAAANAAAAAAAAAAAAAAAAAAAADQAAAAAAAAAAAAAAAAAAAAAAAAAAFp00e2XHFQAIo158ZccVAPqoiH1lxxUA7K6yfmXHFQDetNx/ZccVANC6BoFlxxUAwsAwgmXHFQC0xlqDZccVAKbMhIRlxxUAmNKuhWXHFQCK2NiGZccVAHzeAohlxxUAbuQsiWXHFQAAAAAAAAhAAAAAAAAACEAAAAAAAAAIQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAIQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAAAhAEAAAAAwAFAASAAwACAAEAAwAAAAQAAAALAAAADgAAAAAAAMAAQAAALgBAAAAAAAAwAAAAAAAAADQAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAABQAAAAAgAAACgAAAAEAAAA4P7//wgAAAAMAAAAAgAAAEdDAAAFAAAAcmVmSWQAAAAA////CAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAAIAAACUAAAABAAAAIb///8UAAAAYAAAAGAAAAAAAAMBYAAAAAIAAAAsAAAABAAAAFD///8IAAAAEAAAAAYAAABudW1iZXIAAAQAAAB0eXBlAAAAAHT///8IAAAADAAAAAAAAAAAAAAABAAAAG5hbWUAAAAAAAAAAGb///8AAAIAAAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAABsAAAAdAAAAAAACgF0AAAAAgAAADQAAAAEAAAA3P///wgAAAAQAAAABAAAAHRpbWUAAAAABAAAAHR5cGUAAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAANgBAABBUlJPVzE=',
|
||||
],
|
||||
frames: null as any,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resWithError = {
|
||||
data: {
|
||||
results: {
|
||||
A: {
|
||||
error: 'Hello Error',
|
||||
series: null,
|
||||
tables: null,
|
||||
dataframes: [
|
||||
'QVJST1cxAAD/////WAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAJwAAAADAAAATAAAACgAAAAEAAAAPP///wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAABc////CAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAHz///8IAAAANAAAACoAAAB7Im5vdGljZXMiOlt7InNldmVyaXR5IjoyLCJ0ZXh0IjoiVGV4dCJ9XX0AAAQAAABtZXRhAAAAAAEAAAAYAAAAAAASABgAFAAAABMADAAAAAgABAASAAAAFAAAAEQAAABMAAAAAAAAA0wAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAQAAAABwAAAG51bWJlcnMABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAgAHAAAAbnVtYmVycwAAAAAA/////4gAAAAUAAAAAAAAAAwAFgAUABMADAAEAAwAAAAQAAAAAAAAABQAAAAAAAADAwAKABgADAAIAAQACgAAABQAAAA4AAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAIQBAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA4AAAAAAADAAEAAABoAQAAAAAAAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAnAAAAAMAAABMAAAAKAAAAAQAAAA8////CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAFz///8IAAAADAAAAAAAAAAAAAAABAAAAG5hbWUAAAAAfP///wgAAAA0AAAAKgAAAHsibm90aWNlcyI6W3sic2V2ZXJpdHkiOjIsInRleHQiOiJUZXh0In1dfQAABAAAAG1ldGEAAAAAAQAAABgAAAAAABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAADTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAHAAAAbnVtYmVycwAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAACAAcAAABudW1iZXJzAIABAABBUlJPVzE=',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const emptyResults = {
|
||||
data: { '': { refId: '', meta: null, series: null, tables: null, dataframes: null } },
|
||||
};
|
||||
|
||||
/* eslint-enable */
|
||||
|
||||
describe('GEL Utils', () => {
|
||||
test('should parse output with dataframe', () => {
|
||||
const res = toDataQueryResponse(resp);
|
||||
const frames = res.data;
|
||||
for (const frame of frames) {
|
||||
expect(frame.refId).toEqual('GC');
|
||||
}
|
||||
|
||||
const norm = frames.map(f => toDataFrameDTO(f));
|
||||
expect(norm).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "Time",
|
||||
"type": "time",
|
||||
"values": Array [
|
||||
1569334575000,
|
||||
1569334580000,
|
||||
1569334585000,
|
||||
1569334590000,
|
||||
1569334595000,
|
||||
1569334600000,
|
||||
1569334605000,
|
||||
1569334610000,
|
||||
1569334615000,
|
||||
1569334620000,
|
||||
1569334625000,
|
||||
1569334630000,
|
||||
1569334635000,
|
||||
],
|
||||
},
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "",
|
||||
"type": "number",
|
||||
"values": Array [
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
3,
|
||||
],
|
||||
},
|
||||
],
|
||||
"meta": undefined,
|
||||
"name": undefined,
|
||||
"refId": "GC",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('processEmptyResults', () => {
|
||||
const frames = toDataQueryResponse(emptyResults).data;
|
||||
expect(frames.length).toEqual(0);
|
||||
});
|
||||
|
||||
test('resultWithError', () => {
|
||||
// Generated from:
|
||||
// qdr.Responses[q.GetRefID()] = backend.DataResponse{
|
||||
// Error: fmt.Errorf("an Error: %w", fmt.Errorf("another error")),
|
||||
// Frames: data.Frames{
|
||||
// {
|
||||
// Fields: data.Fields{data.NewField("numbers", nil, []float64{1, 3})},
|
||||
// Meta: &data.FrameMeta{
|
||||
// Notices: []data.Notice{
|
||||
// {
|
||||
// Severity: data.NoticeSeverityError,
|
||||
// Text: "Text",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
const res = toDataQueryResponse(resWithError);
|
||||
expect(res.error).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"message": "Hello Error",
|
||||
"refId": "A",
|
||||
}
|
||||
`);
|
||||
|
||||
const norm = res.data.map(f => toDataFrameDTO(f));
|
||||
expect(norm).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"config": Object {},
|
||||
"labels": undefined,
|
||||
"name": "numbers",
|
||||
"type": "number",
|
||||
"values": Array [
|
||||
1,
|
||||
3,
|
||||
],
|
||||
},
|
||||
],
|
||||
"meta": Object {
|
||||
"notices": Array [
|
||||
Object {
|
||||
"severity": 2,
|
||||
"text": "Text",
|
||||
},
|
||||
],
|
||||
},
|
||||
"name": undefined,
|
||||
"refId": "A",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
91
packages/grafana-runtime/src/utils/queryResponse.ts
Normal file
91
packages/grafana-runtime/src/utils/queryResponse.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {
|
||||
DataQueryResponse,
|
||||
arrowTableToDataFrame,
|
||||
base64StringToArrowTable,
|
||||
KeyValue,
|
||||
LoadingState,
|
||||
DataQueryError,
|
||||
} from '@grafana/data';
|
||||
|
||||
interface DataResponse {
|
||||
error?: string;
|
||||
refId?: string;
|
||||
dataframes?: string[];
|
||||
// series: null,
|
||||
// tables: null,
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the results from `/api/ds/query
|
||||
*/
|
||||
export function toDataQueryResponse(res: any): DataQueryResponse {
|
||||
const rsp: DataQueryResponse = { data: [], state: LoadingState.Done };
|
||||
if (res.data?.results) {
|
||||
const results: KeyValue = res.data.results;
|
||||
for (const refId of Object.keys(results)) {
|
||||
const dr = results[refId] as DataResponse;
|
||||
if (dr) {
|
||||
if (dr.error) {
|
||||
if (!rsp.error) {
|
||||
rsp.error = {
|
||||
refId,
|
||||
message: dr.error,
|
||||
};
|
||||
rsp.state = LoadingState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
if (dr.dataframes) {
|
||||
for (const b64 of dr.dataframes) {
|
||||
const t = base64StringToArrowTable(b64);
|
||||
const f = arrowTableToDataFrame(t);
|
||||
if (!f.refId) {
|
||||
f.refId = refId;
|
||||
}
|
||||
rsp.data.push(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When it is not an OK response, make sure the error gets added
|
||||
if (res.status && res.status !== 200) {
|
||||
if (rsp.state !== LoadingState.Error) {
|
||||
rsp.state = LoadingState.Error;
|
||||
}
|
||||
if (!rsp.error) {
|
||||
rsp.error = toDataQueryError(res);
|
||||
}
|
||||
}
|
||||
|
||||
return rsp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an object into a DataQueryError -- if this is an HTTP response,
|
||||
* it will put the correct values in the error filds
|
||||
*/
|
||||
export function toDataQueryError(err: any): DataQueryError {
|
||||
const error = (err || {}) as DataQueryError;
|
||||
|
||||
if (!error.message) {
|
||||
if (typeof err === 'string' || err instanceof String) {
|
||||
return { message: err } as DataQueryError;
|
||||
}
|
||||
|
||||
let message = 'Query error';
|
||||
if (error.message) {
|
||||
message = error.message;
|
||||
} else if (error.data && error.data.message) {
|
||||
message = error.data.message;
|
||||
} else if (error.data && error.data.error) {
|
||||
message = error.data.error;
|
||||
} else if (error.status) {
|
||||
message = `Query error: ${error.status} ${error.statusText}`;
|
||||
}
|
||||
error.message = message;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
@ -18,6 +18,7 @@ import {
|
||||
DataFrame,
|
||||
guessFieldTypes,
|
||||
} from '@grafana/data';
|
||||
import { toDataQueryError } from '@grafana/runtime';
|
||||
import { emitDataRequestEvent } from './analyticsProcessor';
|
||||
import { ExpressionDatasourceID, expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
|
||||
|
||||
@ -117,7 +118,7 @@ export function runRequest(datasource: DataSourceApi, request: DataQueryRequest)
|
||||
of({
|
||||
...state.panelData,
|
||||
state: LoadingState.Error,
|
||||
error: processQueryError(err),
|
||||
error: toDataQueryError(err),
|
||||
})
|
||||
),
|
||||
tap(emitDataRequestEvent(datasource)),
|
||||
@ -153,30 +154,6 @@ export function callQueryMethod(datasource: DataSourceApi, request: DataQueryReq
|
||||
return from(returnVal);
|
||||
}
|
||||
|
||||
export function processQueryError(err: any): DataQueryError {
|
||||
const error = (err || {}) as DataQueryError;
|
||||
|
||||
if (!error.message) {
|
||||
if (typeof err === 'string' || err instanceof String) {
|
||||
return { message: err } as DataQueryError;
|
||||
}
|
||||
|
||||
let message = 'Query error';
|
||||
if (error.message) {
|
||||
message = error.message;
|
||||
} else if (error.data && error.data.message) {
|
||||
message = error.data.message;
|
||||
} else if (error.data && error.data.error) {
|
||||
message = error.data.error;
|
||||
} else if (error.status) {
|
||||
message = `Query error: ${error.status} ${error.statusText}`;
|
||||
}
|
||||
error.message = message;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* All panels will be passed tables that have our best guess at colum type set
|
||||
*
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
ScopedVars,
|
||||
TimeRange,
|
||||
DataFrame,
|
||||
resultsToDataFrames,
|
||||
DataQueryResponse,
|
||||
LoadingState,
|
||||
toDataFrame,
|
||||
@ -22,7 +21,7 @@ import {
|
||||
FieldType,
|
||||
LogRowModel,
|
||||
} from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { getBackendSrv, toDataQueryResponse } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { ThrottlingErrorMessage } from './components/ThrottlingErrorMessage';
|
||||
@ -496,6 +495,12 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
);
|
||||
}
|
||||
|
||||
const resultsToDataFrames = (val: any): DataFrame[] => {
|
||||
// NOTE: this function currently only processes binary results from:
|
||||
// /api/ds/query -- it will retrun empty results most of the time
|
||||
return toDataQueryResponse(val).data || [];
|
||||
};
|
||||
|
||||
return from(this.awsRequest(TSDB_QUERY_ENDPOINT, requestParams)).pipe(
|
||||
map(response => resultsToDataFrames(response)),
|
||||
catchError(err => {
|
||||
|
@ -14,13 +14,12 @@ import {
|
||||
DataFrame,
|
||||
} from '@grafana/data';
|
||||
import { Scenario, TestDataQuery } from './types';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { getBackendSrv, toDataQueryError } from '@grafana/runtime';
|
||||
import { queryMetricTree } from './metricTree';
|
||||
import { from, merge, Observable, of } from 'rxjs';
|
||||
import { runStream } from './runStreams';
|
||||
import templateSrv from 'app/features/templating/template_srv';
|
||||
import { getSearchFilterScopedVar } from 'app/features/templating/utils';
|
||||
import { processQueryError } from 'app/features/dashboard/state/runRequest';
|
||||
|
||||
type TestData = TimeSeries | TableData;
|
||||
|
||||
@ -164,7 +163,7 @@ function runArrowFile(target: TestDataQuery, req: DataQueryRequest<TestDataQuery
|
||||
data = [arrowTableToDataFrame(table)];
|
||||
} catch (e) {
|
||||
console.warn('Error reading saved arrow', e);
|
||||
const error = processQueryError(e);
|
||||
const error = toDataQueryError(e);
|
||||
error.refId = target.refId;
|
||||
return of({ state: LoadingState.Error, error, data });
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user