2017-08-17 22:00:37 +05:30
|
|
|
<script>
|
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-03-02 22:35:43 +05:30
|
|
|
import GraphMixin from '../../mixins/graph_component_mixin';
|
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';
|
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,
|
|
|
|
},
|
|
|
|
mixins: [GraphMixin, GraphWidthMixin, GraphBundleMixin],
|
|
|
|
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,
|
|
|
|
default: 'main',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
upstream: 'upstream',
|
|
|
|
downstream: 'downstream',
|
|
|
|
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: {
|
|
|
|
hasTriggeredBy() {
|
|
|
|
return (
|
|
|
|
this.type !== this.$options.downstream &&
|
|
|
|
this.triggeredByPipelines &&
|
|
|
|
this.pipeline.triggered_by !== null
|
|
|
|
);
|
|
|
|
},
|
|
|
|
triggeredByPipelines() {
|
|
|
|
return this.pipeline.triggered_by;
|
|
|
|
},
|
|
|
|
hasTriggered() {
|
|
|
|
return (
|
|
|
|
this.type !== this.$options.upstream &&
|
|
|
|
this.triggeredPipelines &&
|
|
|
|
this.pipeline.triggered.length > 0
|
|
|
|
);
|
|
|
|
},
|
|
|
|
triggeredPipelines() {
|
|
|
|
return this.pipeline.triggered;
|
|
|
|
},
|
|
|
|
expandedTriggeredBy() {
|
|
|
|
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)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
expandedTriggered() {
|
|
|
|
return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
|
|
|
|
},
|
|
|
|
pipelineTypeUpstream() {
|
|
|
|
return this.type !== this.$options.downstream && this.expandedTriggeredBy;
|
|
|
|
},
|
|
|
|
pipelineTypeDownstream() {
|
|
|
|
return this.type !== this.$options.upstream && this.expandedTriggered;
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
pipelineProjectId() {
|
|
|
|
return this.pipeline.project.id;
|
|
|
|
},
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
|
|
|
methods: {
|
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
|
|
|
|
*/
|
|
|
|
if (this.expandedTriggered?.id !== pipeline.id) {
|
|
|
|
this.$emit('onResetTriggered', this.pipeline, pipeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$emit('onClickTriggered', 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;
|
|
|
|
},
|
|
|
|
hasUpstream(index) {
|
|
|
|
return index === 0 && this.hasTriggeredBy;
|
|
|
|
},
|
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"
|
|
|
|
type="upstream"
|
|
|
|
class="d-inline-block upstream-pipeline"
|
|
|
|
:class="`js-upstream-pipeline-${expandedTriggeredBy.id}`"
|
|
|
|
:is-loading="false"
|
|
|
|
:pipeline="expandedTriggeredBy"
|
|
|
|
:is-linked-pipeline="true"
|
|
|
|
:mediator="mediator"
|
2020-04-22 19:07:51 +05:30
|
|
|
@onClickTriggeredBy="clickTriggeredByPipeline"
|
2019-12-21 20:55:43 +05:30
|
|
|
@refreshPipelineGraph="requestRefreshPipelineGraph"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<linked-pipelines-column
|
|
|
|
v-if="hasTriggeredBy"
|
|
|
|
:linked-pipelines="triggeredByPipelines"
|
|
|
|
:column-title="__('Upstream')"
|
2020-03-13 15:44:24 +05:30
|
|
|
:project-id="pipelineProjectId"
|
2019-12-21 20:55:43 +05:30
|
|
|
graph-position="left"
|
2020-04-22 19:07:51 +05:30
|
|
|
@linkedPipelineClick="$emit('onClickTriggeredBy', $event)"
|
2019-12-21 20:55:43 +05:30
|
|
|
/>
|
|
|
|
|
|
|
|
<ul
|
|
|
|
v-if="!isLoading"
|
|
|
|
:class="{
|
|
|
|
'inline js-has-linked-pipelines': hasTriggered || hasTriggeredBy,
|
|
|
|
}"
|
|
|
|
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="{
|
2020-10-24 23:57:45 +05:30
|
|
|
'has-upstream gl-ml-11': hasUpstream(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)"
|
2019-12-21 20:55:43 +05:30
|
|
|
:has-triggered-by="hasTriggeredBy"
|
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
|
|
|
|
v-if="hasTriggered"
|
|
|
|
:linked-pipelines="triggeredPipelines"
|
|
|
|
:column-title="__('Downstream')"
|
2020-03-13 15:44:24 +05:30
|
|
|
:project-id="pipelineProjectId"
|
2019-12-21 20:55:43 +05:30
|
|
|
graph-position="right"
|
|
|
|
@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"
|
|
|
|
type="downstream"
|
|
|
|
class="d-inline-block"
|
|
|
|
:class="`js-downstream-pipeline-${expandedTriggered.id}`"
|
|
|
|
:is-loading="false"
|
|
|
|
:pipeline="expandedTriggered"
|
|
|
|
: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"
|
2020-04-22 19:07:51 +05:30
|
|
|
@onClickTriggered="clickTriggeredPipeline"
|
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>
|