diff options
Diffstat (limited to 'app/assets/javascripts/import_entities/import_projects/store/actions.js')
-rw-r--r-- | app/assets/javascripts/import_entities/import_projects/store/actions.js | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/app/assets/javascripts/import_entities/import_projects/store/actions.js b/app/assets/javascripts/import_entities/import_projects/store/actions.js new file mode 100644 index 00000000000..7b7afd13c55 --- /dev/null +++ b/app/assets/javascripts/import_entities/import_projects/store/actions.js @@ -0,0 +1,201 @@ +import Visibility from 'visibilityjs'; +import * as types from './mutation_types'; +import { isProjectImportable } from '../utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import Poll from '~/lib/utils/poll'; +import { visitUrl, objectToQuery } from '~/lib/utils/url_utility'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; +import { s__, sprintf } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; +import httpStatusCodes from '~/lib/utils/http_status'; +import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; + +let eTagPoll; + +const hasRedirectInError = e => e?.response?.data?.error?.redirect; +const redirectToUrlInError = e => visitUrl(e.response.data.error.redirect); +const tooManyRequests = e => e.response.status === httpStatusCodes.TOO_MANY_REQUESTS; +const pathWithParams = ({ path, ...params }) => { + const filteredParams = Object.fromEntries( + Object.entries(params).filter(([, value]) => value !== ''), + ); + const queryString = objectToQuery(filteredParams); + return queryString ? `${path}?${queryString}` : path; +}; + +const isRequired = () => { + // eslint-disable-next-line @gitlab/require-i18n-strings + throw new Error('param is required'); +}; + +const clearJobsEtagPoll = () => { + eTagPoll = null; +}; + +const stopJobsPolling = () => { + if (eTagPoll) eTagPoll.stop(); +}; + +const restartJobsPolling = () => { + if (eTagPoll) eTagPoll.restart(); +}; + +const setImportTarget = ({ commit }, { repoId, importTarget }) => + commit(types.SET_IMPORT_TARGET, { repoId, importTarget }); + +const importAll = ({ state, dispatch }) => { + return Promise.all( + state.repositories + .filter(isProjectImportable) + .map(r => dispatch('fetchImport', r.importSource.id)), + ); +}; + +const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit }) => { + const nextPage = state.pageInfo.page + 1; + commit(types.SET_PAGE, nextPage); + commit(types.REQUEST_REPOS); + + const { provider, filter } = state; + + return axios + .get( + pathWithParams({ + path: reposPath, + filter: filter ?? '', + page: nextPage === 1 ? '' : nextPage.toString(), + }), + ) + .then(({ data }) => { + commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })); + }) + .catch(e => { + commit(types.SET_PAGE, nextPage - 1); + + if (hasRedirectInError(e)) { + redirectToUrlInError(e); + } else if (tooManyRequests(e)) { + createFlash( + sprintf(s__('ImportProjects|%{provider} rate limit exceeded. Try again later'), { + provider: capitalizeFirstCharacter(provider), + }), + ); + + commit(types.RECEIVE_REPOS_ERROR); + } else { + createFlash( + sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), { + provider, + }), + ); + + commit(types.RECEIVE_REPOS_ERROR); + } + }); +}; + +const fetchImportFactory = (importPath = isRequired()) => ({ state, commit, getters }, repoId) => { + const { ciCdOnly } = state; + const importTarget = getters.getImportTarget(repoId); + + commit(types.REQUEST_IMPORT, { repoId, importTarget }); + + const { newName, targetNamespace } = importTarget; + return axios + .post(importPath, { + repo_id: repoId, + ci_cd_only: ciCdOnly, + new_name: newName, + target_namespace: targetNamespace, + }) + .then(({ data }) => { + commit(types.RECEIVE_IMPORT_SUCCESS, { + importedProject: convertObjectPropsToCamelCase(data, { deep: true }), + repoId, + }); + }) + .catch(e => { + const serverErrorMessage = e?.response?.data?.errors; + const flashMessage = serverErrorMessage + ? sprintf( + s__('ImportProjects|Importing the project failed: %{reason}'), + { + reason: serverErrorMessage, + }, + false, + ) + : s__('ImportProjects|Importing the project failed'); + + createFlash(flashMessage); + + commit(types.RECEIVE_IMPORT_ERROR, repoId); + }); +}; + +export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, dispatch }) => { + if (eTagPoll) { + stopJobsPolling(); + clearJobsEtagPoll(); + } + + eTagPoll = new Poll({ + resource: { + fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter: state.filter })), + }, + method: 'fetchJobs', + successCallback: ({ data }) => + commit(types.RECEIVE_JOBS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })), + errorCallback: e => { + if (hasRedirectInError(e)) { + redirectToUrlInError(e); + } else { + createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')); + } + }, + }); + + if (!Visibility.hidden()) { + eTagPoll.makeRequest(); + } + + Visibility.change(() => { + if (!Visibility.hidden()) { + dispatch('restartJobsPolling'); + } else { + dispatch('stopJobsPolling'); + } + }); +}; + +const fetchNamespacesFactory = (namespacesPath = isRequired()) => ({ commit }) => { + commit(types.REQUEST_NAMESPACES); + axios + .get(namespacesPath) + .then(({ data }) => + commit(types.RECEIVE_NAMESPACES_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })), + ) + .catch(() => { + createFlash(s__('ImportProjects|Requesting namespaces failed')); + + commit(types.RECEIVE_NAMESPACES_ERROR); + }); +}; + +const setFilter = ({ commit, dispatch }, filter) => { + commit(types.SET_FILTER, filter); + + return dispatch('fetchRepos'); +}; + +export default ({ endpoints = isRequired() }) => ({ + clearJobsEtagPoll, + stopJobsPolling, + restartJobsPolling, + setFilter, + setImportTarget, + importAll, + fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath }), + fetchImport: fetchImportFactory(endpoints.importPath), + fetchJobs: fetchJobsFactory(endpoints.jobsPath), + fetchNamespaces: fetchNamespacesFactory(endpoints.namespacesPath), +}); |