Newer
Older
import { has } from '@/utilities'
function isErrorObject(obj) {
return has(obj, 'message') && has(obj, 'code')
}
/**
* Parses the variety of error formats that are emitted by
* Django REST framework and yields error objects.
*/
function* extractResponseErrors(responseData) {
if (isErrorObject(responseData)) {
yield responseData
} else if (has(responseData, 'detail') && isErrorObject(responseData.detail)) {
yield responseData.detail
} else if (Array.isArray(responseData)) {
for (const item of responseData) {
if (isErrorObject(item)) {
yield item
}
}
export class APIError extends Error {
constructor(message, response) {
super(message ?? response?.statusText)
this.response = response
this.errors = Array.from(extractResponseErrors(response?.data))
}
/**
* Handle API response errors by either calling the provided callbacks
* or throwing exceptions that can be processed in promise handlers.
*/
static handle(error, message, context, vm) {
if (typeof context?.callback === 'function' || typeof context?.callbackCancel === 'function') {
// This API request was made by a callback-style caller,
// so we do error handling like we used to do it.
handleApiError(vm, error, message)
context?.callbackCancel?.()
} else {
// This API request was made by a caller that did not define any callback handlers.
// We take this as an indication, that the caller wants to use
// traditional flow-control with try-catch statements.
throw new APIError(message, error.response)
}
export function handleApiError(vm, err, message) {
if (err.response) {
vm.$log.error(err.response.status + ' ' + err.response.statusText)
vm.$log.error(err.response)
let msg = 'Error: '
if (message) {
msg += message
}
msg += '\n\n'
msg += 'Status code: ' + err.response.status + ' ' + err.response.statusText
alert(msg)
} else {
vm.$log.error(err)
if (message) {
alert('Error: ' + message + '\nInspect the console for more details.')
} else {
alert('Unspecified error. Inspect the console for details.')
}
/**
* Like APIError.handle this is a helper to allow us to migrate from the
* callback-oriented to promise-based response handling.
*
* When a callback is provided it mimics the old callback behaviour and
* calls the callback with the response and returns undefined.
* If no callback is provided we assume the caller wants to handle
* the response promise.
*/
export function callOrReturn(response, callback) {
if (typeof callback === 'function') {
callback(response)
return undefined
} else {
return response
}
function createURLBuilder(basepath, useTrailingSlash = true) {
// Strip all trailing slashes from the basepath.
// We handle slashes when building URLs.
return function buildURL(...subPaths) {
let params
if (subPaths.at(-1) instanceof URLSearchParams) {
if (subPaths.some((path) => String(path).includes('/'))) {
throw new Error('Subpaths must not contain slashes')
}
const subPath = '/' + subPaths.join('/')
const url = basepath + subPath + (useTrailingSlash ? '/' : '')
return params ? url + `?${params}` : url
}
}
export const createTankURL = createURLBuilder(import.meta.env.VUE_APP_API_TANK, false)
export const createSteeringURL = createURLBuilder(import.meta.env.VUE_APP_API_STEERING)