diff options
Diffstat (limited to 'app/assets/javascripts/ci/jobs_page/components/job_cells/actions_cell.vue')
-rw-r--r-- | app/assets/javascripts/ci/jobs_page/components/job_cells/actions_cell.vue | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/app/assets/javascripts/ci/jobs_page/components/job_cells/actions_cell.vue b/app/assets/javascripts/ci/jobs_page/components/job_cells/actions_cell.vue new file mode 100644 index 00000000000..609f2790869 --- /dev/null +++ b/app/assets/javascripts/ci/jobs_page/components/job_cells/actions_cell.vue @@ -0,0 +1,265 @@ +<script> +import { + GlButton, + GlButtonGroup, + GlModal, + GlModalDirective, + GlSprintf, + GlTooltipDirective, +} from '@gitlab/ui'; +import { reportMessageToSentry } from '~/ci/utils'; +import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; +import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated +import { + ACTIONS_DOWNLOAD_ARTIFACTS, + ACTIONS_START_NOW, + ACTIONS_UNSCHEDULE, + ACTIONS_PLAY, + ACTIONS_RETRY, + ACTIONS_RUN_AGAIN, + CANCEL, + GENERIC_ERROR, + JOB_SCHEDULED, + JOB_SUCCESS, + PLAY_JOB_CONFIRMATION_MESSAGE, + RUN_JOB_NOW_HEADER_TITLE, + FILE_TYPE_ARCHIVE, +} from '../../constants'; +import eventHub from '../../event_hub'; +import cancelJobMutation from '../../graphql/mutations/job_cancel.mutation.graphql'; +import playJobMutation from '../../graphql/mutations/job_play.mutation.graphql'; +import retryJobMutation from '../../graphql/mutations/job_retry.mutation.graphql'; +import unscheduleJobMutation from '../../graphql/mutations/job_unschedule.mutation.graphql'; + +export default { + ACTIONS_DOWNLOAD_ARTIFACTS, + ACTIONS_START_NOW, + ACTIONS_UNSCHEDULE, + ACTIONS_PLAY, + ACTIONS_RETRY, + CANCEL, + GENERIC_ERROR, + PLAY_JOB_CONFIRMATION_MESSAGE, + RUN_JOB_NOW_HEADER_TITLE, + jobRetry: 'jobRetry', + jobCancel: 'jobCancel', + jobPlay: 'jobPlay', + jobUnschedule: 'jobUnschedule', + playJobModalId: 'play-job-modal', + name: 'JobActionsCell', + components: { + GlButton, + GlButtonGroup, + GlCountdown, + GlModal, + GlSprintf, + }, + directives: { + GlModalDirective, + GlTooltip: GlTooltipDirective, + }, + inject: { + admin: { + default: false, + }, + }, + props: { + job: { + type: Object, + required: true, + }, + }, + data() { + return { + retryBtnDisabled: false, + cancelBtnDisabled: false, + playManualBtnDisabled: false, + unscheduleBtnDisabled: false, + }; + }, + computed: { + hasArtifacts() { + return this.job.artifacts.nodes.find((artifact) => artifact.fileType === FILE_TYPE_ARCHIVE); + }, + artifactDownloadPath() { + return this.hasArtifacts.downloadPath; + }, + canReadJob() { + return this.job.userPermissions?.readBuild; + }, + canUpdateJob() { + return this.job.userPermissions?.updateBuild; + }, + canReadArtifacts() { + return this.job.userPermissions?.readJobArtifacts; + }, + isActive() { + return this.job.active; + }, + manualJobPlayable() { + return this.job.playable && !this.admin && this.job.manualJob; + }, + isRetryable() { + return this.job.retryable; + }, + isScheduled() { + return this.job.status === JOB_SCHEDULED; + }, + scheduledAt() { + return this.job.scheduledAt; + }, + currentJobActionPath() { + return this.job.detailedStatus?.action?.path; + }, + currentJobMethod() { + return this.job.detailedStatus?.action?.method; + }, + shouldDisplayArtifacts() { + return this.canReadArtifacts && this.hasArtifacts; + }, + retryButtonTitle() { + return this.job.status === JOB_SUCCESS ? ACTIONS_RUN_AGAIN : ACTIONS_RETRY; + }, + }, + methods: { + async postJobAction(name, mutation, redirect = false) { + try { + const { + data: { + [name]: { errors, job }, + }, + } = await this.$apollo.mutate({ + mutation, + variables: { id: this.job.id }, + }); + if (errors.length > 0) { + reportMessageToSentry(this.$options.name, errors.join(', '), {}); + this.showToastMessage(); + } else if (redirect) { + // Retry and Play actions redirect to job detail view + // we don't need to refetch with jobActionPerformed event + redirectTo(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated + } else { + eventHub.$emit('jobActionPerformed'); + } + } catch (failure) { + reportMessageToSentry(this.$options.name, failure, {}); + this.showToastMessage(); + } + }, + showToastMessage() { + const toastProps = { + text: this.$options.GENERIC_ERROR, + variant: 'danger', + }; + + this.$toast.show(toastProps.text, { + variant: toastProps.variant, + }); + }, + cancelJob() { + this.cancelBtnDisabled = true; + + this.postJobAction(this.$options.jobCancel, cancelJobMutation); + }, + retryJob() { + this.retryBtnDisabled = true; + + this.postJobAction(this.$options.jobRetry, retryJobMutation, true); + }, + playJob() { + this.playManualBtnDisabled = true; + + this.postJobAction(this.$options.jobPlay, playJobMutation, true); + }, + unscheduleJob() { + this.unscheduleBtnDisabled = true; + + this.postJobAction(this.$options.jobUnschedule, unscheduleJobMutation); + }, + }, +}; +</script> + +<template> + <gl-button-group> + <template v-if="canReadJob && canUpdateJob"> + <gl-button + v-if="isActive" + v-gl-tooltip + icon="cancel" + :title="$options.CANCEL" + :aria-label="$options.CANCEL" + :disabled="cancelBtnDisabled" + data-testid="cancel-button" + @click="cancelJob()" + /> + <template v-else-if="isScheduled"> + <gl-button icon="planning" disabled data-testid="countdown"> + <gl-countdown :end-date-string="scheduledAt" /> + </gl-button> + <gl-button + v-gl-modal-directive="$options.playJobModalId" + v-gl-tooltip + icon="play" + :title="$options.ACTIONS_START_NOW" + :aria-label="$options.ACTIONS_START_NOW" + data-testid="play-scheduled" + /> + <gl-modal + :modal-id="$options.playJobModalId" + :title="$options.RUN_JOB_NOW_HEADER_TITLE" + @primary="playJob()" + > + <gl-sprintf :message="$options.PLAY_JOB_CONFIRMATION_MESSAGE"> + <template #job_name>{{ job.name }}</template> + </gl-sprintf> + </gl-modal> + <gl-button + v-gl-tooltip + icon="time-out" + :title="$options.ACTIONS_UNSCHEDULE" + :aria-label="$options.ACTIONS_UNSCHEDULE" + :disabled="unscheduleBtnDisabled" + data-testid="unschedule" + @click="unscheduleJob()" + /> + </template> + <template v-else> + <!--Note: This is the manual job play button --> + <gl-button + v-if="manualJobPlayable" + v-gl-tooltip + icon="play" + :title="$options.ACTIONS_PLAY" + :aria-label="$options.ACTIONS_PLAY" + :disabled="playManualBtnDisabled" + data-testid="play" + @click="playJob()" + /> + <gl-button + v-else-if="isRetryable" + v-gl-tooltip + icon="retry" + :title="retryButtonTitle" + :aria-label="retryButtonTitle" + :method="currentJobMethod" + :disabled="retryBtnDisabled" + data-testid="retry" + @click="retryJob()" + /> + </template> + </template> + <gl-button + v-if="shouldDisplayArtifacts" + v-gl-tooltip + icon="download" + :title="$options.ACTIONS_DOWNLOAD_ARTIFACTS" + :aria-label="$options.ACTIONS_DOWNLOAD_ARTIFACTS" + :href="artifactDownloadPath" + rel="nofollow" + download + data-testid="download-artifacts" + /> + </gl-button-group> +</template> |