Skip to content
Snippets Groups Projects
Commit 2e2f5c70 authored by Erwan Rouchet's avatar Erwan Rouchet
Browse files

Merge branch 'handle-error-payloads' into 'master'

Handle errors with a JSON payload during Annotation

See merge request !474
parents 65f1b6f5 a3b7990e
No related branches found
No related tags found
1 merge request!474Handle errors with a JSON payload during Annotation
import { clone, assign } from 'lodash'
import axios from 'axios'
import * as api from '~/js/api'
import { removeEmptyStrings, paginatedPayload, errorParser } from '~/js/helpers'
import { removeEmptyStrings, paginatedPayload } from '~/js/helpers'
export const initialState = () => ({
elements: null,
......@@ -127,7 +127,7 @@ export const actions = {
return element
} catch (err) {
commit('useError', err, { root: true })
throw errorParser(err)
throw err
}
},
......@@ -153,7 +153,7 @@ export const actions = {
if (save) commit('set', resp.data)
} catch (err) {
commit('useError', err, { root: true })
throw errorParser(err)
throw err
}
},
......@@ -164,7 +164,7 @@ export const actions = {
return (await axios.delete(url, { ...payload }))
} catch (err) {
commit('useError', err, { root: true })
throw errorParser(err)
throw err
}
},
......
......@@ -18,11 +18,15 @@
<input
type="text"
class="input"
:class="{ 'is-danger': fieldErrors.name }"
v-model="name"
:maxlength="ELEMENT_NAME_MAX_LENGTH"
:placeholder="placeholder"
v-on:input="allowUpdate"
/>
<template v-if="fieldErrors.name">
<p class="help is-danger" v-for="err in fieldErrors.name" :key="err">{{ err }}</p>
</template>
</div>
</div>
</div>
......@@ -35,7 +39,7 @@
<div class="field">
<div class="control">
<div class="control" title="Filter by type">
<span class="select is-fullwidth">
<span class="select is-fullwidth" :class="{ 'is-danger': fieldErrors.type }">
<select
ref="select"
v-model="type"
......@@ -47,6 +51,9 @@
</option>
</select>
</span>
<template v-if="fieldErrors.type">
<p class="help is-danger" v-for="err in fieldErrors.type" :key="err">{{ err }}</p>
</template>
</div>
</div>
</div>
......@@ -77,6 +84,10 @@
</div>
</div>
</div>
<div class="notification is-danger" v-if="globalErrors.length">
<button class="delete" v-on:click="cleanGlobalErrors"></button>
<p v-for="error in globalErrors" :key="error">{{ error }}</p>
</div>
<div class="buttons is-right has-addons">
<template v-if="elementId">
<router-link
......@@ -131,7 +142,7 @@
<script>
import { mapGetters, mapMutations, mapState } from 'vuex'
import { ELEMENT_NAME_MAX_LENGTH } from '~/js/config'
import { iiifCoords, iiifUri } from '~/js/helpers'
import { iiifCoords, iiifUri, errorParser } from '~/js/helpers'
import { truncateMixin, corporaMixin } from '~/js/mixins'
import MLClassSelect from '~/vue/MLClassSelect'
import Modal from '~/vue/Modal'
......@@ -182,7 +193,9 @@ export default {
name: '',
// allow User to update element on a valid change
isChanged: false,
showModal: false
showModal: false,
// API fields validation errors
fieldErrors: {}
}),
computed: {
...mapGetters('display', ['iiifWidth']),
......@@ -208,6 +221,12 @@ export default {
},
isValid () {
return this.type && this.validClassification
},
globalErrors () {
const { name, type, ...errors } = this.fieldErrors
return Object.entries(errors)
.filter(([key, value]) => typeof val === 'string' || Array.isArray)
.map(([key, value]) => `${key}: ${value}`)
}
},
methods: {
......@@ -237,9 +256,23 @@ export default {
// Update name input to placeholder value if not defined
if (!this.name) this.name = this.placeholder
},
setErrors (error) {
// Set field errors from API return value
if (!error) this.fieldErrors = {}
else if (!error.response || typeof error.response.data !== 'object') this.fieldErrors = { error: errorParser(error) }
else this.fieldErrors = error.response.data
},
cleanGlobalErrors () {
// Remove non field errors
this.fieldErrors = {
type: this.fieldErrors.type,
name: this.fieldErrors.name
}
},
async createElement () {
if (this.loading || this.elementId || !this.type) return
this.loading = true
this.setErrors(null)
this.nameFromPlaceholder()
try {
const resp = await this.$store.dispatch('elements/create', {
......@@ -262,7 +295,8 @@ export default {
}
})
} catch (e) {
this.notify({ type: 'error', text: `An error occured during element creation: ${e}` })
this.setErrors(e)
this.notify({ type: 'error', text: 'An error occured during element creation.' })
} finally {
this.loading = false
}
......@@ -270,6 +304,7 @@ export default {
async updateElement () {
if (this.loading || !this.type || !this.elementId || !this.isChanged) return
this.loading = true
this.setErrors(null)
this.nameFromPlaceholder()
try {
await this.$store.dispatch('elements/patch', {
......@@ -285,18 +320,22 @@ export default {
this.isChanged = false
this.notify({ type: 'success', text: 'Element updated.' })
} catch (e) {
this.notify({ type: 'error', text: `An error occured while updating element attributes: ${e}` })
this.setErrors(e)
this.notify({ type: 'error', text: 'An error occured while updating element attributes.' })
} finally {
this.loading = false
}
},
async deleteElement () {
if (!this.elementId && !this.canAdmin(this.corpus)) return
this.loading = true
this.setErrors(null)
try {
await this.$store.dispatch('elements/delete', { id: this.elementId })
this.rmAnnotationPolygon(this.elementPolygonAttrs)
} catch (e) {
this.notify({ type: 'error', text: `An error occured while deleting element: ${e}` })
this.setErrors(e)
this.notify({ type: 'error', text: 'An error occured while deleting the element.' })
} finally {
this.loading = false
this.showModal = false
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment