diff options
Diffstat (limited to 'app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue')
-rw-r--r-- | app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue new file mode 100644 index 00000000000..f25994a7506 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue @@ -0,0 +1,365 @@ +<script> +import eventHub from '../../event_hub'; +import PipelinesActionsComponent from './pipelines_actions.vue'; +import PipelinesArtifactsComponent from './pipelines_artifacts.vue'; +import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import PipelineStage from './stage.vue'; +import PipelineUrl from './pipeline_url.vue'; +import PipelineTriggerer from './pipeline_triggerer.vue'; +import PipelinesTimeago from './time_ago.vue'; +import CommitComponent from '~/vue_shared/components/commit.vue'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; +import Icon from '~/vue_shared/components/icon.vue'; +import { PIPELINES_TABLE } from '../../constants'; + +/** + * Pipeline table row. + * + * Given the received object renders a table row in the pipelines' table. + */ +export default { + components: { + PipelinesActionsComponent, + PipelinesArtifactsComponent, + CommitComponent, + PipelineStage, + PipelineUrl, + PipelineTriggerer, + CiBadge, + PipelinesTimeago, + LoadingButton, + Icon, + }, + props: { + pipeline: { + type: Object, + required: true, + }, + pipelineScheduleUrl: { + type: String, + required: false, + default: '', + }, + updateGraphDropdown: { + type: Boolean, + required: false, + default: false, + }, + autoDevopsHelpPath: { + type: String, + required: true, + }, + viewType: { + type: String, + required: true, + }, + cancelingPipeline: { + type: Number, + required: false, + default: null, + }, + }, + pipelinesTable: PIPELINES_TABLE, + data() { + return { + isRetrying: false, + }; + }, + computed: { + actions() { + if (!this.pipeline || !this.pipeline.details) { + return []; + } + const { details } = this.pipeline; + return [...(details.manual_actions || []), ...(details.scheduled_actions || [])]; + }, + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * This field needs a lot of verification, because of different possible cases: + * + * 1. person who is an author of a commit might be a GitLab user + * 2. if person who is an author of a commit is a GitLab user, they can have a GitLab avatar + * 3. If GitLab user does not have avatar they might have a Gravatar + * 4. If committer is not a GitLab User they can have a Gravatar + * 5. We do not have consistent API object in this case + * 6. We should improve API and the code + * + * @returns {Object|Undefined} + */ + commitAuthor() { + let commitAuthorInformation; + + if (!this.pipeline || !this.pipeline.commit) { + return null; + } + + // 1. person who is an author of a commit might be a GitLab user + if (this.pipeline.commit.author) { + // 2. if person who is an author of a commit is a GitLab user + // they can have a GitLab avatar + if (this.pipeline.commit.author.avatar_url) { + commitAuthorInformation = this.pipeline.commit.author; + + // 3. If GitLab user does not have avatar, they might have a Gravatar + } else if (this.pipeline.commit.author_gravatar_url) { + commitAuthorInformation = { + ...this.pipeline.commit.author, + avatar_url: this.pipeline.commit.author_gravatar_url, + }; + } + // 4. If committer is not a GitLab User, they can have a Gravatar + } else { + commitAuthorInformation = { + avatar_url: this.pipeline.commit.author_gravatar_url, + path: `mailto:${this.pipeline.commit.author_email}`, + username: this.pipeline.commit.author_name, + }; + } + + return commitAuthorInformation; + }, + + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTag() { + if (this.pipeline.ref && this.pipeline.ref.tag) { + return this.pipeline.ref.tag; + } + return undefined; + }, + + /** + * If provided, returns the commit ref. + * Needed to render the commit component column. + * + * Matches `path` prop sent in the API to `ref_url` prop needed + * in the commit component. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.pipeline.ref) { + return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { + if (prop === 'path') { + accumulator.ref_url = this.pipeline.ref[prop]; + } else { + accumulator[prop] = this.pipeline.ref[prop]; + } + return accumulator; + }, {}); + } + + return undefined; + }, + + /** + * If provided, returns the commit url. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitUrl() { + if (this.pipeline.commit && this.pipeline.commit.commit_path) { + return this.pipeline.commit.commit_path; + } + return undefined; + }, + + /** + * If provided, returns the commit short sha. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitShortSha() { + if (this.pipeline.commit && this.pipeline.commit.short_id) { + return this.pipeline.commit.short_id; + } + return undefined; + }, + + /** + * If provided, returns the commit title. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.pipeline.commit && this.pipeline.commit.title) { + return this.pipeline.commit.title; + } + return undefined; + }, + + /** + * Timeago components expects a number + * + * @return {type} description + */ + pipelineDuration() { + if (this.pipeline.details && this.pipeline.details.duration) { + return this.pipeline.details.duration; + } + + return 0; + }, + + /** + * Timeago component expects a String. + * + * @return {String} + */ + pipelineFinishedAt() { + if (this.pipeline.details && this.pipeline.details.finished_at) { + return this.pipeline.details.finished_at; + } + + return ''; + }, + + pipelineStatus() { + if (this.pipeline.details && this.pipeline.details.status) { + return this.pipeline.details.status; + } + return {}; + }, + + displayPipelineActions() { + return ( + this.pipeline.flags.retryable || + this.pipeline.flags.cancelable || + this.pipeline.details.manual_actions.length || + this.pipeline.details.artifacts.length + ); + }, + + isChildView() { + return this.viewType === 'child'; + }, + + isCancelling() { + return this.cancelingPipeline === this.pipeline.id; + }, + }, + watch: { + pipeline() { + this.isRetrying = false; + }, + }, + methods: { + handleCancelClick() { + eventHub.$emit('openConfirmationModal', { + pipeline: this.pipeline, + endpoint: this.pipeline.cancel_path, + }); + }, + handleRetryClick() { + this.isRetrying = true; + eventHub.$emit('retryPipeline', this.pipeline.retry_path); + }, + }, +}; +</script> +<template> + <div class="commit gl-responsive-table-row"> + <div class="table-section section-10 commit-link"> + <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Status') }}</div> + <div class="table-mobile-content"> + <ci-badge + :status="pipelineStatus" + :show-text="!isChildView" + data-qa-selector="pipeline_commit_status" + /> + </div> + </div> + + <pipeline-url + :pipeline="pipeline" + :pipeline-schedule-url="pipelineScheduleUrl" + :auto-devops-help-path="autoDevopsHelpPath" + /> + <pipeline-triggerer :pipeline="pipeline" /> + + <div class="table-section section-wrap section-20"> + <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Commit') }}</div> + <div class="table-mobile-content"> + <commit-component + :tag="commitTag" + :commit-ref="commitRef" + :commit-url="commitUrl" + :merge-request-ref="pipeline.merge_request" + :short-sha="commitShortSha" + :title="commitTitle" + :author="commitAuthor" + :show-ref-info="!isChildView" + /> + </div> + </div> + + <div class="table-section section-wrap section-15 stage-cell"> + <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Stages') }}</div> + <div class="table-mobile-content"> + <template v-if="pipeline.details.stages.length > 0"> + <div + v-for="(stage, index) in pipeline.details.stages" + :key="index" + class="stage-container dropdown" + data-testid="widget-mini-pipeline-graph" + > + <pipeline-stage + :type="$options.pipelinesTable" + :stage="stage" + :update-dropdown="updateGraphDropdown" + /> + </div> + </template> + </div> + </div> + + <pipelines-timeago :duration="pipelineDuration" :finished-time="pipelineFinishedAt" /> + + <div + v-if="displayPipelineActions" + class="table-section section-20 table-button-footer pipeline-actions" + > + <div class="btn-group table-action-buttons"> + <pipelines-actions-component v-if="actions.length > 0" :actions="actions" /> + + <pipelines-artifacts-component + v-if="pipeline.details.artifacts.length" + :artifacts="pipeline.details.artifacts" + class="d-md-block" + /> + + <loading-button + v-if="pipeline.flags.retryable" + :loading="isRetrying" + :disabled="isRetrying" + container-class="js-pipelines-retry-button btn btn-default btn-retry" + data-qa-selector="pipeline_retry_button" + @click="handleRetryClick" + > + <icon name="repeat" /> + </loading-button> + + <loading-button + v-if="pipeline.flags.cancelable" + :loading="isCancelling" + :disabled="isCancelling" + data-toggle="modal" + data-target="#confirmation-modal" + container-class="js-pipelines-cancel-button btn btn-remove" + @click="handleCancelClick" + > + <icon name="close" /> + </loading-button> + </div> + </div> + </div> +</template> |