mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugin query API: Handle multiple errors (#63553)
This commit is contained in:
parent
b211ec0a1d
commit
c84cfd2b3f
@ -451,9 +451,15 @@ export interface DataQueryResponse {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally include error info along with the response data
|
* Optionally include error info along with the response data
|
||||||
|
* @deprecated use errors instead -- will be removed in Grafana 10+
|
||||||
*/
|
*/
|
||||||
error?: DataQueryError;
|
error?: DataQueryError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally include multiple errors for different targets
|
||||||
|
*/
|
||||||
|
errors?: DataQueryError[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this to control which state the response should have
|
* Use this to control which state the response should have
|
||||||
* Defaults to LoadingState.Done if state is not defined
|
* Defaults to LoadingState.Done if state is not defined
|
||||||
|
@ -57,6 +57,11 @@ export interface PanelData {
|
|||||||
timings?: DataQueryTimings;
|
timings?: DataQueryTimings;
|
||||||
|
|
||||||
/** Any query errors */
|
/** Any query errors */
|
||||||
|
errors?: DataQueryError[];
|
||||||
|
/**
|
||||||
|
* Single error for legacy reasons
|
||||||
|
* @deprecated use errors instead -- will be removed in Grafana 10+
|
||||||
|
*/
|
||||||
error?: DataQueryError;
|
error?: DataQueryError;
|
||||||
|
|
||||||
/** Contains the range from the request or a shifted time range if a request uses relative time */
|
/** Contains the range from the request or a shifted time range if a request uses relative time */
|
||||||
|
@ -356,6 +356,12 @@ describe('Query Response parser', () => {
|
|||||||
"refId": "A",
|
"refId": "A",
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
expect(res.errors).toEqual([
|
||||||
|
{
|
||||||
|
message: 'Hello Error',
|
||||||
|
refId: 'A',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const norm = res.data.map((f) => toDataFrameDTO(f));
|
const norm = res.data.map((f) => toDataFrameDTO(f));
|
||||||
expect(norm).toMatchInlineSnapshot(`
|
expect(norm).toMatchInlineSnapshot(`
|
||||||
|
@ -87,8 +87,13 @@ export function toDataQueryResponse(
|
|||||||
refId: dr.refId,
|
refId: dr.refId,
|
||||||
message: dr.error,
|
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) {
|
if (dr.frames?.length) {
|
||||||
|
@ -21,10 +21,12 @@ describe('filterPanelDataToQuery', () => {
|
|||||||
toDataFrame({ refId: 'B', fields: [{ name: 'B333' }], meta: {} }),
|
toDataFrame({ refId: 'B', fields: [{ name: 'B333' }], meta: {} }),
|
||||||
toDataFrame({ refId: 'C', fields: [{ name: 'CCCC' }], meta: { requestId: 'sub3' } }),
|
toDataFrame({ refId: 'C', fields: [{ name: 'CCCC' }], meta: { requestId: 'sub3' } }),
|
||||||
],
|
],
|
||||||
error: {
|
errors: [
|
||||||
refId: 'B',
|
{
|
||||||
message: 'Error!!',
|
refId: 'B',
|
||||||
},
|
message: 'Error!!',
|
||||||
|
},
|
||||||
|
],
|
||||||
request: makePretendRequest('111', [
|
request: makePretendRequest('111', [
|
||||||
makePretendRequest('sub1'),
|
makePretendRequest('sub1'),
|
||||||
makePretendRequest('sub2'),
|
makePretendRequest('sub2'),
|
||||||
@ -38,6 +40,7 @@ describe('filterPanelDataToQuery', () => {
|
|||||||
expect(panelData?.series.length).toBe(1);
|
expect(panelData?.series.length).toBe(1);
|
||||||
expect(panelData?.series[0].refId).toBe('A');
|
expect(panelData?.series[0].refId).toBe('A');
|
||||||
expect(panelData?.error).toBeUndefined();
|
expect(panelData?.error).toBeUndefined();
|
||||||
|
expect(panelData?.errors).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match the error to the query', () => {
|
it('should match the error to the query', () => {
|
||||||
@ -45,6 +48,7 @@ describe('filterPanelDataToQuery', () => {
|
|||||||
expect(panelData?.series.length).toBe(3);
|
expect(panelData?.series.length).toBe(3);
|
||||||
expect(panelData?.series[0].refId).toBe('B');
|
expect(panelData?.series[0].refId).toBe('B');
|
||||||
expect(panelData?.error!.refId).toBe('B');
|
expect(panelData?.error!.refId).toBe('B');
|
||||||
|
expect(panelData?.errors![0].refId).toBe('B');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include errors when missing data', () => {
|
it('should include errors when missing data', () => {
|
||||||
@ -53,12 +57,14 @@ describe('filterPanelDataToQuery', () => {
|
|||||||
error: {
|
error: {
|
||||||
message: 'Error!!',
|
message: 'Error!!',
|
||||||
},
|
},
|
||||||
|
errors: [{ message: 'Error!!' }],
|
||||||
} as unknown as PanelData;
|
} as unknown as PanelData;
|
||||||
|
|
||||||
const panelData = filterPanelDataToQuery(withError, 'B');
|
const panelData = filterPanelDataToQuery(withError, 'B');
|
||||||
expect(panelData).toBeDefined();
|
expect(panelData).toBeDefined();
|
||||||
expect(panelData?.state).toBe(LoadingState.Error);
|
expect(panelData?.state).toBe(LoadingState.Error);
|
||||||
expect(panelData?.error).toBe(withError.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', () => {
|
it('should set the state to done if the frame has no errors', () => {
|
||||||
@ -91,6 +97,7 @@ describe('filterPanelDataToQuery', () => {
|
|||||||
const panelDataB = filterPanelDataToQuery(withError, 'Q');
|
const panelDataB = filterPanelDataToQuery(withError, 'Q');
|
||||||
expect(panelDataB?.series.length).toBe(0);
|
expect(panelDataB?.series.length).toBe(0);
|
||||||
expect(panelDataB?.error?.refId).toBe('Q');
|
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', () => {
|
it('should not set the state to done if the frame is loading and has no errors', () => {
|
||||||
|
@ -558,7 +558,7 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat
|
|||||||
const series = data.series.filter((series) => series.refId === refId);
|
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 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 {
|
return {
|
||||||
...data,
|
...data,
|
||||||
state: LoadingState.Error,
|
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
|
// Only say this is an error if the error links to the query
|
||||||
let state = data.state;
|
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 (state !== LoadingState.Loading) {
|
||||||
if (error) {
|
if (error) {
|
||||||
state = LoadingState.Error;
|
state = LoadingState.Error;
|
||||||
@ -583,6 +587,7 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat
|
|||||||
state,
|
state,
|
||||||
series,
|
series,
|
||||||
error,
|
error,
|
||||||
|
errors: error ? [error] : undefined,
|
||||||
timeRange,
|
timeRange,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
|
|||||||
|
|
||||||
let loadingState = packet.state || LoadingState.Done;
|
let loadingState = packet.state || LoadingState.Done;
|
||||||
let error: DataQueryError | undefined = undefined;
|
let error: DataQueryError | undefined = undefined;
|
||||||
|
let errors: DataQueryError[] | undefined = undefined;
|
||||||
|
|
||||||
const series: DataQueryResponseData[] = [];
|
const series: DataQueryResponseData[] = [];
|
||||||
const annotations: DataQueryResponseData[] = [];
|
const annotations: DataQueryResponseData[] = [];
|
||||||
@ -60,9 +61,10 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
|
|||||||
for (const key in packets) {
|
for (const key in packets) {
|
||||||
const packet = packets[key];
|
const packet = packets[key];
|
||||||
|
|
||||||
if (packet.error) {
|
if (packet.error || packet.errors?.length) {
|
||||||
loadingState = LoadingState.Error;
|
loadingState = LoadingState.Error;
|
||||||
error = packet.error;
|
error = packet.error;
|
||||||
|
errors = packet.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.data && packet.data.length) {
|
if (packet.data && packet.data.length) {
|
||||||
@ -79,11 +81,12 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
|
|||||||
|
|
||||||
const timeRange = getRequestTimeRange(request, loadingState);
|
const timeRange = getRequestTimeRange(request, loadingState);
|
||||||
|
|
||||||
const panelData = {
|
const panelData: PanelData = {
|
||||||
state: loadingState,
|
state: loadingState,
|
||||||
series,
|
series,
|
||||||
annotations,
|
annotations,
|
||||||
error,
|
error,
|
||||||
|
errors,
|
||||||
request,
|
request,
|
||||||
timeRange,
|
timeRange,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user