UNPKG

@superset-ui/core

Version:
226 lines 8.65 kB
/** * 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. */ import { COMMON_ERR_MESSAGES, t, ErrorTypeEnum, isProbablyHTML, isJsonString, } from '@superset-ui/core'; const ERROR_CODE_LOOKUP = { 400: 'Bad request', 401: 'Unauthorized', 402: 'Payment required', 403: 'Forbidden', 404: 'Not found', 405: 'Method not allowed', 406: 'Not acceptable', 407: 'Proxy authentication required', 408: 'Request timeout', 409: 'Conflict', 410: 'Gone', 411: 'Length required', 412: 'Precondition failed', 413: 'Payload too large', 414: 'URI too long', 415: 'Unsupported media type', 416: 'Range not satisfiable', 417: 'Expectation failed', 418: "I'm a teapot", 500: 'Server error', 501: 'Not implemented', 502: 'Bad gateway', 503: 'Service unavailable', 504: 'Gateway timeout', 505: 'HTTP version not supported', 506: 'Variant also negotiates', 507: 'Insufficient storage', 508: 'Loop detected', 510: 'Not extended', 511: 'Network authentication required', 599: 'Network error', }; export function checkForHtml(str) { return !isJsonString(str) && isProbablyHTML(str); } export function parseStringResponse(str) { if (checkForHtml(str)) { for (const [code, message] of Object.entries(ERROR_CODE_LOOKUP)) { const regex = new RegExp(`${code}|${message}`, 'i'); if (regex.test(str)) { return t(message); } } return t('Unknown error'); } return str; } export function getErrorFromStatusCode(status) { return ERROR_CODE_LOOKUP[status] || null; } export function retrieveErrorMessage(str, errorObject) { const statusError = 'status' in errorObject ? getErrorFromStatusCode(errorObject.status) : null; // Prefer status code message over the response or HTML text return statusError || parseStringResponse(str); } export function parseErrorJson(responseJson) { let error = { ...responseJson }; // Backwards compatibility for old error renderers with the new error object if (error.errors && error.errors.length > 0) { error.error = error.description = error.errors[0].message; error.link = error.errors[0]?.extra?.link; } // Marshmallow field validation returns the error message in the format // of { message: { field1: [msg1, msg2], field2: [msg], } } if (!error.error && error.message) { if (typeof error.message === 'object') { error.error = Object.values(error.message)[0]?.[0] || t('Invalid input'); } if (typeof error.message === 'string') { if (checkForHtml(error.message)) { error.error = retrieveErrorMessage(error.message, error); } else { error.error = error.message; } } } if (error.stack) { error = { ...error, error: t('Unexpected error: ') + (error.description || t('(no description, click to see stack trace)')), stacktrace: error.stack, }; } else if (error.responseText && error.responseText.indexOf('CSRF') >= 0) { error = { ...error, error: t(COMMON_ERR_MESSAGES.SESSION_TIMED_OUT), }; } return { ...error, error: error.error }; // explicit ClientErrorObject } export function getClientErrorObject(response) { // 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: parseStringResponse(response) }); return; } 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.'), }, { code: 1001, message: t('Issue 1001 - The database is under an unusual load.'), }, ], }, 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 => { // Destructuring instead of spreading to avoid loss of sibling properties to the body const { url, status, statusText, redirected, type } = responseObject; const responseSummary = { url, status, statusText, redirected, type }; const error = { ...errorJson, ...responseSummary, }; resolve(parseErrorJson(error)); }) .catch(() => { // fall back to reading as text responseObject.text().then((errorText) => { resolve({ // Destructuring not necessary here ...responseObject, error: retrieveErrorMessage(errorText, responseObject), }); }); }); return; } // fall back to Response.statusText or generic error of we cannot read the response let error = response.statusText || response.message; if (!error) { // eslint-disable-next-line no-console console.error('non-standard error:', response); error = t('An error occurred'); } resolve({ ...responseObject, error: parseStringResponse(error), }); }); } /* * Utility to get standardized error text for generic update failures */ export async function getErrorText(errorObject, source) { const { error, message } = await getClientErrorObject(errorObject); let errorText = t('Sorry, an unknown error occurred.'); if (error) { errorText = t('Sorry, there was an error saving this %s: %s', source, error); } if (typeof message === 'string' && message === 'Forbidden') { errorText = t('You do not have permission to edit this %s', source); } return errorText; } export function getClientErrorMessage(message, clientError) { let finalMessage = message; const errorMessage = clientError?.message || clientError?.error; if (errorMessage) { finalMessage = `${finalMessage}:\n${errorMessage}`; } return finalMessage; } //# sourceMappingURL=getClientErrorObject.js.map