diff --git a/resources/assets/vue/components/general/elements/ListPollingComponent.vue b/resources/assets/vue/components/general/elements/ListPollingComponent.vue index fcd3b8b8..3cb55535 100644 --- a/resources/assets/vue/components/general/elements/ListPollingComponent.vue +++ b/resources/assets/vue/components/general/elements/ListPollingComponent.vue @@ -74,9 +74,6 @@ data(){ return { filters: this.options, - pollingInterval: null, - isPolling: false, - isFetchingResult: false, isLoading: false, } }, @@ -87,13 +84,26 @@ computed: { pendingList () { return this.$store.getters.isInCompleteQueue(this.section); - } + }, + getJob() { + return this.$store.getters.getJob(this.section); + }, + getJobAttemptCount() { + return this.$store.getters.getJobAttemptCount(this.section); + }, }, watch: { pendingList(inComplete){ if(inComplete){ this.fetchList(); } + }, + getJobAttemptCount(newValue, oldValue) { + // console.log('Old value:', JSON.stringify(oldValue)); + // console.log('New value:', JSON.stringify(newValue)); + if(this.getJob){ + this.fetchJobResult(this.getJob.isLastAttempt); + } } }, methods: { @@ -101,7 +111,7 @@ let listDecorators = this.$store.getters.getListDetails(this.section); let url = this.endpoint + '?page=' + listDecorators.page + '&filters=' + JSON.stringify(listDecorators.filters); this.isLoading = true; - this.submitJob(url); + this.$store.dispatch('submitJobRequest', {'url': url, 'name': this.section}); }, //cief todo: remove? // updateFilters(filters){ @@ -125,82 +135,21 @@ to: result.meta.to, total: result.meta.total }; - - this.stopPolling(); this.$store.dispatch('completeList', {'name': this.section, 'data': result.data}); + this.$store.dispatch('stopPollingJobResultByJobId', {'jobId': this.getJob.jobId}); this.$refs.pagination.makePagination(result.meta, result.links); this.isLoading = false; }, errorHandler(error){ - this.isFetchingResult = false; - this.isPolling = false; - }, - startPolling(jobId, maxAttempts = 8) { - let attempts = 0; - let interval = 10000; // Initial interval - - const resetPollingInterval = (customInterval) => { - this.pollingInterval = setInterval(pollJobResult, customInterval); - }; - - const pollJobResult = () => { - if (this.isPolling || this.isFetchingResult) { - return; - } - this.isPolling = true; - - attempts++; - if(attempts === 1){ - this.stopPolling(); - resetPollingInterval(5000); - } - - if (attempts > maxAttempts) { - this.stopPolling(); - this.isLoading = false; - console.log(`Reached maximum attempts (${maxAttempts}). Polling stopped.`); - return; - } - - if(attempts === maxAttempts){ - this.fetchJobResult(jobId, true); - } - else{ - this.fetchJobResult(jobId); - } - }; - - // pollJobResult(); // Initial call - this.pollingInterval = setInterval(pollJobResult, interval); - }, - stopPolling() { - clearInterval(this.pollingInterval); - this.pollingInterval = null; - this.isPolling = false; - this.isFetchingResult = false; - }, - submitJob(url){ - try { - this.$store.dispatch('crudRequest', {endpoint: url, method: 'get'}).then(response => { - let success = response.ok; - response.json().then(response => { - if(!success){return;} - let jobId = response.payload.data.job_id; - if(jobId){ - this.startPolling(jobId); - } - }); - }) - } catch (error) { - console.error('Error submitJob', error); - } + // console.log("Error: " + JSON.stringify(error)); + this.$store.dispatch('updatePollingJobResultByJobId', {'jobId': this.getJob.jobId, 'isPolling': false, 'isFetchingResult': false }); }, fetchJobResult(jobId, isLastAttempt = false) { - this.isFetchingResult = true; + this.$store.dispatch('updatePollingJobResultByJobId', {'jobId': this.getJob.jobId, 'isFetchingResult': true }); try { - let anotherEndpoint = route('api.job.fetch', jobId); + let anotherEndpoint = route('api.job.fetch', this.getJob.jobId); if(isLastAttempt){ - anotherEndpoint = route('api.job.fetch.last.attempt', jobId, isLastAttempt); + anotherEndpoint = route('api.job.fetch.last.attempt', this.getJob.jobId, isLastAttempt); } this.poll(anotherEndpoint, 'get', this.section, false, false); //cief todo: Uncaught (in promise) null } catch (error) { diff --git a/resources/assets/vue/vuex/modules/jobPolling.js b/resources/assets/vue/vuex/modules/jobPolling.js new file mode 100644 index 00000000..1fc03d4c --- /dev/null +++ b/resources/assets/vue/vuex/modules/jobPolling.js @@ -0,0 +1,134 @@ + +const state = { + pollingJobResults: [] +}; + +export default { + state, + getters: { + getJob: (state) => (name) => { + return state.pollingJobResults.find(item => item.name === name);; + }, + getJobAttemptCount: (state) => (name) => { + let job = state.pollingJobResults.find(item => item.name === name); + return job ? job.pollingAttemptsCount : 0; + }, + }, + mutations: { + ADD_POLLING_JOB_RESULT(state, {jobId, name, pollingAttemptsCount, isPolling, isFetchingResult, maxAttempts, pollJobResult, interval}) { + let intervalId = setInterval(() => { + if (pollJobResult) { + pollJobResult(state); + } + }, interval); + state.pollingJobResults.push({ jobId, name, pollingAttemptsCount, isPolling, isFetchingResult, maxAttempts, intervalId}); + }, + UPDATE_POLLING_JOB_RESULT(state, { jobId, pollingAttemptsCount, isPolling, isFetchingResult, isLastAttempt}) { + const index = state.pollingJobResults.findIndex(s => s.jobId === jobId); + if (index !== -1) { + const updatedPollingJobResults = [...state.pollingJobResults]; + updatedPollingJobResults[index] = { ...updatedPollingJobResults[index], + jobId, + pollingAttemptsCount: pollingAttemptsCount !== undefined ? pollingAttemptsCount : updatedPollingJobResults[index].pollingAttemptsCount, + isPolling: isPolling !== undefined ? isPolling : updatedPollingJobResults[index].isPolling, + isFetchingResult: isFetchingResult !== undefined ? isFetchingResult : updatedPollingJobResults[index].isFetchingResult, + isLastAttempt: isLastAttempt !== undefined ? isLastAttempt : updatedPollingJobResults[index].isLastAttempt, + }; + state.pollingJobResults = updatedPollingJobResults; + } + }, + REMOVE_POLLING_JOB_RESULT_BY_JOBID(state, jobId) { + const index = state.pollingJobResults.findIndex(s => s.jobId === jobId); + if (index !== -1) { + clearInterval(state.pollingJobResults[index].intervalId); + state.pollingJobResults.splice(index, 1); + } + }, + // REMOVE_POLLING_JOB_RESULT_BY_JOBID_2(state, jobId) { + // const index = state.pollingJobResults.findIndex(s => s.jobId === jobId); + // if (index !== -1) { + // clearInterval(state.pollingJobResults[index].intervalId); + // //state.pollingJobResults.splice(index, 1); + // } + // }, + REMOVE_POLLING_JOB_RESULT_BY_NAME(state, name) { + const index = state.pollingJobResults.findIndex(s => s.name === name); + if (index !== -1) { + clearInterval(state.pollingJobResults[index].intervalId); + state.pollingJobResults.splice(index, 1); + } + }, + }, + actions: { + submitJobRequest({ dispatch }, { url, name }){ + dispatch('crudRequestV2', { + endpoint: url, + method: 'get', + + }).then(response => { + let success = response.ok; + response.json().then(response => { + if(!success){return;} + let jobId = response.payload.data.job_id; + if(jobId){ + let pollingAttemptsCount = 0; + dispatch('startPolling', { jobId, name, pollingAttemptsCount }); + } + }); + }) + }, + + stopPollingJobResultByJobId({ commit }, { jobId }){ + commit('REMOVE_POLLING_JOB_RESULT_BY_JOBID', jobId); + }, + + stopPollingJobResultByName({ commit }, { name }){ + commit('REMOVE_POLLING_JOB_RESULT_BY_NAME', name); + }, + + startPolling({ state, commit, dispatch }, { jobId, name, pollingAttemptsCount, maxAttempts = 8}) { + let interval = 10000; + + // console.log(`startPolling jobId: ${jobId}, pollingAttemptsCount: ${pollingAttemptsCount}, name: ${name}`); + + const pollJobResult = (state) => { + //console.log(`MONITOR state ${JSON.stringify(state)}`); + const index = state.pollingJobResults.findIndex(s => s.jobId === jobId); + if (index !== -1) { + pollingAttemptsCount = state.pollingJobResults[index].pollingAttemptsCount; + if (state.pollingJobResults[index].isPolling || state.pollingJobResults[index].isFetchingResult) { + return; + } + } + else{ + return; + } + + pollingAttemptsCount++; + if(pollingAttemptsCount === 1){ + commit("REMOVE_POLLING_JOB_RESULT_BY_JOBID", jobId ); + commit("ADD_POLLING_JOB_RESULT", { jobId, name, pollingAttemptsCount, isPolling: false, isFetchingResult: false, maxAttempts, pollJobResult, interval: 5000}); + } + + if (pollingAttemptsCount > maxAttempts) { + //console.log(`Reached maximum attempts (${maxAttempts}). Polling stopped.`); + commit("REMOVE_POLLING_JOB_RESULT_BY_JOBID", jobId ); + return; + } + + if(pollingAttemptsCount === maxAttempts){ + commit('UPDATE_POLLING_JOB_RESULT', { jobId, pollingAttemptsCount, isPolling: true, isFetchingResult: false, isLastAttempt: true }); + } + else{ + commit('UPDATE_POLLING_JOB_RESULT', { jobId, pollingAttemptsCount, isPolling: true, isFetchingResult: false, isLastAttempt: false}); + } + } + commit("ADD_POLLING_JOB_RESULT", { jobId, name, pollingAttemptsCount, isPolling: false, isFetchingResult: false, maxAttempts, pollJobResult, interval}); + + }, + + updatePollingJobResultByJobId({ commit }, { jobId, isPolling, isFetchingResult }){ + commit('UPDATE_POLLING_JOB_RESULT', { jobId, isPolling, isFetchingResult }); + } + } +} diff --git a/resources/assets/vue/vuex/store.js b/resources/assets/vue/vuex/store.js index ff10c673..907e20e4 100644 --- a/resources/assets/vue/vuex/store.js +++ b/resources/assets/vue/vuex/store.js @@ -7,6 +7,7 @@ import crudRequest from './modules/crudRequest' import crudRequestV2 from './modules/crudRequestV2' import authentication from './modules/authentication' import loadRequestQueue from './modules/loadRequestQueue' +import jobPolling from './modules/jobPolling' Vue.use(Vuex); @@ -18,6 +19,7 @@ export default new Vuex.Store({ createNotification, crudRequest, crudRequestV2, - authentication + authentication, + jobPolling } })