diff options
author | 🌴🌴 Filipa Lacerda - OOO back on September 17th 🌴🌴 <filipa@gitlab.com> | 2018-09-03 07:49:52 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-09-03 07:49:52 +0000 |
commit | 55582b4359a4c46b5cbb4fa60d93ca92cc947063 (patch) | |
tree | 1dc83ec5bba15d99ea5909eb1571c5ef99ef9fae /app/assets/javascripts/jobs/store | |
parent | e550b1abaaa9b0053d3430377938f1e801c1fbb1 (diff) | |
download | gitlab-ce-55582b4359a4c46b5cbb4fa60d93ca92cc947063.tar.gz |
Adds Vuex store for the job log page
Diffstat (limited to 'app/assets/javascripts/jobs/store')
-rw-r--r-- | app/assets/javascripts/jobs/store/actions.js | 175 | ||||
-rw-r--r-- | app/assets/javascripts/jobs/store/index.js | 13 | ||||
-rw-r--r-- | app/assets/javascripts/jobs/store/mutation_types.js | 29 | ||||
-rw-r--r-- | app/assets/javascripts/jobs/store/mutations.js | 94 | ||||
-rw-r--r-- | app/assets/javascripts/jobs/store/state.js | 40 |
5 files changed, 351 insertions, 0 deletions
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js new file mode 100644 index 00000000000..7f5406d6f43 --- /dev/null +++ b/app/assets/javascripts/jobs/store/actions.js @@ -0,0 +1,175 @@ +import Visibility from 'visibilityjs'; +import * as types from './mutation_types'; +import axios from '../../lib/utils/axios_utils'; +import Poll from '../../lib/utils/poll'; +import { setCiStatusFavicon } from '../../lib/utils/common_utils'; +import flash from '../../flash'; +import { __ } from '../../locale'; + +export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint); +export const setTraceEndpoint = ({ commit }, endpoint) => + commit(types.SET_TRACE_ENDPOINT, endpoint); +export const setStagesEndpoint = ({ commit }, endpoint) => + commit(types.SET_STAGES_ENDPOINT, endpoint); +export const setJobsEndpoint = ({ commit }, endpoint) => commit(types.SET_JOBS_ENDPOINT, endpoint); + +let eTagPoll; + +export const clearEtagPoll = () => { + eTagPoll = null; +}; + +export const stopPolling = () => { + if (eTagPoll) eTagPoll.stop(); +}; + +export const restartPolling = () => { + if (eTagPoll) eTagPoll.restart(); +}; + +export const requestJob = ({ commit }) => commit(types.REQUEST_JOB); + +export const fetchJob = ({ state, dispatch }) => { + dispatch('requestJob'); + + eTagPoll = new Poll({ + resource: { + getJob(endpoint) { + return axios.get(endpoint); + }, + }, + data: state.jobEndpoint, + method: 'getJob', + successCallback: ({ data }) => dispatch('receiveJobSuccess', data), + errorCallback: () => dispatch('receiveJobError'), + }); + + if (!Visibility.hidden()) { + eTagPoll.makeRequest(); + } else { + axios + .get(state.jobEndpoint) + .then(({ data }) => dispatch('receiveJobSuccess', data)) + .catch(() => dispatch('receiveJobError')); + } + + Visibility.change(() => { + if (!Visibility.hidden()) { + dispatch('restartPolling'); + } else { + dispatch('stopPolling'); + } + }); +}; + +export const receiveJobSuccess = ({ commit }, data) => commit(types.RECEIVE_JOB_SUCCESS, data); +export const receiveJobError = ({ commit }) => { + commit(types.RECEIVE_JOB_ERROR); + flash(__('An error occurred while fetching the job.')); +}; + +/** + * Job's Trace + */ +export const scrollTop = ({ commit }) => { + commit(types.SCROLL_TO_TOP); + window.scrollTo({ top: 0 }); +}; + +export const scrollBottom = ({ commit }) => { + commit(types.SCROLL_TO_BOTTOM); + window.scrollTo({ top: document.height }); +}; + +export const requestTrace = ({ commit }) => commit(types.REQUEST_TRACE); + +let traceTimeout; +export const fetchTrace = ({ dispatch, state }) => { + dispatch('requestTrace'); + + axios + .get(`${state.traceEndpoint}/trace.json`, { + params: { state: state.traceState }, + }) + .then(({ data }) => { + if (!state.fetchingStatusFavicon) { + dispatch('fetchFavicon'); + } + dispatch('receiveTraceSuccess', data); + + if (!data.complete) { + traceTimeout = setTimeout(() => { + dispatch('fetchTrace'); + }, 4000); + } else { + dispatch('stopPollingTrace'); + } + }) + .catch(() => dispatch('receiveTraceError')); +}; +export const stopPollingTrace = ({ commit }) => { + commit(types.STOP_POLLING_TRACE); + clearTimeout(traceTimeout); +}; +export const receiveTraceSuccess = ({ commit }, log) => commit(types.RECEIVE_TRACE_SUCCESS, log); +export const receiveTraceError = ({ commit }) => { + commit(types.RECEIVE_TRACE_ERROR); + clearTimeout(traceTimeout); + flash(__('An error occurred while fetching the job log.')); +}; + +export const fetchFavicon = ({ state, dispatch }) => { + dispatch('requestStatusFavicon'); + setCiStatusFavicon(`${state.pagePath}/status.json`) + .then(() => dispatch('receiveStatusFaviconSuccess')) + .catch(() => dispatch('requestStatusFaviconError')); +}; +export const requestStatusFavicon = ({ commit }) => commit(types.REQUEST_STATUS_FAVICON); +export const receiveStatusFaviconSuccess = ({ commit }) => + commit(types.RECEIVE_STATUS_FAVICON_SUCCESS); +export const requestStatusFaviconError = ({ commit }) => commit(types.RECEIVE_STATUS_FAVICON_ERROR); + +/** + * Stages dropdown on sidebar + */ +export const requestStages = ({ commit }) => commit(types.REQUEST_STAGES); +export const fetchStages = ({ state, dispatch }) => { + dispatch('requestStages'); + + axios + .get(state.stagesEndpoint) + .then(({ data }) => dispatch('receiveStagesSuccess', data)) + .catch(() => dispatch('receiveStagesError')); +}; +export const receiveStagesSuccess = ({ commit }, data) => + commit(types.RECEIVE_STAGES_SUCCESS, data); +export const receiveStagesError = ({ commit }) => { + commit(types.RECEIVE_STAGES_ERROR); + flash(__('An error occurred while fetching stages.')); +}; + +/** + * Jobs list on sidebar - depend on stages dropdown + */ +export const requestJobsForStage = ({ commit }) => commit(types.REQUEST_JOBS_FOR_STAGE); +export const setSelectedStage = ({ commit }, stage) => commit(types.SET_SELECTED_STAGE, stage); + +// On stage click, set selected stage + fetch job +export const fetchJobsForStage = ({ state, dispatch }, stage) => { + dispatch('setSelectedStage', stage); + dispatch('requestJobsForStage'); + + axios + .get(state.stageJobsEndpoint) + .then(({ data }) => dispatch('receiveJobsForStageSuccess', data)) + .catch(() => dispatch('receiveJobsForStageError')); +}; +export const receiveJobsForStageSuccess = ({ commit }, data) => + commit(types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, data); +export const receiveJobsForStageError = ({ commit }) => { + commit(types.RECEIVE_JOBS_FOR_STAGE_ERROR); + flash(__('An error occurred while fetching the jobs.')); +}; + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/jobs/store/index.js b/app/assets/javascripts/jobs/store/index.js new file mode 100644 index 00000000000..d8f6f56ce61 --- /dev/null +++ b/app/assets/javascripts/jobs/store/index.js @@ -0,0 +1,13 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import state from './state'; +import * as actions from './actions'; +import mutations from './mutations'; + +Vue.use(Vuex); + +export default () => new Vuex.Store({ + actions, + mutations, + state: state(), +}); diff --git a/app/assets/javascripts/jobs/store/mutation_types.js b/app/assets/javascripts/jobs/store/mutation_types.js new file mode 100644 index 00000000000..e66e1d4f116 --- /dev/null +++ b/app/assets/javascripts/jobs/store/mutation_types.js @@ -0,0 +1,29 @@ +export const SET_JOB_ENDPOINT = 'SET_JOB_ENDPOINT'; +export const SET_TRACE_ENDPOINT = 'SET_TRACE_ENDPOINT'; +export const SET_STAGES_ENDPOINT = 'SET_STAGES_ENDPOINT'; +export const SET_JOBS_ENDPOINT = 'SET_JOBS_ENDPOINT'; + +export const SCROLL_TO_TOP = 'SCROLL_TO_TOP'; +export const SCROLL_TO_BOTTOM = 'SCROLL_TO_BOTTOM'; + +export const REQUEST_JOB = 'REQUEST_JOB'; +export const RECEIVE_JOB_SUCCESS = 'RECEIVE_JOB_SUCCESS'; +export const RECEIVE_JOB_ERROR = 'RECEIVE_JOB_ERROR'; + +export const REQUEST_TRACE = 'REQUEST_TRACE'; +export const STOP_POLLING_TRACE = 'STOP_POLLING_TRACE'; +export const RECEIVE_TRACE_SUCCESS = 'RECEIVE_TRACE_SUCCESS'; +export const RECEIVE_TRACE_ERROR = 'RECEIVE_TRACE_ERROR'; + +export const REQUEST_STATUS_FAVICON = 'REQUEST_STATUS_FAVICON'; +export const RECEIVE_STATUS_FAVICON_SUCCESS = 'RECEIVE_STATUS_FAVICON_SUCCESS'; +export const RECEIVE_STATUS_FAVICON_ERROR = 'RECEIVE_STATUS_FAVICON_ERROR'; + +export const REQUEST_STAGES = 'REQUEST_STAGES'; +export const RECEIVE_STAGES_SUCCESS = 'RECEIVE_STAGES_SUCCESS'; +export const RECEIVE_STAGES_ERROR = 'RECEIVE_STAGES_ERROR'; + +export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE'; +export const REQUEST_JOBS_FOR_STAGE = 'REQUEST_JOBS_FOR_STAGE'; +export const RECEIVE_JOBS_FOR_STAGE_SUCCESS = 'RECEIVE_JOBS_FOR_STAGE_SUCCESS'; +export const RECEIVE_JOBS_FOR_STAGE_ERROR = 'RECEIVE_JOBS_FOR_STAGE_ERROR'; diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js new file mode 100644 index 00000000000..2a451ef0cd1 --- /dev/null +++ b/app/assets/javascripts/jobs/store/mutations.js @@ -0,0 +1,94 @@ +/* eslint-disable no-param-reassign */ + +import * as types from './mutation_types'; + +export default { + [types.REQUEST_STATUS_FAVICON](state) { + state.fetchingStatusFavicon = true; + }, + [types.RECEIVE_STATUS_FAVICON_SUCCESS](state) { + state.fetchingStatusFavicon = false; + }, + [types.RECEIVE_STATUS_FAVICON_ERROR](state) { + state.fetchingStatusFavicon = false; + }, + + [types.RECEIVE_TRACE_SUCCESS](state, log) { + if (log.state) { + state.traceState = log.state; + } + + if (log.append) { + state.trace += log.html; + state.traceSize += log.size; + } else { + state.trace = log.html; + state.traceSize = log.size; + } + + if (state.traceSize < log.total) { + state.isTraceSizeVisible = true; + } else { + state.isTraceSizeVisible = false; + } + + state.isTraceComplete = log.complete; + state.hasTraceError = false; + }, + [types.STOP_POLLING_TRACE](state) { + state.isTraceComplete = true; + }, + // todo_fl: check this. + [types.RECEIVE_TRACE_ERROR](state) { + state.isLoadingTrace = false; + state.isTraceComplete = true; + state.hasTraceError = true; + }, + + [types.REQUEST_JOB](state) { + state.isLoading = true; + }, + [types.RECEIVE_JOB_SUCCESS](state, job) { + state.isLoading = false; + state.hasError = false; + state.job = job; + }, + [types.RECEIVE_JOB_ERROR](state) { + state.isLoading = false; + state.hasError = true; + state.job = {}; + }, + + [types.SCROLL_TO_TOP](state) { + state.isTraceScrolledToBottom = false; + state.hasBeenScrolled = true; + }, + [types.SCROLL_TO_BOTTOM](state) { + state.isTraceScrolledToBottom = true; + state.hasBeenScrolled = true; + }, + + [types.REQUEST_STAGES](state) { + state.isLoadingStages = true; + }, + [types.RECEIVE_STAGES_SUCCESS](state, stages) { + state.isLoadingStages = false; + state.stages = stages; + }, + [types.RECEIVE_STAGES_ERROR](state) { + state.isLoadingStages = false; + state.stages = []; + }, + + [types.REQUEST_JOBS_FOR_STAGE](state) { + state.isLoadingJobs = true; + }, + [types.RECEIVE_JOBS_FOR_STAGE_SUCCESS](state, jobs) { + state.isLoadingJobs = false; + state.jobs = jobs; + }, + [types.RECEIVE_JOBS_FOR_STAGE_ERROR](state) { + state.isLoadingJobs = false; + state.jobs = []; + }, +}; diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js new file mode 100644 index 00000000000..509cb69a5d3 --- /dev/null +++ b/app/assets/javascripts/jobs/store/state.js @@ -0,0 +1,40 @@ +export default () => ({ + jobEndpoint: null, + traceEndpoint: null, + + // dropdown options + stagesEndpoint: null, + // list of jobs on sidebard + stageJobsEndpoint: null, + + // job log + isLoading: false, + hasError: false, + job: {}, + + // trace + isLoadingTrace: false, + hasTraceError: false, + + trace: '', + + isTraceScrolledToBottom: false, + hasBeenScrolled: false, + + isTraceComplete: false, + traceSize: 0, // todo_fl: needs to be converted into human readable format in components + isTraceSizeVisible: false, + + fetchingStatusFavicon: false, + // used as a query parameter + traceState: null, + // used to check if we need to redirect the user - todo_fl: check if actually needed + traceStatus: null, + + // sidebar dropdown + isLoadingStages: false, + isLoadingJobs: false, + selectedStage: null, + stages: [], + jobs: [], +}); |