-
Notifications
You must be signed in to change notification settings - Fork 14.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(ReportModal): simplify state reducer and improve error handling #19942
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
/** | ||
* Types mirroring enums in `superset/reports/models.py`: | ||
*/ | ||
export type ReportScheduleType = 'Alert' | 'Report'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is appending There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm using the same variables as in the backend but maybe we can change the backend as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'll change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hello, I think that I do add these to the backend in this refactor of the report modal. |
||
export type ReportCreationMethod = 'charts' | 'dashboards' | 'alerts_reports'; | ||
|
||
export type ReportRecipientType = 'Email' | 'Slack'; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -29,15 +29,15 @@ export type ClientErrorObject = { | |||||
error: string; | ||||||
errors?: SupersetError[]; | ||||||
link?: string; | ||||||
// marshmallow field validation returns the error mssage in the format | ||||||
// of { field: [msg1, msg2] } | ||||||
message?: string; | ||||||
severity?: string; | ||||||
stacktrace?: string; | ||||||
statusText?: string; | ||||||
} & Partial<SupersetClientResponse>; | ||||||
|
||||||
interface ResponseWithTimeout extends Response { | ||||||
// see rejectAfterTimeout.ts | ||||||
interface TimeoutError { | ||||||
statusText: 'timeout'; | ||||||
timeout: number; | ||||||
} | ||||||
|
||||||
|
@@ -48,7 +48,13 @@ export function parseErrorJson(responseObject: JsonObject): ClientErrorObject { | |||||
error.error = error.description = error.errors[0].message; | ||||||
error.link = error.errors[0]?.extra?.link; | ||||||
} | ||||||
|
||||||
// Marshmallow field validation returns the error mssage in the format | ||||||
// of { message: { field1: [msg1, msg2], field2: [msg], } } | ||||||
if (error.message && typeof error.message === 'object' && !error.error) { | ||||||
error.error = | ||||||
Object.values(error.message as Record<string, string[]>)[0]?.[0] || | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Maybe it'll never happen, but i've found being cautious about parsing errors to be a good idea (since the backend hasn't standardized on it yet) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
t('Invalid input'); | ||||||
} | ||||||
if (error.stack) { | ||||||
error = { | ||||||
...error, | ||||||
|
@@ -68,78 +74,95 @@ export function parseErrorJson(responseObject: JsonObject): ClientErrorObject { | |||||
} | ||||||
|
||||||
export function getClientErrorObject( | ||||||
response: SupersetClientResponse | ResponseWithTimeout | string, | ||||||
response: | ||||||
| SupersetClientResponse | ||||||
| TimeoutError | ||||||
| { response: Response } | ||||||
| string, | ||||||
): Promise<ClientErrorObject> { | ||||||
// takes a SupersetClientResponse as input, attempts to read response as Json if possible, | ||||||
// and returns a Promise that resolves to a plain object with error key and text value. | ||||||
return new Promise(resolve => { | ||||||
if (typeof response === 'string') { | ||||||
resolve({ error: response }); | ||||||
} else { | ||||||
const responseObject = | ||||||
response instanceof Response ? response : response.response; | ||||||
if (responseObject && !responseObject.bodyUsed) { | ||||||
// attempt to read the body as json, and fallback to text. we must clone the | ||||||
// response in order to fallback to .text() because Response is single-read | ||||||
responseObject | ||||||
.clone() | ||||||
.json() | ||||||
.then(errorJson => { | ||||||
const error = { ...responseObject, ...errorJson }; | ||||||
resolve(parseErrorJson(error)); | ||||||
}) | ||||||
.catch(() => { | ||||||
// fall back to reading as text | ||||||
responseObject.text().then((errorText: any) => { | ||||||
resolve({ ...responseObject, error: errorText }); | ||||||
}); | ||||||
}); | ||||||
} else if ( | ||||||
'statusText' in response && | ||||||
response.statusText === 'timeout' && | ||||||
'timeout' in response | ||||||
) { | ||||||
resolve({ | ||||||
...responseObject, | ||||||
error: 'Request timed out', | ||||||
errors: [ | ||||||
{ | ||||||
error_type: ErrorTypeEnum.FRONTEND_TIMEOUT_ERROR, | ||||||
extra: { | ||||||
timeout: response.timeout / 1000, | ||||||
issue_codes: [ | ||||||
{ | ||||||
code: 1000, | ||||||
message: t( | ||||||
'Issue 1000 - The dataset is too large to query.', | ||||||
), | ||||||
}, | ||||||
{ | ||||||
code: 1001, | ||||||
message: t( | ||||||
'Issue 1001 - The database is under an unusual load.', | ||||||
), | ||||||
}, | ||||||
], | ||||||
}, | ||||||
level: 'error', | ||||||
message: 'Request timed out', | ||||||
return; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Early return to reduce indentation. |
||||||
} | ||||||
|
||||||
if ( | ||||||
response instanceof TypeError && | ||||||
response.message === 'Failed to fetch' | ||||||
) { | ||||||
resolve({ | ||||||
error: t('Network error'), | ||||||
}); | ||||||
return; | ||||||
} | ||||||
|
||||||
if ( | ||||||
'timeout' in response && | ||||||
'statusText' in response && | ||||||
response.statusText === 'timeout' | ||||||
) { | ||||||
resolve({ | ||||||
...response, | ||||||
error: t('Request timed out'), | ||||||
errors: [ | ||||||
{ | ||||||
error_type: ErrorTypeEnum.FRONTEND_TIMEOUT_ERROR, | ||||||
extra: { | ||||||
timeout: response.timeout / 1000, | ||||||
issue_codes: [ | ||||||
{ | ||||||
code: 1000, | ||||||
message: t('Issue 1000 - The dataset is too large to query.'), | ||||||
}, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: timeout can happen in other API endpoints as well. We should probably move these error codes out of this generic helper function. |
||||||
{ | ||||||
code: 1001, | ||||||
message: t( | ||||||
'Issue 1001 - The database is under an unusual load.', | ||||||
), | ||||||
}, | ||||||
], | ||||||
}, | ||||||
], | ||||||
}); | ||||||
} else { | ||||||
// fall back to Response.statusText or generic error of we cannot read the response | ||||||
let error = (response as any).statusText || (response as any).message; | ||||||
if (!error) { | ||||||
// eslint-disable-next-line no-console | ||||||
console.error('non-standard error:', response); | ||||||
error = t('An error occurred'); | ||||||
} | ||||||
resolve({ | ||||||
...responseObject, | ||||||
error, | ||||||
level: 'error', | ||||||
message: 'Request timed out', | ||||||
}, | ||||||
], | ||||||
}); | ||||||
return; | ||||||
} | ||||||
|
||||||
const responseObject = | ||||||
response instanceof Response ? response : response.response; | ||||||
if (responseObject && !responseObject.bodyUsed) { | ||||||
// attempt to read the body as json, and fallback to text. we must clone the | ||||||
// response in order to fallback to .text() because Response is single-read | ||||||
responseObject | ||||||
.clone() | ||||||
.json() | ||||||
.then(errorJson => { | ||||||
const error = { ...responseObject, ...errorJson }; | ||||||
resolve(parseErrorJson(error)); | ||||||
}) | ||||||
.catch(() => { | ||||||
// fall back to reading as text | ||||||
responseObject.text().then((errorText: any) => { | ||||||
resolve({ ...responseObject, error: errorText }); | ||||||
}); | ||||||
}); | ||||||
} | ||||||
return; | ||||||
} | ||||||
|
||||||
// fall back to Response.statusText or generic error of we cannot read the response | ||||||
let error = (response as any).statusText || (response as any).message; | ||||||
if (!error) { | ||||||
// eslint-disable-next-line no-console | ||||||
console.error('non-standard error:', response); | ||||||
error = t('An error occurred'); | ||||||
} | ||||||
resolve({ | ||||||
...responseObject, | ||||||
error, | ||||||
}); | ||||||
}); | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: update
views/CURD/alert/types.ts
to use these types