summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/pipelines/components/graph
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /app/assets/javascripts/pipelines/components/graph
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
downloadgitlab-ce-b76ae638462ab0f673e5915986070518dd3f9ad3.tar.gz
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'app/assets/javascripts/pipelines/components/graph')
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue15
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue269
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue48
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue10
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue91
-rw-r--r--app/assets/javascripts/pipelines/components/graph/perf_utils.js50
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue112
-rw-r--r--app/assets/javascripts/pipelines/components/graph/utils.js26
8 files changed, 132 insertions, 489 deletions
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index ea45b5e3ec7..015f0519c72 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -39,10 +39,10 @@ export default {
required: false,
default: false,
},
- pipelineLayers: {
- type: Array,
+ computedPipelineInfo: {
+ type: Object,
required: false,
- default: () => [],
+ default: () => ({}),
},
type: {
type: String,
@@ -81,7 +81,10 @@ export default {
layout() {
return this.isStageView
? this.pipeline.stages
- : generateColumnsFromLayersListMemoized(this.pipeline, this.pipelineLayers);
+ : generateColumnsFromLayersListMemoized(
+ this.pipeline,
+ this.computedPipelineInfo.pipelineLayers,
+ );
},
hasDownstreamPipelines() {
return Boolean(this.pipeline?.downstream?.length > 0);
@@ -92,6 +95,9 @@ export default {
isStageView() {
return this.viewType === STAGE_VIEW;
},
+ linksData() {
+ return this.computedPipelineInfo?.linksData ?? null;
+ },
metricsConfig() {
return {
path: this.configPaths.metricsPath,
@@ -188,6 +194,7 @@ export default {
:container-id="containerId"
:container-measurements="measurements"
:highlighted-job="hoveredJobName"
+ :links-data="linksData"
:metrics-config="metricsConfig"
:show-links="showJobLinks"
:view-type="viewType"
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue
deleted file mode 100644
index 39d0fa8a8ca..00000000000
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue
+++ /dev/null
@@ -1,269 +0,0 @@
-<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import { escape, capitalize } from 'lodash';
-import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
-import { reportToSentry } from '../../utils';
-import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
-import LinkedPipelinesColumnLegacy from './linked_pipelines_column_legacy.vue';
-import StageColumnComponentLegacy from './stage_column_component_legacy.vue';
-
-export default {
- name: 'PipelineGraphLegacy',
- components: {
- GlLoadingIcon,
- LinkedPipelinesColumnLegacy,
- StageColumnComponentLegacy,
- },
- mixins: [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 {
- downstreamMarginTop: null,
- jobName: null,
- pipelineExpanded: {
- jobName: '',
- expanded: false,
- },
- };
- },
- computed: {
- graph() {
- return this.pipeline.details?.stages;
- },
- hasUpstream() {
- return (
- this.type !== this.$options.downstream &&
- this.upstreamPipelines &&
- this.pipeline.triggered_by !== null
- );
- },
- upstreamPipelines() {
- return this.pipeline.triggered_by;
- },
- hasDownstream() {
- return (
- this.type !== this.$options.upstream &&
- this.downstreamPipelines &&
- this.pipeline.triggered.length > 0
- );
- },
- downstreamPipelines() {
- return this.pipeline.triggered;
- },
- expandedUpstream() {
- return (
- this.pipeline.triggered_by &&
- Array.isArray(this.pipeline.triggered_by) &&
- this.pipeline.triggered_by.find((el) => el.isExpanded)
- );
- },
- expandedDownstream() {
- return this.pipeline.triggered && this.pipeline.triggered.find((el) => el.isExpanded);
- },
- pipelineTypeUpstream() {
- return this.type !== this.$options.downstream && this.expandedUpstream;
- },
- pipelineTypeDownstream() {
- return this.type !== this.$options.upstream && this.expandedDownstream;
- },
- pipelineProjectId() {
- return this.pipeline.project.id;
- },
- },
- errorCaptured(err, _vm, info) {
- reportToSentry(this.$options.name, `error: ${err}, info: ${info}`);
- },
- methods: {
- 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);
- },
- 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
- * offsetTop and then subtracting 15
- */
- this.downstreamMarginTop = this.calculateMarginTop(downstreamNode, 15);
-
- /**
- * 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.expandedDownstream?.id !== pipeline.id) {
- this.$emit('onResetDownstream', this.pipeline, pipeline);
- }
-
- this.$emit('onClickDownstreamPipeline', pipeline);
- },
- calculateMarginTop(downstreamNode, pixelDiff) {
- return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
- },
- hasOnlyOneJob(stage) {
- return stage.groups.length === 1;
- },
- hasUpstreamColumn(index) {
- return index === 0 && this.hasUpstream;
- },
- setJob(jobName) {
- this.jobName = jobName;
- },
- setPipelineExpanded(jobName, expanded) {
- if (expanded) {
- this.pipelineExpanded = {
- jobName,
- expanded,
- };
- } else {
- this.pipelineExpanded = {
- expanded,
- jobName: '',
- };
- }
- },
- },
-};
-</script>
-<template>
- <div class="build-content middle-block js-pipeline-graph">
- <div
- class="pipeline-visualization pipeline-graph"
- :class="{ 'pipeline-tab-content': !isLinkedPipeline }"
- >
- <div class="gl-w-full">
- <div class="container-fluid container-limited">
- <gl-loading-icon v-if="isLoading" class="m-auto" size="lg" />
- <pipeline-graph-legacy
- v-if="pipelineTypeUpstream"
- :type="$options.upstream"
- class="d-inline-block upstream-pipeline"
- :class="`js-upstream-pipeline-${expandedUpstream.id}`"
- :is-loading="false"
- :pipeline="expandedUpstream"
- :is-linked-pipeline="true"
- :mediator="mediator"
- @onClickUpstreamPipeline="clickUpstreamPipeline"
- @refreshPipelineGraph="requestRefreshPipelineGraph"
- />
-
- <linked-pipelines-column-legacy
- v-if="hasUpstream"
- :type="$options.upstream"
- :linked-pipelines="upstreamPipelines"
- :column-title="__('Upstream')"
- :project-id="pipelineProjectId"
- @linkedPipelineClick="$emit('onClickUpstreamPipeline', $event)"
- />
-
- <ul
- v-if="!isLoading"
- :class="{
- 'inline js-has-linked-pipelines': hasDownstream || hasUpstream,
- }"
- class="stage-column-list align-top"
- >
- <stage-column-component-legacy
- v-for="(stage, index) in graph"
- :key="stage.name"
- :class="{
- 'has-upstream gl-ml-11': hasUpstreamColumn(index),
- 'has-only-one-job': hasOnlyOneJob(stage),
- 'gl-mr-26': shouldAddRightMargin(index),
- }"
- :title="capitalizeStageName(stage.name)"
- :groups="stage.groups"
- :stage-connector-class="stageConnectorClass(index, stage)"
- :is-first-column="isFirstColumn(index)"
- :has-upstream="hasUpstream"
- :action="stage.status.action"
- :job-hovered="jobName"
- :pipeline-expanded="pipelineExpanded"
- @refreshPipelineGraph="refreshPipelineGraph"
- />
- </ul>
-
- <linked-pipelines-column-legacy
- v-if="hasDownstream"
- :type="$options.downstream"
- :linked-pipelines="downstreamPipelines"
- :column-title="__('Downstream')"
- :project-id="pipelineProjectId"
- @linkedPipelineClick="handleClickedDownstream"
- @downstreamHovered="setJob"
- @pipelineExpandToggle="setPipelineExpanded"
- />
-
- <pipeline-graph-legacy
- v-if="pipelineTypeDownstream"
- :type="$options.downstream"
- class="d-inline-block"
- :class="`js-downstream-pipeline-${expandedDownstream.id}`"
- :is-loading="false"
- :pipeline="expandedDownstream"
- :is-linked-pipeline="true"
- :style="{ 'margin-top': downstreamMarginTop }"
- :mediator="mediator"
- @onClickDownstreamPipeline="clickDownstreamPipeline"
- @refreshPipelineGraph="requestRefreshPipelineGraph"
- />
- </div>
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index a948a57c144..e995d400907 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -4,15 +4,15 @@ import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.qu
import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
import { __ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
import DismissPipelineGraphCallout from '../../graphql/mutations/dismiss_pipeline_notification.graphql';
+import getPipelineQuery from '../../graphql/queries/get_pipeline_header_data.query.graphql';
import { reportToSentry, reportMessageToSentry } from '../../utils';
-import { listByLayers } from '../parsing_utils';
import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW, VIEW_TYPE_KEY } from './constants';
import PipelineGraph from './graph_component.vue';
import GraphViewSelector from './graph_view_selector.vue';
import {
+ calculatePipelineLayersInfo,
getQueryHeaders,
serializeLoadErrors,
toggleQueryPollingByVisibility,
@@ -31,7 +31,6 @@ export default {
LocalStorageSync,
PipelineGraph,
},
- mixins: [glFeatureFlagMixin()],
inject: {
graphqlResourceEtag: {
default: '',
@@ -50,9 +49,10 @@ export default {
return {
alertType: null,
callouts: [],
+ computedPipelineInfo: null,
currentViewType: STAGE_VIEW,
+ canRefetchHeaderPipeline: false,
pipeline: null,
- pipelineLayers: null,
showAlert: false,
showLinks: false,
};
@@ -78,6 +78,26 @@ export default {
);
},
},
+ headerPipeline: {
+ query: getPipelineQuery,
+ // this query is already being called in header_component.vue, which shares the same cache as this component
+ // the skip here is to prevent sending double network requests on page load
+ skip() {
+ return !this.canRefetchHeaderPipeline;
+ },
+ variables() {
+ return {
+ fullPath: this.pipelineProjectPath,
+ iid: this.pipelineIid,
+ };
+ },
+ update(data) {
+ return data.project?.pipeline || {};
+ },
+ error() {
+ this.reportFailure({ type: LOAD_FAILURE, skipSentry: true });
+ },
+ },
pipeline: {
context() {
return getQueryHeaders(this.graphqlResourceEtag);
@@ -178,7 +198,7 @@ export default {
return this.$apollo.queries.pipeline.loading && !this.pipeline;
},
showGraphViewSelector() {
- return Boolean(this.glFeatures.pipelineGraphLayersView && this.pipeline?.usesNeeds);
+ return this.pipeline?.usesNeeds;
},
},
mounted() {
@@ -192,12 +212,16 @@ export default {
reportToSentry(this.$options.name, `error: ${err}, info: ${info}`);
},
methods: {
- getPipelineLayers() {
- if (this.currentViewType === LAYER_VIEW && !this.pipelineLayers) {
- this.pipelineLayers = listByLayers(this.pipeline);
+ getPipelineInfo() {
+ if (this.currentViewType === LAYER_VIEW && !this.computedPipelineInfo) {
+ this.computedPipelineInfo = calculatePipelineLayersInfo(
+ this.pipeline,
+ this.$options.name,
+ this.metricsPath,
+ );
}
- return this.pipelineLayers;
+ return this.computedPipelineInfo;
},
handleTipDismissal() {
try {
@@ -217,6 +241,10 @@ export default {
},
refreshPipelineGraph() {
this.$apollo.queries.pipeline.refetch();
+
+ // this will update the status in header_component since they share the same cache
+ this.canRefetchHeaderPipeline = true;
+ this.$apollo.queries.headerPipeline.refetch();
},
/* eslint-disable @gitlab/require-i18n-strings */
reportFailure({ type, err = 'No error string passed.', skipSentry = false }) {
@@ -262,7 +290,7 @@ export default {
v-if="pipeline"
:config-paths="configPaths"
:pipeline="pipeline"
- :pipeline-layers="getPipelineLayers()"
+ :computed-pipeline-info="getPipelineInfo()"
:show-links="showLinks"
:view-type="graphViewType"
@error="reportFailure"
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
index 52ee40bd982..d251e0d8bd8 100644
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
@@ -2,10 +2,10 @@
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { LOAD_FAILURE } from '../../constants';
import { reportToSentry } from '../../utils';
-import { listByLayers } from '../parsing_utils';
import { ONE_COL_WIDTH, UPSTREAM, LAYER_VIEW, STAGE_VIEW } from './constants';
import LinkedPipeline from './linked_pipeline.vue';
import {
+ calculatePipelineLayersInfo,
getQueryHeaders,
serializeLoadErrors,
toggleQueryPollingByVisibility,
@@ -138,7 +138,11 @@ export default {
},
getPipelineLayers(id) {
if (this.viewType === LAYER_VIEW && !this.pipelineLayers[id]) {
- this.pipelineLayers[id] = listByLayers(this.currentPipeline);
+ this.pipelineLayers[id] = calculatePipelineLayersInfo(
+ this.currentPipeline,
+ this.$options.name,
+ this.configPaths.metricsPath,
+ );
}
return this.pipelineLayers[id];
@@ -223,7 +227,7 @@ export default {
class="d-inline-block gl-mt-n2"
:config-paths="configPaths"
:pipeline="currentPipeline"
- :pipeline-layers="getPipelineLayers(pipeline.id)"
+ :computed-pipeline-info="getPipelineLayers(pipeline.id)"
:show-links="showLinks"
:is-linked-pipeline="true"
:view-type="graphViewType"
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue
deleted file mode 100644
index 39baeb6e1c3..00000000000
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<script>
-import { reportToSentry } from '../../utils';
-import { UPSTREAM } from './constants';
-import LinkedPipeline from './linked_pipeline.vue';
-
-export default {
- components: {
- LinkedPipeline,
- },
- props: {
- columnTitle: {
- type: String,
- required: true,
- },
- linkedPipelines: {
- type: Array,
- required: true,
- },
- type: {
- type: String,
- required: true,
- },
- projectId: {
- type: Number,
- required: true,
- },
- },
- computed: {
- columnClass() {
- const positionValues = {
- right: 'gl-ml-11',
- left: 'gl-mr-7',
- };
- return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
- },
- graphPosition() {
- return this.isUpstream ? 'left' : 'right';
- },
- isExpanded() {
- return this.pipeline?.isExpanded || false;
- },
- isUpstream() {
- return this.type === UPSTREAM;
- },
- },
- errorCaptured(err, _vm, info) {
- reportToSentry('linked_pipelines_column_legacy', `error: ${err}, info: ${info}`);
- },
- methods: {
- onPipelineClick(downstreamNode, pipeline, index) {
- this.$emit('linkedPipelineClick', pipeline, index, downstreamNode);
- },
- onDownstreamHovered(jobName) {
- this.$emit('downstreamHovered', jobName);
- },
- onPipelineExpandToggle(jobName, expanded) {
- // Highlighting only applies to downstream pipelines
- if (this.isUpstream) {
- return;
- }
-
- this.$emit('pipelineExpandToggle', jobName, expanded);
- },
- },
-};
-</script>
-
-<template>
- <div :class="columnClass" class="stage-column linked-pipelines-column">
- <div class="stage-name linked-pipelines-column-title">{{ columnTitle }}</div>
- <div v-if="isUpstream" class="cross-project-triangle"></div>
- <ul>
- <li v-for="(pipeline, index) in linkedPipelines" :key="pipeline.id">
- <linked-pipeline
- :class="{
- active: pipeline.isExpanded,
- 'left-connector': pipeline.isExpanded && graphPosition === 'left',
- }"
- :pipeline="pipeline"
- :column-title="columnTitle"
- :project-id="projectId"
- :type="type"
- :expanded="isExpanded"
- @pipelineClicked="onPipelineClick($event, pipeline, index)"
- @downstreamHovered="onDownstreamHovered"
- @pipelineExpandToggle="onPipelineExpandToggle"
- />
- </li>
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/perf_utils.js b/app/assets/javascripts/pipelines/components/graph/perf_utils.js
new file mode 100644
index 00000000000..3737a209f5c
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/perf_utils.js
@@ -0,0 +1,50 @@
+import {
+ PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
+ PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
+ PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
+ PIPELINES_DETAIL_LINK_DURATION,
+ PIPELINES_DETAIL_LINKS_TOTAL,
+ PIPELINES_DETAIL_LINKS_JOB_RATIO,
+} from '~/performance/constants';
+
+import { performanceMarkAndMeasure } from '~/performance/utils';
+import { reportPerformance } from '../graph_shared/api';
+
+export const beginPerfMeasure = () => {
+ performanceMarkAndMeasure({ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START });
+};
+
+export const finishPerfMeasureAndSend = (numLinks, numGroups, metricsPath) => {
+ performanceMarkAndMeasure({
+ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
+ measures: [
+ {
+ name: PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
+ start: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
+ },
+ ],
+ });
+
+ window.requestAnimationFrame(() => {
+ const duration = window.performance.getEntriesByName(
+ PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
+ )[0]?.duration;
+
+ if (!duration) {
+ return;
+ }
+
+ const data = {
+ histograms: [
+ { name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
+ { name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
+ {
+ name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
+ value: numLinks / numGroups,
+ },
+ ],
+ };
+
+ reportPerformance(metricsPath, data);
+ });
+};
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue
deleted file mode 100644
index cbaf07c05cf..00000000000
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue
+++ /dev/null
@@ -1,112 +0,0 @@
-<script>
-import { isEmpty, escape } from 'lodash';
-import stageColumnMixin from '../../mixins/stage_column_mixin';
-import { reportToSentry } from '../../utils';
-import ActionComponent from '../jobs_shared/action_component.vue';
-import JobGroupDropdown from './job_group_dropdown.vue';
-import JobItem from './job_item.vue';
-
-export default {
- components: {
- JobItem,
- JobGroupDropdown,
- ActionComponent,
- },
- mixins: [stageColumnMixin],
- props: {
- title: {
- type: String,
- required: true,
- },
- groups: {
- type: Array,
- required: true,
- },
- isFirstColumn: {
- type: Boolean,
- required: false,
- default: false,
- },
- stageConnectorClass: {
- type: String,
- required: false,
- default: '',
- },
- action: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- jobHovered: {
- type: String,
- required: false,
- default: '',
- },
- pipelineExpanded: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
- computed: {
- hasAction() {
- return !isEmpty(this.action);
- },
- },
- errorCaptured(err, _vm, info) {
- reportToSentry('stage_column_component_legacy', `error: ${err}, info: ${info}`);
- },
- methods: {
- groupId(group) {
- return `ci-badge-${escape(group.name)}`;
- },
- pipelineActionRequestComplete() {
- this.$emit('refreshPipelineGraph');
- },
- },
-};
-</script>
-<template>
- <li :class="stageConnectorClass" class="stage-column">
- <div class="stage-name position-relative" data-testid="stage-column-title">
- {{ title }}
- <action-component
- v-if="hasAction"
- :action-icon="action.icon"
- :tooltip-text="action.title"
- :link="action.path"
- class="js-stage-action stage-action rounded"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
- </div>
-
- <div class="builds-container">
- <ul>
- <li
- v-for="(group, index) in groups"
- :id="groupId(group)"
- :key="group.id"
- :class="buildConnnectorClass(index)"
- class="build"
- >
- <div class="curve"></div>
-
- <job-item
- v-if="group.size === 1"
- :job="group.jobs[0]"
- :job-hovered="jobHovered"
- :pipeline-expanded="pipelineExpanded"
- css-class-job-name="build-content"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
-
- <job-group-dropdown
- v-if="group.size > 1"
- :group="group"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
- </li>
- </ul>
- </div>
- </li>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/utils.js b/app/assets/javascripts/pipelines/components/graph/utils.js
index 163b3898c28..3da792cb9df 100644
--- a/app/assets/javascripts/pipelines/components/graph/utils.js
+++ b/app/assets/javascripts/pipelines/components/graph/utils.js
@@ -1,7 +1,10 @@
import { isEmpty } from 'lodash';
import Visibility from 'visibilityjs';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { reportToSentry } from '../../utils';
+import { listByLayers } from '../parsing_utils';
import { unwrapStagesWithNeedsAndLookup } from '../unwrapping_utils';
+import { beginPerfMeasure, finishPerfMeasureAndSend } from './perf_utils';
const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
return {
@@ -10,6 +13,28 @@ const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
};
};
+const calculatePipelineLayersInfo = (pipeline, componentName, metricsPath) => {
+ const shouldCollectMetrics = Boolean(metricsPath);
+
+ if (shouldCollectMetrics) {
+ beginPerfMeasure();
+ }
+
+ let layers = null;
+
+ try {
+ layers = listByLayers(pipeline);
+
+ if (shouldCollectMetrics) {
+ finishPerfMeasureAndSend(layers.linksData.length, layers.numGroups, metricsPath);
+ }
+ } catch (err) {
+ reportToSentry(componentName, err);
+ }
+
+ return layers;
+};
+
/* eslint-disable @gitlab/require-i18n-strings */
const getQueryHeaders = (etagResource) => {
return {
@@ -106,6 +131,7 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => {
const validateConfigPaths = (value) => value.graphqlResourceEtag?.length > 0;
export {
+ calculatePipelineLayersInfo,
getQueryHeaders,
serializeGqlErr,
serializeLoadErrors,