Commit 8ef5ee8e authored by Filipa Lacerda's avatar Filipa Lacerda Committed by Kamil Trzciński

Recursively renders upstream/downstream:

Transforms the graph component to recursively
render both upstream and downstream pipelines
parent c0257f10
...@@ -43,20 +43,17 @@ export default () => { ...@@ -43,20 +43,17 @@ export default () => {
props: { props: {
isLoading: this.mediator.state.isLoading, isLoading: this.mediator.state.isLoading,
pipeline: this.mediator.store.state.pipeline, pipeline: this.mediator.store.state.pipeline,
// EE-only start mediator: this.mediator,
triggeredPipelines: this.mediator.store.state.triggeredPipelines,
triggered: this.mediator.store.state.triggered,
triggeredByPipelines: this.mediator.store.state.triggeredByPipelines,
triggeredBy: this.mediator.store.state.triggeredBy,
// EE-only end
}, },
on: { on: {
refreshPipelineGraph: this.requestRefreshPipelineGraph, refreshPipelineGraph: this.requestRefreshPipelineGraph,
// EE-only start // EE-only start
// refreshTriggeredPipelineGraph: this.mediator.refreshTriggeredByPipelineGraph, // refreshTriggeredPipelineGraph: this.mediator.refreshTriggeredByPipelineGraph,
// refreshTriggeredByPipelineGraph: this.mediator.refreshTriggeredByPipelineGraph, // refreshTriggeredByPipelineGraph: this.mediator.refreshTriggeredByPipelineGraph,
onClickTriggeredBy: pipeline => this.clickTriggeredByPipeline(pipeline), onClickTriggeredBy: (parentPipeline, pipeline) =>
onClickTriggered: pipeline => this.clickTriggeredPipeline(pipeline), this.clickTriggeredByPipeline(parentPipeline, pipeline),
onClickTriggered: (parentPipeline, pipeline) =>
this.clickTriggeredPipeline(parentPipeline, pipeline),
// EE-only end // EE-only end
}, },
}); });
......
...@@ -4,6 +4,7 @@ import { GlLoadingIcon } from '@gitlab/ui'; ...@@ -4,6 +4,7 @@ import { GlLoadingIcon } from '@gitlab/ui';
import LinkedPipelinesColumn from 'ee/pipelines/components/graph/linked_pipelines_column.vue'; import LinkedPipelinesColumn from 'ee/pipelines/components/graph/linked_pipelines_column.vue';
import EEGraphMixin from 'ee/pipelines/mixins/graph_component_mixin'; import EEGraphMixin from 'ee/pipelines/mixins/graph_component_mixin';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue'; import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import GraphEEMixin from 'ee/pipelines/mixins/graph_pipeline_bundle_mixin'; // eslint-disable-line import/order
export default { export default {
name: 'PipelineGraph', name: 'PipelineGraph',
...@@ -12,7 +13,7 @@ export default { ...@@ -12,7 +13,7 @@ export default {
StageColumnComponent, StageColumnComponent,
GlLoadingIcon, GlLoadingIcon,
}, },
mixins: [EEGraphMixin], mixins: [EEGraphMixin, GraphEEMixin],
props: { props: {
isLoading: { isLoading: {
type: Boolean, type: Boolean,
...@@ -22,6 +23,19 @@ export default { ...@@ -22,6 +23,19 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
isLinkedPipeline: {
type: Boolean,
default: false,
},
mediator: {
type: Object,
required: true,
},
type: {
type: String,
required: false,
default: 'main',
},
}, },
computed: { computed: {
graph() { graph() {
...@@ -29,6 +43,7 @@ export default { ...@@ -29,6 +43,7 @@ export default {
}, },
}, },
methods: { methods: {
// todo filipa: move this into a ce mixin
capitalizeStageName(name) { capitalizeStageName(name) {
const escapedName = _.escape(name); const escapedName = _.escape(name);
return escapedName.charAt(0).toUpperCase() + escapedName.slice(1); return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
...@@ -60,15 +75,31 @@ export default { ...@@ -60,15 +75,31 @@ export default {
</script> </script>
<template> <template>
<div class="build-content middle-block js-pipeline-graph"> <div class="build-content middle-block js-pipeline-graph">
<div class="pipeline-visualization pipeline-graph pipeline-tab-content"> <div
<div class="text-center"><gl-loading-icon v-if="isLoading" :size="3" /></div> class="pipeline-visualization pipeline-graph"
:class="{ 'pipeline-tab-content': !isLinkedPipeline }"
>
<div class="text-center" v-if="isLoading"><gl-loading-icon :size="3" /></div>
<pipeline-graph
type="upstream"
class="d-inline-block upstream-pipeline"
v-if="expandedTriggeredBy && type !== 'downstream'"
:is-loading="false"
:pipeline="expandedTriggeredBy"
:is-linked-pipeline="true"
:mediator="mediator"
@onClickTriggeredBy="
(parentPipeline, pipeline) => this.clickTriggeredByPipeline(parentPipeline, pipeline)
"
/>
<linked-pipelines-column <linked-pipelines-column
v-if="hasTriggeredBy" v-if="hasTriggeredBy"
:linked-pipelines="triggeredByPipelines" :linked-pipelines="triggeredByPipelines"
:column-title="__('Upstream')" :column-title="__('Upstream')"
graph-position="left" graph-position="left"
@linkedPipelineClick="pipeline => $emit('onClickTriggeredBy', pipeline)" @linkedPipelineClick="pipeline => $emit('onClickTriggeredBy', this.pipeline, pipeline)"
/> />
<ul <ul
...@@ -102,6 +133,20 @@ export default { ...@@ -102,6 +133,20 @@ export default {
graph-position="right" graph-position="right"
@linkedPipelineClick="handleClickedDownstream" @linkedPipelineClick="handleClickedDownstream"
/> />
<pipeline-graph
type="downstream"
class="d-inline-block"
v-if="expandedTriggered && type !== 'upstream'"
:is-loading="false"
:pipeline="expandedTriggered"
:is-linked-pipeline="true"
:style="{ 'margin-top': marginTop }"
@onClickTriggered="
(parentPipeline, pipeline) => this.clickTriggeredPipeline(parentPipeline, pipeline)
"
:mediator="mediator"
/>
</div> </div>
</div> </div>
</template> </template>
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
return this.pipeline.details.status; return this.pipeline.details.status;
}, },
projectName() { projectName() {
return this.pipeline.project.name; return this.pipeline.project && this.pipeline.project.name;
}, },
}, },
methods: { methods: {
......
// todo filipa: move this to the ee graph component
import _ from 'underscore';
export default { export default {
data() { data() {
return { return {
...@@ -6,17 +9,34 @@ export default { ...@@ -6,17 +9,34 @@ export default {
}, },
computed: { computed: {
hasTriggeredBy() { hasTriggeredBy() {
return this.pipeline.triggered_by && this.pipeline.triggered_by != null; return (
this.type !== 'downstream' &&
this.pipeline.triggered_by &&
this.pipeline.triggered_by != null
);
}, },
triggeredByPipelines() { triggeredByPipelines() {
return this.pipeline.triggered_by; return this.pipeline.triggered_by;
}, },
hasTriggered() { hasTriggered() {
return this.pipeline.triggered && this.pipeline.triggered.length > 0; return (
this.type !== 'upstream' && this.pipeline.triggered && this.pipeline.triggered.length > 0
);
}, },
triggeredPipelines() { triggeredPipelines() {
return this.pipeline.triggered; return this.pipeline.triggered;
}, },
expandedTriggeredBy() {
return (
this.pipeline.triggered_by &&
_.isArray(this.pipeline.triggered_by) &&
this.pipeline.triggered_by.find(el => el.isExpanded)
);
},
expandedTriggered() {
return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
},
/** /**
* Calculates the margin top of the clicked downstream pipeline by * Calculates the margin top of the clicked downstream pipeline by
* adding the height of each linked pipeline and the margin * adding the height of each linked pipeline and the margin
...@@ -34,8 +54,9 @@ export default { ...@@ -34,8 +54,9 @@ export default {
this.$emit('refreshTriggeredByPipelineGraph'); this.$emit('refreshTriggeredByPipelineGraph');
}, },
handleClickedDownstream(pipeline, clickedIndex) { handleClickedDownstream(pipeline, clickedIndex) {
//filipa: figure out the clickIndex thing
this.triggeredTopIndex = clickedIndex; this.triggeredTopIndex = clickedIndex;
this.$emit('onClickTriggered', pipeline); this.$emit('onClickTriggered', this.pipeline, pipeline);
}, },
}, },
}; };
import keys from '../constants';
export default { export default {
methods: { methods: {
/** /**
...@@ -13,19 +11,28 @@ export default { ...@@ -13,19 +11,28 @@ export default {
* @param {String} resetStoreKey Store key for the visible pipeline that will need to be reset * @param {String} resetStoreKey Store key for the visible pipeline that will need to be reset
* @param {Object} pipeline The clicked pipeline * @param {Object} pipeline The clicked pipeline
*/ */
clickPipeline(pipeline, method) { clickPipeline(parentPipeline, pipeline, openMethod, closeMethod) {
debugger;
if (!pipeline.isExpanded) { if (!pipeline.isExpanded) {
this.mediator.store[method](pipeline); this.mediator.store[openMethod](parentPipeline, pipeline);
} else { } else {
this.mediator.store.closePipeline(pipeline); this.mediator.store[closeMethod](pipeline);
} }
}, },
clickTriggeredByPipeline(pipeline) { clickTriggeredByPipeline(parentPipeline, pipeline) {
this.clickPipeline(pipeline, 'openTriggeredByPipeline'); this.clickPipeline(
parentPipeline,
pipeline,
'openTriggeredByPipeline',
'closeTriggeredByPipeline',
);
}, },
clickTriggeredPipeline(pipeline) { clickTriggeredPipeline(parentPipeline, pipeline) {
this.clickPipeline(pipeline, 'openTriggeredPipeline'); this.clickPipeline(
parentPipeline,
pipeline,
'openTriggeredPipeline',
'closeTriggeredPipeline',
);
}, },
}, },
}; };
import _ from 'underscore';
import CePipelineStore from '~/pipelines/stores/pipeline_store'; import CePipelineStore from '~/pipelines/stores/pipeline_store';
import data from '../mock.json'; import data from '../mock.json';
import Vue from 'vue'; import Vue from 'vue';
...@@ -11,9 +12,9 @@ export default class PipelineStore extends CePipelineStore { ...@@ -11,9 +12,9 @@ export default class PipelineStore extends CePipelineStore {
} }
/** /**
* For the triggered pipelines, parses them to add `isLoading` and `isExpanded` keys * For the triggered pipelines adds the `isExpanded` key
* *
* For the triggered_by pipeline, parsed the object to add `isLoading` and `isExpanded` keys * For the triggered_by pipeline adds the `isExpanded` key
* and saves it as an array * and saves it as an array
* *
* @param {Object} pipeline * @param {Object} pipeline
...@@ -24,13 +25,12 @@ export default class PipelineStore extends CePipelineStore { ...@@ -24,13 +25,12 @@ export default class PipelineStore extends CePipelineStore {
if (pipeline.triggered_by) { if (pipeline.triggered_by) {
this.state.pipeline.triggered_by = [pipeline.triggered_by]; this.state.pipeline.triggered_by = [pipeline.triggered_by];
this.parseTriggeredByPipelines(this.state.pipeline, pipeline.triggered_by);
this.parseTriggeredByPipelines(this.state.pipeline.triggered_by[0]);
} }
if (pipeline.triggered && pipeline.triggered.length) { if (pipeline.triggered && pipeline.triggered.length) {
this.state.triggeredPipelines = pipeline.triggered.map(triggered => this.state.pipeline.triggered.forEach(el => this.parseTriggeredPipelines(el));
this.parseTriggeredPipelines(this.state.pipeline, triggered),
);
} }
} }
...@@ -44,40 +44,51 @@ export default class PipelineStore extends CePipelineStore { ...@@ -44,40 +44,51 @@ export default class PipelineStore extends CePipelineStore {
* @param {Array} parentPipeline * @param {Array} parentPipeline
* @param {Object} pipeline * @param {Object} pipeline
*/ */
parseTriggeredByPipelines(parentPipeline, pipeline) { parseTriggeredByPipelines(pipeline) {
// keep old value in case it's opened because we're polling // keep old value in case it's opened because we're polling
Vue.set(pipeline, 'isExpanded', pipeline.isExpanded || false); Vue.set(pipeline, 'isExpanded', pipeline.isExpanded || false);
if (pipeline.triggered_by) { if (pipeline.triggered_by) {
pipeline.triggered_by = [pipeline.triggered_by]; if (!_.isArray(pipeline.triggered_by)) {
this.parseTriggeredByPipelines(pipeline, pipeline.triggered_by); pipeline.triggered_by = [pipeline.triggered_by];
}
this.parseTriggeredByPipelines(pipeline.triggered_by[0]);
} }
// if (pipeline.triggered && pipeline.triggered.length) {
// pipeline.triggered.forEach(el => this.parseTriggeredPipelines(el));
// }
} }
parsePipeline(pipeline) {}
/** /**
* Recursively parses the triggered pipelines * Recursively parses the triggered pipelines
* @param {Array} parentPipeline * @param {Array} parentPipeline
* @param {Object} pipeline * @param {Object} pipeline
*/ */
parseTriggeredPipelines(parentPipeline, pipeline) { parseTriggeredPipelines(pipeline) {
// keep old value in case it's opened because we're polling // keep old value in case it's opened because we're polling
Vue.set(pipeline, 'isExpanded', pipeline.isExpanded || false); Vue.set(pipeline, 'isExpanded', pipeline.isExpanded || false);
if (pipeline.triggered && pipeline.triggered.length > 0) { if (pipeline.triggered && pipeline.triggered.length > 0) {
pipeline.triggered.forEach(el => this.parseTriggeredPipelines(el)); pipeline.triggered.forEach(el => this.parseTriggeredPipelines(el));
} }
// if (pipeline.triggered_by) {
// pipeline.triggered_by = [pipeline.triggered_by];
// this.parseTriggeredByPipelines(pipeline.triggered_by[0]);
// }
} }
/** /**
* Recursively resets all triggered by pipelines * Recursively resets all triggered by pipelines
* *
*
* @param {Object} pipeline * @param {Object} pipeline
*/ */
resetTriggeredByPipeline(pipeline) { resetTriggeredByPipeline(parentPipeline, pipeline) {
this.closePipeline(pipeline); parentPipeline.triggered_by.forEach(el => this.closePipeline(el));
if (pipeline.triggered_by && pipeline.triggered_by) { if (pipeline.triggered_by && pipeline.triggered_by) {
this.resetTriggeredByPipeline(pipeline.triggered_by); this.resetTriggeredByPipeline(pipeline, pipeline.triggered_by);
} }
} }
...@@ -85,15 +96,39 @@ export default class PipelineStore extends CePipelineStore { ...@@ -85,15 +96,39 @@ export default class PipelineStore extends CePipelineStore {
* Opens the clicked pipeline and closes all other ones. * Opens the clicked pipeline and closes all other ones.
* @param {Object} pipeline * @param {Object} pipeline
*/ */
openTriggeredByPipeline(pipeline) { openTriggeredByPipeline(parentPipeline, pipeline) {
// first we need to reset all triggeredBy pipelines // first we need to reset all triggeredBy pipelines
this.resetTriggeredByPipeline(pipeline); this.resetTriggeredByPipeline(parentPipeline, pipeline);
this.openPipeline(pipeline); this.openPipeline(pipeline);
} }
/** /**
* Closes the given pipeline * On click, will close the given pipeline and all nested triggered by pipelines
*
* @param {Object} pipeline
*/
closeTriggeredByPipeline(pipeline) {
this.closePipeline(pipeline);
if (pipeline.triggered_by && pipeline.triggered_by.length) {
pipeline.triggered_by.forEach(triggeredBy => this.closeTriggeredByPipeline(triggeredBy));
}
}
/**
* On click, will close the given pipeline and all the nested triggered ones
* @param {Object} pipeline
*/
closeTriggeredPipeline(pipeline) {
this.closePipeline(pipeline);
if (pipeline.triggered && pipeline.triggered.length) {
pipeline.triggered.forEach(triggered => this.closeTriggeredPipeline(triggered));
}
}
/**
* Utility function, Closes the given pipeline
* @param {Object} pipeline * @param {Object} pipeline
*/ */
closePipeline(pipeline) { closePipeline(pipeline) {
...@@ -101,19 +136,20 @@ export default class PipelineStore extends CePipelineStore { ...@@ -101,19 +136,20 @@ export default class PipelineStore extends CePipelineStore {
} }
/** /**
* Closes the given pipeline * Utility function, Opens the given pipeline
* @param {Object} pipeline * @param {Object} pipeline
*/ */
openPipeline(pipeline) { openPipeline(pipeline) {
Vue.set(pipeline, 'isExpanded', true); Vue.set(pipeline, 'isExpanded', true);
} }
/** /**
* Opens the clicked triggered pipeline and closes all other ones. * Opens the clicked triggered pipeline and closes all other ones.
* *
* @param {Object} pipeline * @param {Object} pipeline
*/ */
openTriggeredPipeline(pipeline) { openTriggeredPipeline(parentPipeline, pipeline) {
this.resetTriggeredPipelines(pipeline); this.resetTriggeredPipelines(parentPipeline, pipeline);
this.openPipeline(pipeline); this.openPipeline(pipeline);
} }
...@@ -122,11 +158,11 @@ export default class PipelineStore extends CePipelineStore { ...@@ -122,11 +158,11 @@ export default class PipelineStore extends CePipelineStore {
* *
* @param {Object} pipeline * @param {Object} pipeline
*/ */
resetTriggeredPipelines(pipeline) { resetTriggeredPipelines(parentPipeline, pipeline) {
this.closePipeline(pipeline); parentPipeline.triggered.forEach(el => this.closePipeline(el));
if (pipeline.triggered && pipeline.triggered.length) { if (pipeline.triggered && pipeline.triggered.length) {
pipeline.triggered.forEach(el => this.resetTriggeredPipelines(el)); pipeline.triggered.forEach(el => this.resetTriggeredPipelines(pipeline, el));
} }
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment