summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/jobs/store
diff options
context:
space:
mode:
author🌴🌴 Filipa Lacerda - OOO back on September 17th 🌴🌴 <filipa@gitlab.com>2018-09-03 07:49:52 +0000
committerPhil Hughes <me@iamphill.com>2018-09-03 07:49:52 +0000
commit55582b4359a4c46b5cbb4fa60d93ca92cc947063 (patch)
tree1dc83ec5bba15d99ea5909eb1571c5ef99ef9fae /app/assets/javascripts/jobs/store
parente550b1abaaa9b0053d3430377938f1e801c1fbb1 (diff)
downloadgitlab-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.js175
-rw-r--r--app/assets/javascripts/jobs/store/index.js13
-rw-r--r--app/assets/javascripts/jobs/store/mutation_types.js29
-rw-r--r--app/assets/javascripts/jobs/store/mutations.js94
-rw-r--r--app/assets/javascripts/jobs/store/state.js40
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: [],
+});