Commit 43387d80 authored by Sarah Groff Hennigh-Palermo's avatar Sarah Groff Hennigh-Palermo Committed by Andrew Fontaine

Implement DAG pipeline serializer

Add DAG entities for pipeline, stage, group, job.
parent 7a533749
<script>
import { GlAlert } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
export default {
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'Dag',
components: {
GlAlert,
},
props: {
graphUrl: {
type: String,
required: false,
default: '',
},
},
data() {
return {
showFailureAlert: false,
};
},
computed: {
shouldDisplayGraph() {
return !this.showFailureAlert;
},
},
mounted() {
const { drawGraph, reportFailure } = this;
if (!this.graphUrl) {
reportFailure();
return;
}
axios
.get(this.graphUrl)
.then(response => {
drawGraph(response.data);
})
.catch(reportFailure);
},
methods: {
drawGraph(data) {
return data;
},
hideAlert() {
this.showFailureAlert = false;
},
reportFailure() {
this.showFailureAlert = true;
},
},
};
</script>
<template>
<div>
<gl-alert v-if="showFailureAlert" variant="danger" @dismiss="hideAlert">
{{ __('We are currently unable to fetch data for this graph.') }}
</gl-alert>
<div v-if="shouldDisplayGraph" data-testid="dag-graph-container">
<!-- graph goes here -->
</div>
</div>
</template>
...@@ -4,6 +4,7 @@ import Translate from '~/vue_shared/translate'; ...@@ -4,6 +4,7 @@ import Translate from '~/vue_shared/translate';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility'; import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
import pipelineGraph from './components/graph/graph_component.vue'; import pipelineGraph from './components/graph/graph_component.vue';
import Dag from './components/dag/dag.vue';
import GraphBundleMixin from './mixins/graph_pipeline_bundle_mixin'; import GraphBundleMixin from './mixins/graph_pipeline_bundle_mixin';
import PipelinesMediator from './pipeline_details_mediator'; import PipelinesMediator from './pipeline_details_mediator';
import pipelineHeader from './components/header_component.vue'; import pipelineHeader from './components/header_component.vue';
...@@ -144,6 +145,25 @@ const createTestDetails = detailsEndpoint => { ...@@ -144,6 +145,25 @@ const createTestDetails = detailsEndpoint => {
.catch(() => {}); .catch(() => {});
}; };
const createDagApp = () => {
const el = document.querySelector('#js-pipeline-dag-vue');
const graphUrl = el.dataset?.pipelineDataPath;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
Dag,
},
render(createElement) {
return createElement('dag', {
props: {
graphUrl,
},
});
},
});
};
export default () => { export default () => {
const { dataset } = document.querySelector('.js-pipeline-details-vue'); const { dataset } = document.querySelector('.js-pipeline-details-vue');
const mediator = new PipelinesMediator({ endpoint: dataset.endpoint }); const mediator = new PipelinesMediator({ endpoint: dataset.endpoint });
...@@ -153,4 +173,5 @@ export default () => { ...@@ -153,4 +173,5 @@ export default () => {
createPipelineHeaderApp(mediator); createPipelineHeaderApp(mediator);
createPipelinesTabs(dataset); createPipelinesTabs(dataset);
createTestDetails(dataset.testReportsCountEndpoint); createTestDetails(dataset.testReportsCountEndpoint);
createDagApp();
}; };
...@@ -6,14 +6,14 @@ ...@@ -6,14 +6,14 @@
%li.js-pipeline-tab-link %li.js-pipeline-tab-link
= link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
= _('Pipeline') = _('Pipeline')
%li.js-builds-tab-link
= link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
= _('Jobs')
%span.badge.badge-pill.js-builds-counter= pipeline.total_size
- if dag_pipeline_tab_enabled - if dag_pipeline_tab_enabled
%li.js-dag-tab-link %li.js-dag-tab-link
= link_to dag_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-dag', action: 'dag', toggle: 'tab' }, class: 'dag-tab' do = link_to dag_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-dag', action: 'dag', toggle: 'tab' }, class: 'dag-tab' do
= _('DAG') = _('DAG')
%li.js-builds-tab-link
= link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
= _('Jobs')
%span.badge.badge-pill.js-builds-counter= pipeline.total_size
- if @pipeline.failed_builds.present? - if @pipeline.failed_builds.present?
%li.js-failures-tab-link %li.js-failures-tab-link
= link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
......
...@@ -24326,6 +24326,9 @@ msgstr "" ...@@ -24326,6 +24326,9 @@ msgstr ""
msgid "Warning: Displaying this diagram might cause performance issues on this page." msgid "Warning: Displaying this diagram might cause performance issues on this page."
msgstr "" msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
msgid "We could not determine the path to remove the epic" msgid "We could not determine the path to remove the epic"
msgstr "" msgstr ""
......
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
import { GlAlert } from '@gitlab/ui';
import Dag from '~/pipelines/components/dag/dag.vue';
describe('Pipeline DAG graph', () => {
let wrapper;
let axiosMock;
const getAlert = () => wrapper.find(GlAlert);
const getGraph = () => wrapper.find('[data-testid="dag-graph-container"]');
const dataPath = 'root/test/pipelines/90/dag.json';
const createComponent = (propsData = {}, method = mount) => {
axiosMock = new MockAdapter(axios);
if (wrapper?.destroy) {
wrapper.destroy();
}
wrapper = method(Dag, {
propsData,
data() {
return {
showFailureAlert: false,
};
},
});
};
afterEach(() => {
axiosMock.restore();
wrapper.destroy();
wrapper = null;
});
describe('when there is no dataUrl', () => {
beforeEach(() => {
createComponent({ graphUrl: undefined });
});
it('shows the alert and not the graph', () => {
expect(getAlert().exists()).toBe(true);
expect(getGraph().exists()).toBe(false);
});
});
describe('when there is a dataUrl', () => {
beforeEach(() => {
createComponent({ graphUrl: dataPath });
});
it('shows the graph and not the alert', () => {
expect(getAlert().exists()).toBe(false);
expect(getGraph().exists()).toBe(true);
});
describe('but the data fetch fails', () => {
beforeEach(() => {
axiosMock.onGet(dataPath).replyOnce(500);
createComponent({ graphUrl: dataPath });
});
it('shows the alert and not the graph', () => {
return wrapper.vm
.$nextTick()
.then(waitForPromises)
.then(() => {
expect(getAlert().exists()).toBe(true);
expect(getGraph().exists()).toBe(false);
});
});
});
});
});
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