Commit 83683d87 by Ryan McKinley Committed by GitHub

Errors: support errors with frame data from backend responses (#24176)

parent 9e06f9c4
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;
}
// 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);
......@@ -110,16 +115,6 @@ export class DataSourceWithBackend<
}
/**
* 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
*/
async getResource(path: string, params?: any): Promise<any> {
......
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",
},
]
`);
});
});
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 });
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment