Plugin query API: Handle multiple errors (#63553)

This commit is contained in:
Andres Martinez Gotor 2023-03-02 11:25:50 +01:00 committed by GitHub
parent b211ec0a1d
commit c84cfd2b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 46 additions and 9 deletions

View File

@ -451,9 +451,15 @@ export interface DataQueryResponse {
/**
* Optionally include error info along with the response data
* @deprecated use errors instead -- will be removed in Grafana 10+
*/
error?: DataQueryError;
/**
* Optionally include multiple errors for different targets
*/
errors?: DataQueryError[];
/**
* Use this to control which state the response should have
* Defaults to LoadingState.Done if state is not defined

View File

@ -57,6 +57,11 @@ export interface PanelData {
timings?: DataQueryTimings;
/** Any query errors */
errors?: DataQueryError[];
/**
* Single error for legacy reasons
* @deprecated use errors instead -- will be removed in Grafana 10+
*/
error?: DataQueryError;
/** Contains the range from the request or a shifted time range if a request uses relative time */

View File

@ -356,6 +356,12 @@ describe('Query Response parser', () => {
"refId": "A",
}
`);
expect(res.errors).toEqual([
{
message: 'Hello Error',
refId: 'A',
},
]);
const norm = res.data.map((f) => toDataFrameDTO(f));
expect(norm).toMatchInlineSnapshot(`

View File

@ -87,8 +87,13 @@ export function toDataQueryResponse(
refId: dr.refId,
message: dr.error,
};
rsp.state = LoadingState.Error;
}
if (rsp.errors) {
rsp.errors.push({ refId: dr.refId, message: dr.error });
} else {
rsp.errors = [{ refId: dr.refId, message: dr.error }];
}
rsp.state = LoadingState.Error;
}
if (dr.frames?.length) {

View File

@ -21,10 +21,12 @@ describe('filterPanelDataToQuery', () => {
toDataFrame({ refId: 'B', fields: [{ name: 'B333' }], meta: {} }),
toDataFrame({ refId: 'C', fields: [{ name: 'CCCC' }], meta: { requestId: 'sub3' } }),
],
error: {
errors: [
{
refId: 'B',
message: 'Error!!',
},
],
request: makePretendRequest('111', [
makePretendRequest('sub1'),
makePretendRequest('sub2'),
@ -38,6 +40,7 @@ describe('filterPanelDataToQuery', () => {
expect(panelData?.series.length).toBe(1);
expect(panelData?.series[0].refId).toBe('A');
expect(panelData?.error).toBeUndefined();
expect(panelData?.errors).toBeUndefined();
});
it('should match the error to the query', () => {
@ -45,6 +48,7 @@ describe('filterPanelDataToQuery', () => {
expect(panelData?.series.length).toBe(3);
expect(panelData?.series[0].refId).toBe('B');
expect(panelData?.error!.refId).toBe('B');
expect(panelData?.errors![0].refId).toBe('B');
});
it('should include errors when missing data', () => {
@ -53,12 +57,14 @@ describe('filterPanelDataToQuery', () => {
error: {
message: 'Error!!',
},
errors: [{ message: 'Error!!' }],
} as unknown as PanelData;
const panelData = filterPanelDataToQuery(withError, 'B');
expect(panelData).toBeDefined();
expect(panelData?.state).toBe(LoadingState.Error);
expect(panelData?.error).toBe(withError.error);
expect(panelData?.errors).toEqual(withError.errors);
});
it('should set the state to done if the frame has no errors', () => {
@ -91,6 +97,7 @@ describe('filterPanelDataToQuery', () => {
const panelDataB = filterPanelDataToQuery(withError, 'Q');
expect(panelDataB?.series.length).toBe(0);
expect(panelDataB?.error?.refId).toBe('Q');
expect(panelDataB?.errors![0].refId).toBe('Q');
});
it('should not set the state to done if the frame is loading and has no errors', () => {

View File

@ -558,7 +558,7 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat
const series = data.series.filter((series) => series.refId === refId);
// If there was an error with no data and the panel is not in a loading state, pass it to the QueryEditors
if (data.state !== LoadingState.Loading && data.error && !data.series.length) {
if (data.state !== LoadingState.Loading && (data.error || data.errors?.length) && !data.series.length) {
return {
...data,
state: LoadingState.Error,
@ -567,7 +567,11 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat
// Only say this is an error if the error links to the query
let state = data.state;
const error = data.error && data.error.refId === refId ? data.error : undefined;
let error = data.errors?.find((e) => e.refId === refId);
if (!error && data.error) {
error = data.error.refId === refId ? data.error : undefined;
}
if (state !== LoadingState.Loading) {
if (error) {
state = LoadingState.Error;
@ -583,6 +587,7 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat
state,
series,
error,
errors: error ? [error] : undefined,
timeRange,
};
}

View File

@ -53,6 +53,7 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
let loadingState = packet.state || LoadingState.Done;
let error: DataQueryError | undefined = undefined;
let errors: DataQueryError[] | undefined = undefined;
const series: DataQueryResponseData[] = [];
const annotations: DataQueryResponseData[] = [];
@ -60,9 +61,10 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
for (const key in packets) {
const packet = packets[key];
if (packet.error) {
if (packet.error || packet.errors?.length) {
loadingState = LoadingState.Error;
error = packet.error;
errors = packet.errors;
}
if (packet.data && packet.data.length) {
@ -79,11 +81,12 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
const timeRange = getRequestTimeRange(request, loadingState);
const panelData = {
const panelData: PanelData = {
state: loadingState,
series,
annotations,
error,
errors,
request,
timeRange,
};