2020-05-20 15:01:56 +00:00
|
|
|
import axios from 'axios'
|
|
|
|
|
2021-04-08 12:39:22 +00:00
|
|
|
import { upperCaseFirst } from '@baserow/modules/core/utils/string'
|
2019-11-08 08:52:36 +00:00
|
|
|
|
2020-03-24 20:26:16 +00:00
|
|
|
export class ResponseErrorMessage {
|
2019-11-08 08:52:36 +00:00
|
|
|
constructor(title, message) {
|
|
|
|
this.title = title
|
|
|
|
this.message = message
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ErrorHandler {
|
2019-12-20 10:47:01 +00:00
|
|
|
constructor(store, response, code = null, detail = null) {
|
2019-11-08 08:52:36 +00:00
|
|
|
this.isHandled = false
|
|
|
|
this.store = store
|
2019-12-20 10:47:01 +00:00
|
|
|
this.response = response
|
2019-11-08 08:52:36 +00:00
|
|
|
this.setError(code, detail)
|
|
|
|
|
2020-03-24 20:26:16 +00:00
|
|
|
// A temporary global errorMap containing error messages for certain errors codes.
|
|
|
|
// This must later be replaced by a more dynamic way.
|
2019-11-08 08:52:36 +00:00
|
|
|
this.errorMap = {
|
|
|
|
ERROR_USER_NOT_IN_GROUP: new ResponseErrorMessage(
|
|
|
|
'Action not allowed.',
|
|
|
|
"The action couldn't be completed because you aren't a " +
|
|
|
|
'member of the related group.'
|
2020-03-24 19:22:34 +00:00
|
|
|
),
|
2021-04-09 17:17:30 +01:00
|
|
|
ERROR_USER_INVALID_GROUP_PERMISSIONS: new ResponseErrorMessage(
|
2021-02-04 16:16:33 +00:00
|
|
|
'Action not allowed.',
|
|
|
|
"The action couldn't be completed because you don't have the right " +
|
|
|
|
'permissions to the related group.'
|
|
|
|
),
|
2020-03-24 19:22:34 +00:00
|
|
|
// @TODO move these errors to the module.
|
|
|
|
ERROR_TABLE_DOES_NOT_EXIST: new ResponseErrorMessage(
|
|
|
|
"Table doesn't exist.",
|
|
|
|
"The action couldn't be completed because the related table doesn't exist" +
|
|
|
|
' anymore.'
|
|
|
|
),
|
|
|
|
ERROR_ROW_DOES_NOT_EXIST: new ResponseErrorMessage(
|
|
|
|
"Row doesn't exist.",
|
|
|
|
"The action couldn't be completed because the related row doesn't exist" +
|
|
|
|
' anymore.'
|
2020-03-31 14:15:27 +00:00
|
|
|
),
|
2020-11-30 18:52:39 +00:00
|
|
|
ERROR_FILE_SIZE_TOO_LARGE: new ResponseErrorMessage(
|
|
|
|
'File to large',
|
|
|
|
'The provided file is too large.'
|
|
|
|
),
|
|
|
|
ERROR_INVALID_FILE: new ResponseErrorMessage(
|
|
|
|
'Invalid file',
|
|
|
|
'The provided file is not a valid file.'
|
|
|
|
),
|
|
|
|
ERROR_FILE_URL_COULD_NOT_BE_REACHED: new ResponseErrorMessage(
|
|
|
|
'Invalid URL',
|
|
|
|
'The provided file URL could not be reached.'
|
|
|
|
),
|
2021-03-16 16:24:46 +00:00
|
|
|
ERROR_INVALID_FILE_URL: new ResponseErrorMessage(
|
|
|
|
'Invalid URL',
|
|
|
|
'The provided file URL is invalid or not allowed.'
|
|
|
|
),
|
2021-05-10 17:23:55 +00:00
|
|
|
USER_ADMIN_CANNOT_DEACTIVATE_SELF: new ResponseErrorMessage(
|
|
|
|
'Action not allowed.',
|
|
|
|
'You cannot de-activate or un-staff yourself.'
|
|
|
|
),
|
|
|
|
USER_ADMIN_CANNOT_DELETE_SELF: new ResponseErrorMessage(
|
|
|
|
'Action not allowed.',
|
|
|
|
'You cannot delete yourself.'
|
|
|
|
),
|
2021-05-05 22:09:28 +00:00
|
|
|
ERROR_MAX_FIELD_COUNT_EXCEEDED: new ResponseErrorMessage(
|
|
|
|
"Couldn't create field.",
|
|
|
|
"The action couldn't be completed because the field count exceeds the limit"
|
|
|
|
),
|
2021-07-09 08:29:08 +00:00
|
|
|
ERROR_CANNOT_RESTORE_PARENT_BEFORE_CHILD: new ResponseErrorMessage(
|
|
|
|
'Please restore the parent first.',
|
2021-07-12 11:41:39 +02:00
|
|
|
'You cannot restore this item because it depends on a deleted item.' +
|
|
|
|
' Please restore the parent item first.'
|
2021-07-09 08:29:08 +00:00
|
|
|
),
|
2021-08-04 10:16:10 +00:00
|
|
|
ERROR_GROUP_USER_IS_LAST_ADMIN: new ResponseErrorMessage(
|
|
|
|
"Can't leave the group",
|
|
|
|
"It's not possible to leave the group because you're the last admin. Please" +
|
|
|
|
' delete the group or give another user admin permissions.'
|
|
|
|
),
|
2019-11-08 08:52:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// A temporary notFoundMap containing the error messages for when the
|
|
|
|
// response contains a 404 error based on the provided context name. Note
|
|
|
|
// that an entry is not found a default message will be generated.
|
|
|
|
this.notFoundMap = {}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the error code and details.
|
|
|
|
*/
|
|
|
|
setError(code, detail) {
|
|
|
|
this.code = code
|
|
|
|
this.detail = detail
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if there is a readable error.
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
hasError() {
|
2019-12-20 10:47:01 +00:00
|
|
|
return this.response !== undefined && this.response.code !== null
|
2019-11-08 08:52:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true is the response status code is equal to not found (404).
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
isNotFound() {
|
2019-12-20 10:47:01 +00:00
|
|
|
return this.response !== undefined && this.response.status === 404
|
|
|
|
}
|
|
|
|
|
2021-03-25 23:33:58 +01:00
|
|
|
/**
|
|
|
|
* Returns true if the response status code is equal to not found (429) which
|
|
|
|
* means that the user is sending too much requests to the server.
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
isTooManyRequests() {
|
|
|
|
return this.response !== undefined && this.response.status === 429
|
|
|
|
}
|
|
|
|
|
2019-12-20 10:47:01 +00:00
|
|
|
/**
|
|
|
|
* Return true if there is a network error.
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
hasNetworkError() {
|
|
|
|
return this.response === undefined
|
2019-11-08 08:52:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-24 20:26:16 +00:00
|
|
|
* Finds a message in the global errors or in the provided specific error map.
|
2019-11-08 08:52:36 +00:00
|
|
|
*/
|
2020-03-24 20:26:16 +00:00
|
|
|
getErrorMessage(specificErrorMap = null) {
|
|
|
|
if (
|
|
|
|
specificErrorMap !== null &&
|
2020-03-31 14:15:27 +00:00
|
|
|
Object.prototype.hasOwnProperty.call(specificErrorMap, this.code)
|
2020-03-24 20:26:16 +00:00
|
|
|
) {
|
|
|
|
return specificErrorMap[this.code]
|
2019-11-08 08:52:36 +00:00
|
|
|
}
|
2020-03-24 20:26:16 +00:00
|
|
|
|
2020-03-31 14:15:27 +00:00
|
|
|
if (Object.prototype.hasOwnProperty.call(this.errorMap, this.code)) {
|
2020-03-24 20:26:16 +00:00
|
|
|
return this.errorMap[this.code]
|
|
|
|
}
|
|
|
|
|
|
|
|
return new ResponseErrorMessage(
|
|
|
|
'Action not completed.',
|
|
|
|
"The action couldn't be completed because an unknown error has" +
|
|
|
|
' occured.'
|
|
|
|
)
|
2019-11-08 08:52:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds a not found message for a given context.
|
|
|
|
*/
|
|
|
|
getNotFoundMessage(name) {
|
2020-03-31 14:15:27 +00:00
|
|
|
if (!Object.prototype.hasOwnProperty.call(this.notFoundMap, name)) {
|
2019-11-08 08:52:36 +00:00
|
|
|
return new ResponseErrorMessage(
|
2021-04-08 12:39:22 +00:00
|
|
|
`${upperCaseFirst(name)} not found.`,
|
2019-11-08 08:52:36 +00:00
|
|
|
`The selected ${name.toLowerCase()} wasn't found, maybe it has already been deleted.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return this.notFoundMap[name]
|
|
|
|
}
|
|
|
|
|
2019-12-20 10:47:01 +00:00
|
|
|
/**
|
|
|
|
* Returns a standard network error message. For example if the API server
|
|
|
|
* could not be reached.
|
|
|
|
*/
|
|
|
|
getNetworkErrorMessage() {
|
|
|
|
return new ResponseErrorMessage(
|
|
|
|
'Network error',
|
|
|
|
'Could not connect to the API server.'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-03-25 23:33:58 +01:00
|
|
|
/**
|
|
|
|
* Returns a standard network error message. For example if the API server
|
|
|
|
* could not be reached.
|
|
|
|
*/
|
|
|
|
getTooManyRequestsError() {
|
|
|
|
return new ResponseErrorMessage(
|
|
|
|
'Too many requests',
|
|
|
|
'You are sending too many requests to the server. Please wait a moment.'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-11-08 08:52:36 +00:00
|
|
|
/**
|
|
|
|
* If there is an error or the requested detail is not found an error
|
|
|
|
* message related to the problem is returned.
|
|
|
|
*/
|
2020-03-24 20:26:16 +00:00
|
|
|
getMessage(name = null, specificErrorMap = null) {
|
2021-03-25 23:33:58 +01:00
|
|
|
if (this.isTooManyRequests()) {
|
|
|
|
return this.getTooManyRequestsError()
|
|
|
|
}
|
2019-12-20 10:47:01 +00:00
|
|
|
if (this.hasNetworkError()) {
|
|
|
|
return this.getNetworkErrorMessage()
|
|
|
|
}
|
2019-11-08 08:52:36 +00:00
|
|
|
if (this.hasError()) {
|
2020-03-24 20:26:16 +00:00
|
|
|
return this.getErrorMessage(specificErrorMap)
|
2019-11-08 08:52:36 +00:00
|
|
|
}
|
|
|
|
if (this.isNotFound()) {
|
|
|
|
return this.getNotFoundMessage(name)
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If there is an error or the requested detail is not found we will try to
|
|
|
|
* get find an existing message of one is not provided and notify the user
|
|
|
|
* about what went wrong. After that the error is marked as handled.
|
|
|
|
*/
|
|
|
|
notifyIf(name = null, message = null) {
|
2019-12-20 10:47:01 +00:00
|
|
|
if (
|
|
|
|
!(this.hasError() || this.hasNetworkError() || this.isNotFound()) ||
|
|
|
|
this.isHandled
|
|
|
|
) {
|
2019-11-08 08:52:36 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message === null) {
|
|
|
|
message = this.getMessage(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.store.dispatch(
|
|
|
|
'notification/error',
|
|
|
|
{
|
|
|
|
title: message.title,
|
2020-03-31 14:15:27 +00:00
|
|
|
message: message.message,
|
2019-11-08 08:52:36 +00:00
|
|
|
},
|
|
|
|
{ root: true }
|
|
|
|
)
|
|
|
|
|
|
|
|
this.handled()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will mark the error as handled so that the same error message isn't shown
|
|
|
|
* twice.
|
|
|
|
*/
|
|
|
|
handled() {
|
|
|
|
this.isHandled = true
|
|
|
|
}
|
|
|
|
}
|
2019-08-26 17:49:28 +00:00
|
|
|
|
2020-05-20 19:01:19 +00:00
|
|
|
export default function ({ store, app }, inject) {
|
2020-05-20 15:01:56 +00:00
|
|
|
const url =
|
2020-05-20 19:01:19 +00:00
|
|
|
(process.client
|
|
|
|
? app.$env.PUBLIC_BACKEND_URL
|
2020-06-23 14:09:48 +02:00
|
|
|
: app.$env.PRIVATE_BACKEND_URL) + '/api'
|
2020-05-20 15:01:56 +00:00
|
|
|
const client = axios.create({
|
|
|
|
baseURL: url,
|
|
|
|
withCredentials: false,
|
|
|
|
headers: {
|
|
|
|
Accept: 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2019-08-26 17:49:28 +00:00
|
|
|
// Create a request interceptor to add the authorization token to every
|
|
|
|
// request if the user is authenticated.
|
2020-03-31 14:15:27 +00:00
|
|
|
client.interceptors.request.use((config) => {
|
2019-08-26 17:49:28 +00:00
|
|
|
if (store.getters['auth/isAuthenticated']) {
|
|
|
|
const token = store.getters['auth/token']
|
|
|
|
config.headers.Authorization = `JWT ${token}`
|
|
|
|
}
|
2021-01-24 14:53:06 +00:00
|
|
|
if (store.getters['auth/webSocketId'] !== null) {
|
|
|
|
const webSocketId = store.getters['auth/webSocketId']
|
|
|
|
config.headers.WebSocketId = webSocketId
|
|
|
|
}
|
2019-08-26 17:49:28 +00:00
|
|
|
return config
|
|
|
|
})
|
|
|
|
|
|
|
|
// Create a response interceptor to add more detail tot the error message
|
|
|
|
// and to create a notification when there is a network error.
|
|
|
|
client.interceptors.response.use(
|
2020-03-31 14:15:27 +00:00
|
|
|
(response) => {
|
2019-08-26 17:49:28 +00:00
|
|
|
return response
|
|
|
|
},
|
2020-03-31 14:15:27 +00:00
|
|
|
(error) => {
|
2019-12-20 10:47:01 +00:00
|
|
|
error.handler = new ErrorHandler(store, error.response)
|
2019-08-26 17:49:28 +00:00
|
|
|
|
|
|
|
// Add the error message in the response to the error object.
|
|
|
|
if (
|
|
|
|
error.response &&
|
2020-05-21 09:26:14 +02:00
|
|
|
typeof error.response.data === 'object' &&
|
2019-08-26 17:49:28 +00:00
|
|
|
'error' in error.response.data &&
|
|
|
|
'detail' in error.response.data
|
|
|
|
) {
|
2019-11-08 08:52:36 +00:00
|
|
|
error.handler.setError(
|
|
|
|
error.response.data.error,
|
|
|
|
error.response.data.detail
|
|
|
|
)
|
2019-08-26 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.reject(error)
|
|
|
|
}
|
|
|
|
)
|
2020-05-20 15:01:56 +00:00
|
|
|
|
|
|
|
inject('client', client)
|
2019-08-26 17:49:28 +00:00
|
|
|
}
|