Skip to content

Commit

Permalink
Refactor ServerException to output more descriptive error messages, r…
Browse files Browse the repository at this point in the history
…efs #54
  • Loading branch information
carlobeltrame committed Apr 20, 2021
1 parent 744d74a commit 9719380
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 31 deletions.
13 changes: 5 additions & 8 deletions src/ServerException.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AxiosResponse } from 'axios'
import {AxiosError, AxiosResponse} from 'axios'

/**
* Error class for returning server exceptions (attaches Axios response object to error)
Expand All @@ -8,14 +8,11 @@ export default class ServerException extends Error {

/**
* @param response Axios reponse object
* @param params Standard Error parameters
* @param message Error message to prepend to the response message
* @param error
*/
public constructor (response: AxiosResponse, message?: string) {
super(message)

if (!this.message) {
this.message = 'Server error ' + response.status + ' (' + response.statusText + ')'
}
public constructor (response: AxiosResponse, message: string, error: AxiosError) {
super(message + ' (status ' + response.status + '): ' + error.message)
this.name = 'ServerException'
this.response = response
}
Expand Down
16 changes: 9 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
storeHalJsonData(data)
return get(data._links.self.href)
}, (error) => {
throw handleAxiosError(uri, error)
throw handleAxiosError('post to', uri, error)
})
}

Expand Down Expand Up @@ -274,7 +274,7 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
storeHalJsonData(data)
return get(uri)
}, (error) => {
throw handleAxiosError(uri, error)
throw handleAxiosError('patch', uri, error)
})

return returnedResource
Expand Down Expand Up @@ -326,7 +326,7 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
() => deleted(uri),
(error) => {
store.commit('deletingFailed', uri)
throw handleAxiosError(uri, error)
throw handleAxiosError('delete', uri, error)
}
)
}
Expand Down Expand Up @@ -403,11 +403,12 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,

/**
* Processes error object received from Axios for further usage. Triggers delete chain as side effect.
* @param operation Describes the action that was ongoing while the error happened, e.g. get or reload
* @param uri Requested URI that triggered the error
* @param error Raw error object received from Axios
* @returns Error Return new error object with human understandable error message
*/
function handleAxiosError (uri: string, error: AxiosError): Error {
function handleAxiosError (operation: string, uri: string, error: AxiosError): Error {
// Server Error (response received but with error code)
if (error.response) {
const response = error.response
Expand All @@ -416,16 +417,17 @@ function HalJsonVuex (store: Store<Record<string, State>>, axios: AxiosInstance,
// 404 Entity not found error
store.commit('deleting', uri)
deleted(uri) // no need to wait for delete operation to finish
return new ServerException(response, `Could not perform operation, "${uri}" has been deleted`)
return new ServerException(response, `Could not ${operation} "${uri}"`, error)
} else if (response.status === 403) {
// 403 Permission error
return new ServerException(response, 'No permission to perform operation')
return new ServerException(response, `No permission to ${operation} "${uri}"`, error)
} else {
// other unknown server error
return new ServerException(response)
return new ServerException(response, `Error trying to ${operation} "${uri}"`, error)
}
} else {
// another error
error.message = `Error trying to ${operation} "${uri}": ${error.message}`
return error
}
}
Expand Down
43 changes: 27 additions & 16 deletions tests/store.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,7 @@ describe('API store', () => {
// when
const load = vm.api.get('/camps/1')._meta.load
// then
await expect(load).rejects.toThrow('Network Error')
await expect(load).rejects.toThrow('Error trying to fetch \"/camps/1\": Network Error')
})

it('returns error when `get` encounters network timeout', async () => {
Expand All @@ -1210,23 +1210,17 @@ describe('API store', () => {
// when
const load = vm.api.get('/camps/1')._meta.load
// then
await expect(load).rejects.toThrow('timeout of 0ms exceeded')
await expect(load).rejects.toThrow('Error trying to fetch \"/camps/1\": timeout of 0ms exceeded')
})

it('returns error when `get` encounters 404 Not Found', async () => {
// given
axiosMock.onGet('http://localhost/camps/1').replyOnce(200, { id: 1, _links: { self: { href: '/camps/1' } } })
axiosMock.onGet('http://localhost/camps/1').replyOnce(404)

// when
await vm.api.get('/camps/1')._meta.load
// then
expect(vm.$store.state.api['/camps/1']).toMatchObject({ id: 1, _meta: { self: '/camps/1' } })

// when
const load = vm.api.reload('/camps/1')
const load = vm.api.get('/camps/1')._meta.load
// then
await expect(load).rejects.toThrow('"/camps/1" has been deleted')
await expect(load).rejects.toThrow('Could not fetch \"/camps/1\" (status 404): Request failed with status code 404')
expect(vm.$store.state.api['/camps/1']).toBeUndefined()
})

Expand All @@ -1236,7 +1230,24 @@ describe('API store', () => {
// when
const load = vm.api.get('/camps/1')._meta.load
// then
await expect(load).rejects.toThrow('No permission')
await expect(load).rejects.toThrow('No permission to fetch \"/camps/1\" (status 403): Request failed with status code 403')
})

it('returns error when `reload` encounters 404 Not Found', async () => {
// given
axiosMock.onGet('http://localhost/camps/1').replyOnce(200, { id: 1, _links: { self: { href: '/camps/1' } } })
axiosMock.onGet('http://localhost/camps/1').replyOnce(404)

// when
await vm.api.get('/camps/1')._meta.load
// then
expect(vm.$store.state.api['/camps/1']).toMatchObject({ id: 1, _meta: { self: '/camps/1' } })

// when
const load = vm.api.reload('/camps/1')
// then
await expect(load).rejects.toThrow('Could not reload \"/camps/1\" (status 404): Request failed with status code 404')
expect(vm.$store.state.api['/camps/1']).toBeUndefined()
})

it('returns error when `patch` encounters network error', async () => {
Expand All @@ -1245,7 +1256,7 @@ describe('API store', () => {
// when
const load = vm.api.patch('/camps/1', {})
// then
await expect(load).rejects.toThrow('Network Error')
await expect(load).rejects.toThrow('Error trying to patch \"/camps/1\": Network Error')
})

it('returns error when `patch` encounters network timeout', async () => {
Expand All @@ -1254,7 +1265,7 @@ describe('API store', () => {
// when
const load = vm.api.patch('/camps/1', {})
// then
await expect(load).rejects.toThrow('timeout of 0ms exceeded')
await expect(load).rejects.toThrow('Error trying to patch \"/camps/1\": timeout of 0ms exceeded')
})

it('returns error when `patch` encounters 404 Not Found', async () => {
Expand All @@ -1270,7 +1281,7 @@ describe('API store', () => {
// when
const load = vm.api.patch('/camps/1', {})
// then
await expect(load).rejects.toThrow('"/camps/1" has been deleted')
await expect(load).rejects.toThrow('Could not patch \"/camps/1\" (status 404): Request failed with status code 404')
await expect(vm.$store.state.api['/camps/1']).toBeUndefined()
})

Expand All @@ -1280,7 +1291,7 @@ describe('API store', () => {
// when
const load = vm.api.patch('/camps/1', {})
// then
await expect(load).rejects.toThrow('No permission')
await expect(load).rejects.toThrow('No permission to patch \"/camps/1\" (status 403): Request failed with status code 403')
})

it('returns error when `patch` encounters 422 Unprocessable Entity (Validation error)', async () => {
Expand All @@ -1299,7 +1310,7 @@ describe('API store', () => {
const load = vm.api.patch('/camps/1', {})

// then
await expect(load).rejects.toThrow('Server error 422 (undefined)')
await expect(load).rejects.toThrow('Error trying to patch \"/camps/1\" (status 422): Request failed with status code 422')
})
})
})
Expand Down

0 comments on commit 9719380

Please sign in to comment.