Commit b15f4a3f authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '276949-pipeline-restructure-7' into 'master'

Pipeline Graph Structural Update: Polling

See merge request gitlab-org/gitlab!49825
parents 21781af2 7843602e
...@@ -69,9 +69,6 @@ export default { ...@@ -69,9 +69,6 @@ export default {
}, },
}, },
methods: { methods: {
handleError(errorType) {
this.$emit('error', errorType);
},
setJob(jobName) { setJob(jobName) {
this.hoveredJobName = jobName; this.hoveredJobName = jobName;
}, },
...@@ -97,7 +94,7 @@ export default { ...@@ -97,7 +94,7 @@ export default {
:linked-pipelines="upstreamPipelines" :linked-pipelines="upstreamPipelines"
:column-title="__('Upstream')" :column-title="__('Upstream')"
:type="$options.pipelineTypeConstants.UPSTREAM" :type="$options.pipelineTypeConstants.UPSTREAM"
@error="handleError" @error="emit('error', errorType)"
/> />
</template> </template>
<template #main> <template #main>
...@@ -109,6 +106,7 @@ export default { ...@@ -109,6 +106,7 @@ export default {
:action="stage.status.action" :action="stage.status.action"
:job-hovered="hoveredJobName" :job-hovered="hoveredJobName"
:pipeline-expanded="pipelineExpanded" :pipeline-expanded="pipelineExpanded"
@refreshPipelineGraph="$emit('refreshPipelineGraph')"
/> />
</template> </template>
<template #downstream> <template #downstream>
...@@ -119,7 +117,7 @@ export default { ...@@ -119,7 +117,7 @@ export default {
:type="$options.pipelineTypeConstants.DOWNSTREAM" :type="$options.pipelineTypeConstants.DOWNSTREAM"
@downstreamHovered="setJob" @downstreamHovered="setJob"
@pipelineExpandToggle="togglePipelineExpanded" @pipelineExpandToggle="togglePipelineExpanded"
@error="handleError" @error="emit('error', errorType)"
/> />
</template> </template>
</linked-graph-wrapper> </linked-graph-wrapper>
......
...@@ -4,7 +4,7 @@ import { __ } from '~/locale'; ...@@ -4,7 +4,7 @@ import { __ } from '~/locale';
import { DEFAULT, LOAD_FAILURE } from '../../constants'; import { DEFAULT, LOAD_FAILURE } from '../../constants';
import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query.graphql'; import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query.graphql';
import PipelineGraph from './graph_component.vue'; import PipelineGraph from './graph_component.vue';
import { unwrapPipelineData } from './utils'; import { unwrapPipelineData, toggleQueryPollingByVisibility } from './utils';
export default { export default {
name: 'PipelineGraphWrapper', name: 'PipelineGraphWrapper',
...@@ -35,6 +35,7 @@ export default { ...@@ -35,6 +35,7 @@ export default {
apollo: { apollo: {
pipeline: { pipeline: {
query: getPipelineDetails, query: getPipelineDetails,
pollInterval: 10000,
variables() { variables() {
return { return {
projectPath: this.pipelineProjectPath, projectPath: this.pipelineProjectPath,
...@@ -64,11 +65,24 @@ export default { ...@@ -64,11 +65,24 @@ export default {
}; };
} }
}, },
showLoadingIcon() {
/*
Shows the icon only when the graph is empty, not when it is is
being refetched, for instance, on action completion
*/
return this.$apollo.queries.pipeline.loading && !this.pipeline;
},
},
mounted() {
toggleQueryPollingByVisibility(this.$apollo.queries.pipeline);
}, },
methods: { methods: {
hideAlert() { hideAlert() {
this.showAlert = false; this.showAlert = false;
}, },
refreshPipelineGraph() {
this.$apollo.queries.pipeline.refetch();
},
reportFailure(type) { reportFailure(type) {
this.showAlert = true; this.showAlert = true;
this.failureType = type; this.failureType = type;
...@@ -81,7 +95,12 @@ export default { ...@@ -81,7 +95,12 @@ export default {
<gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert"> <gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
{{ alert.text }} {{ alert.text }}
</gl-alert> </gl-alert>
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" class="gl-mx-auto gl-my-4" size="lg" /> <gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
<pipeline-graph v-if="pipeline" :pipeline="pipeline" @error="reportFailure" /> <pipeline-graph
v-if="pipeline"
:pipeline="pipeline"
@error="reportFailure"
@refreshPipelineGraph="refreshPipelineGraph"
/>
</div> </div>
</template> </template>
...@@ -3,7 +3,7 @@ import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query ...@@ -3,7 +3,7 @@ import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query
import LinkedPipeline from './linked_pipeline.vue'; import LinkedPipeline from './linked_pipeline.vue';
import { LOAD_FAILURE } from '../../constants'; import { LOAD_FAILURE } from '../../constants';
import { UPSTREAM } from './constants'; import { UPSTREAM } from './constants';
import { unwrapPipelineData } from './utils'; import { unwrapPipelineData, toggleQueryPollingByVisibility } from './utils';
export default { export default {
components: { components: {
...@@ -67,6 +67,7 @@ export default { ...@@ -67,6 +67,7 @@ export default {
this.$apollo.addSmartQuery('currentPipeline', { this.$apollo.addSmartQuery('currentPipeline', {
query: getPipelineDetails, query: getPipelineDetails,
pollInterval: 10000,
variables() { variables() {
return { return {
projectPath, projectPath,
...@@ -83,6 +84,8 @@ export default { ...@@ -83,6 +84,8 @@ export default {
this.$emit('error', LOAD_FAILURE); this.$emit('error', LOAD_FAILURE);
}, },
}); });
toggleQueryPollingByVisibility(this.$apollo.queries.currentPipeline);
}, },
isExpanded(id) { isExpanded(id) {
return Boolean(this.currentPipeline?.id && id === this.currentPipeline.id); return Boolean(this.currentPipeline?.id && id === this.currentPipeline.id);
......
...@@ -79,6 +79,7 @@ export default { ...@@ -79,6 +79,7 @@ export default {
:tooltip-text="action.title" :tooltip-text="action.title"
:link="action.path" :link="action.path"
class="js-stage-action stage-action rounded" class="js-stage-action stage-action rounded"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/> />
</div> </div>
</template> </template>
...@@ -96,6 +97,7 @@ export default { ...@@ -96,6 +97,7 @@ export default {
:job-hovered="jobHovered" :job-hovered="jobHovered"
:pipeline-expanded="pipelineExpanded" :pipeline-expanded="pipelineExpanded"
css-class-job-name="gl-build-content" css-class-job-name="gl-build-content"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/> />
<job-group-dropdown v-else :group="group" /> <job-group-dropdown v-else :group="group" />
</div> </div>
......
import Visibility from 'visibilityjs';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { unwrapStagesWithNeeds } from '../unwrapping_utils'; import { unwrapStagesWithNeeds } from '../unwrapping_utils';
...@@ -40,4 +41,17 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => { ...@@ -40,4 +41,17 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => {
}; };
}; };
export { unwrapPipelineData }; const toggleQueryPollingByVisibility = (queryRef, interval = 10000) => {
const stopStartQuery = query => {
if (!Visibility.hidden()) {
query.startPolling(interval);
} else {
query.stopPolling();
}
};
stopStartQuery(queryRef);
Visibility.change(stopStartQuery.bind(null, queryRef));
};
export { unwrapPipelineData, toggleQueryPollingByVisibility };
...@@ -7,7 +7,12 @@ import { GRAPHQL } from './components/graph/constants'; ...@@ -7,7 +7,12 @@ import { GRAPHQL } from './components/graph/constants';
Vue.use(VueApollo); Vue.use(VueApollo);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(
{},
{
batchMax: 2,
},
),
}); });
const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid) => { const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid) => {
......
...@@ -44,6 +44,18 @@ describe('graph component', () => { ...@@ -44,6 +44,18 @@ describe('graph component', () => {
it('renders the main columns in the graph', () => { it('renders the main columns in the graph', () => {
expect(findStageColumns()).toHaveLength(defaultProps.pipeline.stages.length); expect(findStageColumns()).toHaveLength(defaultProps.pipeline.stages.length);
}); });
describe('when column requests a refresh', () => {
beforeEach(() => {
findStageColumns()
.at(0)
.vm.$emit('refreshPipelineGraph');
});
it('refreshPipelineGraph is emitted', () => {
expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
});
});
}); });
describe('when linked pipelines are not present', () => { describe('when linked pipelines are not present', () => {
......
...@@ -108,4 +108,17 @@ describe('Pipeline graph wrapper', () => { ...@@ -108,4 +108,17 @@ describe('Pipeline graph wrapper', () => {
expect(getGraph().exists()).toBe(false); expect(getGraph().exists()).toBe(false);
}); });
}); });
describe('when refresh action is emitted', () => {
beforeEach(async () => {
createComponentWithApollo();
jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch');
await wrapper.vm.$nextTick();
getGraph().vm.$emit('refreshPipelineGraph');
});
it('calls refetch', () => {
expect(wrapper.vm.$apollo.queries.pipeline.refetch).toHaveBeenCalled();
});
});
}); });
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import ActionComponent from '~/pipelines/components/graph/action_component.vue'; import ActionComponent from '~/pipelines/components/graph/action_component.vue';
import JobItem from '~/pipelines/components/graph/job_item.vue';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue'; import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
const mockJob = { const mockJob = {
...@@ -37,6 +38,7 @@ describe('stage column component', () => { ...@@ -37,6 +38,7 @@ describe('stage column component', () => {
const findStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]'); const findStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
const findStageColumnGroup = () => wrapper.find('[data-testid="stage-column-group"]'); const findStageColumnGroup = () => wrapper.find('[data-testid="stage-column-group"]');
const findAllStageColumnGroups = () => wrapper.findAll('[data-testid="stage-column-group"]'); const findAllStageColumnGroups = () => wrapper.findAll('[data-testid="stage-column-group"]');
const findJobItem = () => wrapper.find(JobItem);
const findActionComponent = () => wrapper.find(ActionComponent); const findActionComponent = () => wrapper.find(ActionComponent);
const createComponent = ({ method = shallowMount, props = {} } = {}) => { const createComponent = ({ method = shallowMount, props = {} } = {}) => {
...@@ -67,6 +69,28 @@ describe('stage column component', () => { ...@@ -67,6 +69,28 @@ describe('stage column component', () => {
}); });
}); });
describe('when job notifies action is complete', () => {
beforeEach(() => {
createComponent({
method: mount,
props: {
groups: [
{
title: 'Fish',
size: 1,
jobs: [mockJob],
},
],
},
});
findJobItem().vm.$emit('pipelineActionRequestComplete');
});
it('emits refreshPipelineGraph', () => {
expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
});
});
describe('job', () => { describe('job', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
......
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