2017-08-17 22:00:37 +05:30
|
|
|
<script>
|
2021-01-03 14:25:43 +05:30
|
|
|
import { escape, capitalize } from 'lodash';
|
2019-02-15 15:39:39 +05:30
|
|
|
import { GlLoadingIcon } from '@gitlab/ui';
|
2018-05-09 12:01:36 +05:30
|
|
|
import StageColumnComponent from './stage_column_component.vue';
|
2019-12-21 20:55:43 +05:30
|
|
|
import GraphWidthMixin from '../../mixins/graph_width_mixin';
|
|
|
|
import LinkedPipelinesColumn from './linked_pipelines_column.vue';
|
|
|
|
import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
|
2021-01-29 00:20:46 +05:30
|
|
|
import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
export default {
|
2019-12-21 20:55:43 +05:30
|
|
|
name: 'PipelineGraph',
|
2018-05-09 12:01:36 +05:30
|
|
|
components: {
|
|
|
|
StageColumnComponent,
|
2018-12-13 13:39:08 +05:30
|
|
|
GlLoadingIcon,
|
2019-12-21 20:55:43 +05:30
|
|
|
LinkedPipelinesColumn,
|
|
|
|
},
|
2021-01-03 14:25:43 +05:30
|
|
|
mixins: [GraphWidthMixin, GraphBundleMixin],
|
2019-12-21 20:55:43 +05:30
|
|
|
props: {
|
|
|
|
isLoading: {
|
|
|
|
type: Boolean,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
pipeline: {
|
|
|
|
type: Object,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
isLinkedPipeline: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
mediator: {
|
|
|
|
type: Object,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
type: {
|
|
|
|
type: String,
|
|
|
|
required: false,
|
2021-01-29 00:20:46 +05:30
|
|
|
default: MAIN,
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
upstream: UPSTREAM,
|
|
|
|
downstream: DOWNSTREAM,
|
2019-12-21 20:55:43 +05:30
|
|
|
data() {
|
|
|
|
return {
|
2020-03-13 15:44:24 +05:30
|
|
|
downstreamMarginTop: null,
|
2020-07-28 23:09:34 +05:30
|
|
|
jobName: null,
|
2020-11-24 15:15:51 +05:30
|
|
|
pipelineExpanded: {
|
|
|
|
jobName: '',
|
|
|
|
expanded: false,
|
|
|
|
},
|
2019-12-21 20:55:43 +05:30
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
2021-01-03 14:25:43 +05:30
|
|
|
graph() {
|
|
|
|
return this.pipeline.details?.stages;
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
hasUpstream() {
|
2019-12-21 20:55:43 +05:30
|
|
|
return (
|
|
|
|
this.type !== this.$options.downstream &&
|
2021-01-29 00:20:46 +05:30
|
|
|
this.upstreamPipelines &&
|
2019-12-21 20:55:43 +05:30
|
|
|
this.pipeline.triggered_by !== null
|
|
|
|
);
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
upstreamPipelines() {
|
2019-12-21 20:55:43 +05:30
|
|
|
return this.pipeline.triggered_by;
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
hasDownstream() {
|
2019-12-21 20:55:43 +05:30
|
|
|
return (
|
|
|
|
this.type !== this.$options.upstream &&
|
2021-01-29 00:20:46 +05:30
|
|
|
this.downstreamPipelines &&
|
2019-12-21 20:55:43 +05:30
|
|
|
this.pipeline.triggered.length > 0
|
|
|
|
);
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
downstreamPipelines() {
|
2019-12-21 20:55:43 +05:30
|
|
|
return this.pipeline.triggered;
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
expandedUpstream() {
|
2019-12-21 20:55:43 +05:30
|
|
|
return (
|
|
|
|
this.pipeline.triggered_by &&
|
2020-03-13 15:44:24 +05:30
|
|
|
Array.isArray(this.pipeline.triggered_by) &&
|
2019-12-21 20:55:43 +05:30
|
|
|
this.pipeline.triggered_by.find(el => el.isExpanded)
|
|
|
|
);
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
expandedDownstream() {
|
2019-12-21 20:55:43 +05:30
|
|
|
return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
|
|
|
|
},
|
|
|
|
pipelineTypeUpstream() {
|
2021-01-29 00:20:46 +05:30
|
|
|
return this.type !== this.$options.downstream && this.expandedUpstream;
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
|
|
|
pipelineTypeDownstream() {
|
2021-01-29 00:20:46 +05:30
|
|
|
return this.type !== this.$options.upstream && this.expandedDownstream;
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
pipelineProjectId() {
|
|
|
|
return this.pipeline.project.id;
|
|
|
|
},
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
|
|
|
methods: {
|
2021-01-03 14:25:43 +05:30
|
|
|
capitalizeStageName(name) {
|
|
|
|
const escapedName = escape(name);
|
|
|
|
return capitalize(escapedName);
|
|
|
|
},
|
|
|
|
isFirstColumn(index) {
|
|
|
|
return index === 0;
|
|
|
|
},
|
|
|
|
stageConnectorClass(index, stage) {
|
|
|
|
let className;
|
|
|
|
|
|
|
|
// If it's the first stage column and only has one job
|
|
|
|
if (this.isFirstColumn(index) && stage.groups.length === 1) {
|
|
|
|
className = 'no-margin';
|
|
|
|
} else if (index > 0) {
|
|
|
|
// If it is not the first column
|
|
|
|
className = 'left-margin';
|
|
|
|
}
|
|
|
|
|
|
|
|
return className;
|
|
|
|
},
|
|
|
|
refreshPipelineGraph() {
|
|
|
|
this.$emit('refreshPipelineGraph');
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* CSS class is applied:
|
|
|
|
* - if pipeline graph contains only one stage column component
|
|
|
|
*
|
|
|
|
* @param {number} index
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
shouldAddRightMargin(index) {
|
|
|
|
return !(index === this.graph.length - 1);
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
handleClickedDownstream(pipeline, clickedIndex, downstreamNode) {
|
|
|
|
/**
|
|
|
|
* Calculates the margin top of the clicked downstream pipeline by
|
|
|
|
* subtracting the clicked downstream pipelines offsetTop by it's parent's
|
2020-07-28 23:09:34 +05:30
|
|
|
* offsetTop and then subtracting 15
|
2020-03-13 15:44:24 +05:30
|
|
|
*/
|
2020-07-28 23:09:34 +05:30
|
|
|
this.downstreamMarginTop = this.calculateMarginTop(downstreamNode, 15);
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
/**
|
|
|
|
* If the expanded trigger is defined and the id is different than the
|
|
|
|
* pipeline we clicked, then it means we clicked on a sibling downstream link
|
|
|
|
* and we want to reset the pipeline store. Triggering the reset without
|
|
|
|
* this condition would mean not allowing downstreams of downstreams to expand
|
|
|
|
*/
|
2021-01-29 00:20:46 +05:30
|
|
|
if (this.expandedDownstream?.id !== pipeline.id) {
|
|
|
|
this.$emit('onResetDownstream', this.pipeline, pipeline);
|
2020-04-22 19:07:51 +05:30
|
|
|
}
|
|
|
|
|
2021-01-29 00:20:46 +05:30
|
|
|
this.$emit('onClickDownstreamPipeline', pipeline);
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
calculateMarginTop(downstreamNode, pixelDiff) {
|
|
|
|
return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
|
|
|
|
},
|
2019-12-21 20:55:43 +05:30
|
|
|
hasOnlyOneJob(stage) {
|
|
|
|
return stage.groups.length === 1;
|
|
|
|
},
|
2021-01-29 00:20:46 +05:30
|
|
|
hasUpstreamColumn(index) {
|
|
|
|
return index === 0 && this.hasUpstream;
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
2020-07-28 23:09:34 +05:30
|
|
|
setJob(jobName) {
|
|
|
|
this.jobName = jobName;
|
|
|
|
},
|
2020-11-24 15:15:51 +05:30
|
|
|
setPipelineExpanded(jobName, expanded) {
|
|
|
|
if (expanded) {
|
|
|
|
this.pipelineExpanded = {
|
|
|
|
jobName,
|
|
|
|
expanded,
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
this.pipelineExpanded = {
|
|
|
|
expanded,
|
|
|
|
jobName: '',
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
2018-05-09 12:01:36 +05:30
|
|
|
},
|
|
|
|
};
|
2017-08-17 22:00:37 +05:30
|
|
|
</script>
|
|
|
|
<template>
|
|
|
|
<div class="build-content middle-block js-pipeline-graph">
|
2019-12-21 20:55:43 +05:30
|
|
|
<div
|
|
|
|
class="pipeline-visualization pipeline-graph"
|
|
|
|
:class="{ 'pipeline-tab-content': !isLinkedPipeline }"
|
|
|
|
>
|
2019-12-04 20:38:33 +05:30
|
|
|
<div
|
|
|
|
:style="{
|
|
|
|
paddingLeft: `${graphLeftPadding}px`,
|
|
|
|
paddingRight: `${graphRightPadding}px`,
|
|
|
|
}"
|
|
|
|
>
|
2020-04-22 19:07:51 +05:30
|
|
|
<gl-loading-icon v-if="isLoading" class="m-auto" size="lg" />
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
<pipeline-graph
|
|
|
|
v-if="pipelineTypeUpstream"
|
2021-01-29 00:20:46 +05:30
|
|
|
:type="$options.upstream"
|
2019-12-21 20:55:43 +05:30
|
|
|
class="d-inline-block upstream-pipeline"
|
2021-01-29 00:20:46 +05:30
|
|
|
:class="`js-upstream-pipeline-${expandedUpstream.id}`"
|
2019-12-21 20:55:43 +05:30
|
|
|
:is-loading="false"
|
2021-01-29 00:20:46 +05:30
|
|
|
:pipeline="expandedUpstream"
|
2019-12-21 20:55:43 +05:30
|
|
|
:is-linked-pipeline="true"
|
|
|
|
:mediator="mediator"
|
2021-01-29 00:20:46 +05:30
|
|
|
@onClickUpstreamPipeline="clickUpstreamPipeline"
|
2019-12-21 20:55:43 +05:30
|
|
|
@refreshPipelineGraph="requestRefreshPipelineGraph"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<linked-pipelines-column
|
2021-01-29 00:20:46 +05:30
|
|
|
v-if="hasUpstream"
|
|
|
|
:type="$options.upstream"
|
|
|
|
:linked-pipelines="upstreamPipelines"
|
2019-12-21 20:55:43 +05:30
|
|
|
:column-title="__('Upstream')"
|
2020-03-13 15:44:24 +05:30
|
|
|
:project-id="pipelineProjectId"
|
2021-01-29 00:20:46 +05:30
|
|
|
@linkedPipelineClick="$emit('onClickUpstreamPipeline', $event)"
|
2019-12-21 20:55:43 +05:30
|
|
|
/>
|
|
|
|
|
|
|
|
<ul
|
|
|
|
v-if="!isLoading"
|
|
|
|
:class="{
|
2021-01-29 00:20:46 +05:30
|
|
|
'inline js-has-linked-pipelines': hasDownstream || hasUpstream,
|
2019-12-21 20:55:43 +05:30
|
|
|
}"
|
|
|
|
class="stage-column-list align-top"
|
|
|
|
>
|
2019-12-04 20:38:33 +05:30
|
|
|
<stage-column-component
|
|
|
|
v-for="(stage, index) in graph"
|
|
|
|
:key="stage.name"
|
|
|
|
:class="{
|
2021-01-29 00:20:46 +05:30
|
|
|
'has-upstream gl-ml-11': hasUpstreamColumn(index),
|
2019-12-21 20:55:43 +05:30
|
|
|
'has-only-one-job': hasOnlyOneJob(stage),
|
2020-07-28 23:09:34 +05:30
|
|
|
'gl-mr-26': shouldAddRightMargin(index),
|
2019-12-04 20:38:33 +05:30
|
|
|
}"
|
|
|
|
:title="capitalizeStageName(stage.name)"
|
|
|
|
:groups="stage.groups"
|
|
|
|
:stage-connector-class="stageConnectorClass(index, stage)"
|
|
|
|
:is-first-column="isFirstColumn(index)"
|
2021-01-29 00:20:46 +05:30
|
|
|
:has-upstream="hasUpstream"
|
2019-12-04 20:38:33 +05:30
|
|
|
:action="stage.status.action"
|
2020-07-28 23:09:34 +05:30
|
|
|
:job-hovered="jobName"
|
2020-11-24 15:15:51 +05:30
|
|
|
:pipeline-expanded="pipelineExpanded"
|
2019-12-04 20:38:33 +05:30
|
|
|
@refreshPipelineGraph="refreshPipelineGraph"
|
|
|
|
/>
|
|
|
|
</ul>
|
2019-12-21 20:55:43 +05:30
|
|
|
|
|
|
|
<linked-pipelines-column
|
2021-01-29 00:20:46 +05:30
|
|
|
v-if="hasDownstream"
|
|
|
|
:type="$options.downstream"
|
|
|
|
:linked-pipelines="downstreamPipelines"
|
2019-12-21 20:55:43 +05:30
|
|
|
:column-title="__('Downstream')"
|
2020-03-13 15:44:24 +05:30
|
|
|
:project-id="pipelineProjectId"
|
2019-12-21 20:55:43 +05:30
|
|
|
@linkedPipelineClick="handleClickedDownstream"
|
2020-07-28 23:09:34 +05:30
|
|
|
@downstreamHovered="setJob"
|
2020-11-24 15:15:51 +05:30
|
|
|
@pipelineExpandToggle="setPipelineExpanded"
|
2019-12-21 20:55:43 +05:30
|
|
|
/>
|
|
|
|
|
|
|
|
<pipeline-graph
|
|
|
|
v-if="pipelineTypeDownstream"
|
2021-01-29 00:20:46 +05:30
|
|
|
:type="$options.downstream"
|
2019-12-21 20:55:43 +05:30
|
|
|
class="d-inline-block"
|
2021-01-29 00:20:46 +05:30
|
|
|
:class="`js-downstream-pipeline-${expandedDownstream.id}`"
|
2019-12-21 20:55:43 +05:30
|
|
|
:is-loading="false"
|
2021-01-29 00:20:46 +05:30
|
|
|
:pipeline="expandedDownstream"
|
2019-12-21 20:55:43 +05:30
|
|
|
:is-linked-pipeline="true"
|
2020-03-13 15:44:24 +05:30
|
|
|
:style="{ 'margin-top': downstreamMarginTop }"
|
2019-12-21 20:55:43 +05:30
|
|
|
:mediator="mediator"
|
2021-01-29 00:20:46 +05:30
|
|
|
@onClickDownstreamPipeline="clickDownstreamPipeline"
|
2019-12-21 20:55:43 +05:30
|
|
|
@refreshPipelineGraph="requestRefreshPipelineGraph"
|
|
|
|
/>
|
2019-12-04 20:38:33 +05:30
|
|
|
</div>
|
2017-08-17 22:00:37 +05:30
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|