Async job poller and notifications for actions (#32)

Async job poller tech capability implementation, show notifications on success/fail.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Anurag Awasthi 2019-09-03 14:53:38 +05:30 committed by Rohit Yadav
parent 8b9fccdf11
commit db42cdf830
7 changed files with 101 additions and 12 deletions

View File

@ -234,6 +234,7 @@ import DataView from '@/components/widgets/DataView'
import InstanceView from '@/components/widgets/InstanceView' import InstanceView from '@/components/widgets/InstanceView'
import Status from '@/components/widgets/Status' import Status from '@/components/widgets/Status'
import { mixinDevice } from '@/utils/mixin.js' import { mixinDevice } from '@/utils/mixin.js'
import { constants } from 'crypto';
export default { export default {
name: 'Resource', name: 'Resource',
@ -506,9 +507,21 @@ export default {
const closeAction = this.closeAction const closeAction = this.closeAction
const showError = this.$notification['error'] const showError = this.$notification['error']
api(this.currentAction.api, params).then(json => { api(this.currentAction.api, params).then(json => {
for (const obj in json) {
if (obj.includes('response')) {
for (const res in json[obj]) {
if (res === 'jobid') {
this.$store.dispatch('AddAsyncJob', { 'title': this.currentAction.label, 'jobid': json[obj][res], 'description': this.resource.name, 'status': 'progress'})
break
}
}
break
}
}
closeAction() closeAction()
}).catch(function (error) { }).catch(function (error) {
closeAction() closeAction()
console.log(error)
showError({ showError({
message: 'Request Failed', message: 'Request Failed',
description: error.response.headers['x-description'] description: error.response.headers['x-description']

View File

@ -17,7 +17,7 @@
</a-list-item> </a-list-item>
<a-list-item v-for="(job, index) in jobs" :key="index"> <a-list-item v-for="(job, index) in jobs" :key="index">
<a-list-item-meta :title="job.title" :description="job.description"> <a-list-item-meta :title="job.title" :description="job.description">
<a-avatar :style="job.style" :icon="job.icon" slot="avatar"/> <a-avatar :style="notificationAvatar[job.status].style" :icon="notificationAvatar[job.status].icon" slot="avatar"/>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>
</a-list> </a-list>
@ -32,26 +32,89 @@
</template> </template>
<script> <script>
import { api } from '@/api'
import store from '@/store'
import { constants } from 'crypto';
export default { export default {
name: 'HeaderNotice', name: 'HeaderNotice',
data () { data () {
return { return {
loading: false, loading: false,
visible: false, visible: false,
jobs: [] jobs: [],
poller: null,
notificationAvatar: {
'done': { 'icon': 'check-circle', 'style': 'backgroundColor:#87d068' },
'progress': { 'icon': 'loading', 'style': 'backgroundColor:#ffbf00' },
'failed': { 'icon': 'close-circle', 'style': 'backgroundColor:#f56a00' }
}
} }
}, },
methods: { methods: {
showNotifications () { showNotifications () {
this.visible = !this.visible this.visible = !this.visible
this.jobs.push({ 'title': 'Start VM', description: 'VM Deployment', icon: 'check-circle', status: 'done', style: 'backgroundColor:#87d068' })
this.jobs.push({ 'title': 'Start VM', description: 'VM Deployment', icon: 'loading', status: 'progress', style: 'backgroundColor:#ffbf00' })
this.jobs.push({ 'title': 'Start VM', description: 'VM Deployment', icon: 'close-circle', status: 'failed', style: 'backgroundColor:#f56a00' })
}, },
clearJobs () { clearJobs () {
this.visible = false this.visible = false
this.jobs = [] this.jobs = []
this.$store.commit('SET_ASYNC_JOB_IDS', [])
},
startPolling() {
this.poller = setInterval(() => {
this.pollJobs()
}, 2500)
},
async pollJobs () {
var hasUpdated = false
for (var i in this.jobs) {
if (this.jobs[i].status === 'progress') {
await api('queryAsyncJobResult', {'jobid': this.jobs[i].jobid}).then(json => {
var result = json.queryasyncjobresultresponse
if (result.jobstatus === 1 && this.jobs[i].status !== 'done') {
hasUpdated = true
this.$notification['success']({
message: this.jobs[i].title,
description: this.jobs[i].description
})
this.jobs[i].status = 'done'
} else if (result.jobstatus === 2 && this.jobs[i].status !== 'failed') {
hasUpdated = true
this.jobs[i].status = 'failed'
if (result.jobresult.errortext !== null) {
this.jobs[i].description = '(' + this.jobs[i].description + ') ' + result.jobresult.errortext
}
this.$notification['error']({
message: this.jobs[i].title,
description: this.jobs[i].description
})
}
}).catch(function (e) {
console.log('Error encountered while fetching async job result' + e)
})
}
}
if (hasUpdated) {
this.$store.commit('SET_ASYNC_JOB_IDS', this.jobs.reverse())
}
} }
},
beforeDestroy () {
clearInterval(this.poller)
},
created () {
this.startPolling()
},
mounted () {
this.jobs = store.getters.asyncJobIds.reverse()
this.$store.watch(
(state, getters) => getters.asyncJobIds,
(newValue, oldValue) => {
if (oldValue !== newValue && newValue !== undefined) {
this.jobs = newValue.reverse()
}
}
)
} }
} }
</script> </script>

View File

@ -12,7 +12,8 @@ import {
DEFAULT_FIXED_HEADER_HIDDEN, DEFAULT_FIXED_HEADER_HIDDEN,
DEFAULT_FIXED_SIDEMENU, DEFAULT_FIXED_SIDEMENU,
DEFAULT_CONTENT_WIDTH_TYPE, DEFAULT_CONTENT_WIDTH_TYPE,
DEFAULT_MULTI_TAB DEFAULT_MULTI_TAB,
ASYNC_JOB_IDS
} from '@/store/mutation-types' } from '@/store/mutation-types'
import config from '@/config/settings' import config from '@/config/settings'
@ -29,4 +30,5 @@ export default function Initializer () {
store.commit('TOGGLE_MULTI_TAB', Vue.ls.get(DEFAULT_MULTI_TAB, config.multiTab)) store.commit('TOGGLE_MULTI_TAB', Vue.ls.get(DEFAULT_MULTI_TAB, config.multiTab))
store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN)) store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN))
store.commit('SET_PROJECT', Vue.ls.get(CURRENT_PROJECT)) store.commit('SET_PROJECT', Vue.ls.get(CURRENT_PROJECT))
store.commit('SET_ASYNC_JOB_IDS', Vue.ls.get(ASYNC_JOB_IDS))
} }

View File

@ -10,7 +10,8 @@ const getters = {
apis: state => state.user.apis, apis: state => state.user.apis,
userInfo: state => state.user.info, userInfo: state => state.user.info,
addRouters: state => state.permission.addRouters, addRouters: state => state.permission.addRouters,
multiTab: state => state.app.multiTab multiTab: state => state.app.multiTab,
asyncJobIds: state => state.user.asyncJobIds
} }
export default getters export default getters

View File

@ -39,7 +39,6 @@ const app = {
state.device = device state.device = device
}, },
TOGGLE_THEME: (state, theme) => { TOGGLE_THEME: (state, theme) => {
// setStore('_DEFAULT_THEME', theme)
Vue.ls.set(DEFAULT_THEME, theme) Vue.ls.set(DEFAULT_THEME, theme)
state.theme = theme state.theme = theme
}, },

View File

@ -1,7 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import md5 from 'md5' import md5 from 'md5'
import { login, logout, api } from '@/api' import { login, logout, api } from '@/api'
import { ACCESS_TOKEN, CURRENT_PROJECT } from '@/store/mutation-types' import { ACCESS_TOKEN, CURRENT_PROJECT, ASYNC_JOB_IDS } from '@/store/mutation-types'
import { welcome } from '@/utils/util' import { welcome } from '@/utils/util'
// import VueCookies from 'vue-cookies' // import VueCookies from 'vue-cookies'
@ -13,7 +13,8 @@ const user = {
avatar: '', avatar: '',
info: {}, info: {},
apis: {}, apis: {},
project: {} project: {},
asyncJobIds: []
}, },
mutations: { mutations: {
@ -36,6 +37,10 @@ const user = {
}, },
SET_APIS: (state, apis) => { SET_APIS: (state, apis) => {
state.apis = apis state.apis = apis
},
SET_ASYNC_JOB_IDS: (state, jobsJsonArray) => {
Vue.ls.set(ASYNC_JOB_IDS, jobsJsonArray)
state.asyncJobIds = jobsJsonArray
} }
}, },
@ -61,6 +66,7 @@ const user = {
Vue.ls.set(ACCESS_TOKEN, result.sessionkey, 60 * 60 * 1000) Vue.ls.set(ACCESS_TOKEN, result.sessionkey, 60 * 60 * 1000)
commit('SET_TOKEN', result.sessionkey) commit('SET_TOKEN', result.sessionkey)
commit('SET_PROJECT', {}) commit('SET_PROJECT', {})
commit('SET_ASYNC_JOB_IDS', [])
resolve() resolve()
}).catch(error => { }).catch(error => {
@ -104,7 +110,6 @@ const user = {
}) })
}) })
}, },
Logout ({ commit, state }) { Logout ({ commit, state }) {
return new Promise((resolve) => { return new Promise((resolve) => {
// Remove cookies // Remove cookies
@ -122,6 +127,7 @@ const user = {
commit('SET_APIS', {}) commit('SET_APIS', {})
Vue.ls.remove(CURRENT_PROJECT) Vue.ls.remove(CURRENT_PROJECT)
Vue.ls.remove(ACCESS_TOKEN) Vue.ls.remove(ACCESS_TOKEN)
Vue.ls.remove(ASYNC_JOB_IDS)
logout(state.token).then(() => { logout(state.token).then(() => {
resolve() resolve()
@ -129,8 +135,12 @@ const user = {
resolve() resolve()
}) })
}) })
},
AddAsyncJob ({ commit }, jobJson) {
var jobsArray = Vue.ls.get(ASYNC_JOB_IDS, [])
jobsArray.push(jobJson)
commit('SET_ASYNC_JOB_IDS', jobsArray)
} }
} }
} }

View File

@ -10,6 +10,7 @@ export const DEFAULT_FIXED_SIDEMENU = 'DEFAULT_FIXED_SIDEMENU'
export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN' export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN'
export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE' export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE'
export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB' export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB'
export const ASYNC_JOB_IDS = 'ASYNC_JOB_IDS'
export const CONTENT_WIDTH_TYPE = { export const CONTENT_WIDTH_TYPE = {
Fluid: 'Fluid', Fluid: 'Fluid',