Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
05f0211d
Commit
05f0211d
authored
Apr 08, 2021
by
Sarah Groff Hennigh-Palermo
Committed by
Jose Ivan Vargas
Apr 08, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Pipeline Graph: Add ability to switch between views
parent
044170c7
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
219 additions
and
50 deletions
+219
-50
app/assets/javascripts/pipelines/components/graph/graph_component.vue
...avascripts/pipelines/components/graph/graph_component.vue
+48
-10
app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
...ts/pipelines/components/graph/graph_component_wrapper.vue
+12
-1
app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
...cripts/pipelines/components/graph/graph_view_selector.vue
+3
-3
app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
...ts/pipelines/components/graph/linked_pipelines_column.vue
+16
-1
app/assets/javascripts/pipelines/components/graph/utils.js
app/assets/javascripts/pipelines/components/graph/utils.js
+4
-3
app/assets/javascripts/pipelines/components/graph_shared/links_inner.vue
...scripts/pipelines/components/graph_shared/links_inner.vue
+21
-2
app/assets/javascripts/pipelines/components/parsing_utils.js
app/assets/javascripts/pipelines/components/parsing_utils.js
+26
-0
app/assets/javascripts/pipelines/components/unwrapping_utils.js
...sets/javascripts/pipelines/components/unwrapping_utils.js
+21
-7
spec/frontend/pipelines/graph/graph_component_spec.js
spec/frontend/pipelines/graph/graph_component_spec.js
+2
-1
spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
.../frontend/pipelines/graph/graph_component_wrapper_spec.js
+32
-3
spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
.../frontend/pipelines/graph/linked_pipelines_column_spec.js
+31
-2
spec/frontend/pipelines/graph/mock_data.js
spec/frontend/pipelines/graph/mock_data.js
+1
-15
spec/frontend/pipelines/unwrapping_utils_spec.js
spec/frontend/pipelines/unwrapping_utils_spec.js
+2
-2
No files found.
app/assets/javascripts/pipelines/components/graph/graph_component.vue
View file @
05f0211d
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
import
{
reportToSentry
}
from
'
../../utils
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
LinkedGraphWrapper
from
'
../graph_shared/linked_graph_wrapper.vue
'
;
import
LinkedGraphWrapper
from
'
../graph_shared/linked_graph_wrapper.vue
'
;
import
LinksLayer
from
'
../graph_shared/links_layer.vue
'
;
import
LinksLayer
from
'
../graph_shared/links_layer.vue
'
;
import
{
DOWNSTREAM
,
MAIN
,
UPSTREAM
,
ONE_COL_WIDTH
}
from
'
./constants
'
;
import
{
DOWNSTREAM
,
MAIN
,
UPSTREAM
,
ONE_COL_WIDTH
,
STAGE_VIEW
}
from
'
./constants
'
;
import
LinkedPipelinesColumn
from
'
./linked_pipelines_column.vue
'
;
import
LinkedPipelinesColumn
from
'
./linked_pipelines_column.vue
'
;
import
StageColumnComponent
from
'
./stage_column_component.vue
'
;
import
StageColumnComponent
from
'
./stage_column_component.vue
'
;
import
{
validateConfigPaths
}
from
'
./utils
'
;
import
{
validateConfigPaths
}
from
'
./utils
'
;
...
@@ -25,11 +25,20 @@ export default {
...
@@ -25,11 +25,20 @@ export default {
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
viewType
:
{
type
:
String
,
required
:
true
,
},
isLinkedPipeline
:
{
isLinkedPipeline
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
false
,
required
:
false
,
default
:
false
,
default
:
false
,
},
},
pipelineLayers
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
type
:
{
type
:
{
type
:
String
,
type
:
String
,
required
:
false
,
required
:
false
,
...
@@ -63,8 +72,8 @@ export default {
...
@@ -63,8 +72,8 @@ export default {
downstreamPipelines
()
{
downstreamPipelines
()
{
return
this
.
hasDownstreamPipelines
?
this
.
pipeline
.
downstream
:
[];
return
this
.
hasDownstreamPipelines
?
this
.
pipeline
.
downstream
:
[];
},
},
graph
()
{
layout
()
{
return
this
.
pipeline
.
stages
;
return
this
.
isStageView
?
this
.
pipeline
.
stages
:
this
.
generateColumnsFromLayersList
()
;
},
},
hasDownstreamPipelines
()
{
hasDownstreamPipelines
()
{
return
Boolean
(
this
.
pipeline
?.
downstream
?.
length
>
0
);
return
Boolean
(
this
.
pipeline
?.
downstream
?.
length
>
0
);
...
@@ -72,12 +81,18 @@ export default {
...
@@ -72,12 +81,18 @@ export default {
hasUpstreamPipelines
()
{
hasUpstreamPipelines
()
{
return
Boolean
(
this
.
pipeline
?.
upstream
?.
length
>
0
);
return
Boolean
(
this
.
pipeline
?.
upstream
?.
length
>
0
);
},
},
isStageView
()
{
return
this
.
viewType
===
STAGE_VIEW
;
},
metricsConfig
()
{
metricsConfig
()
{
return
{
return
{
path
:
this
.
configPaths
.
metricsPath
,
path
:
this
.
configPaths
.
metricsPath
,
collectMetrics
:
true
,
collectMetrics
:
true
,
};
};
},
},
shouldHideLinks
()
{
return
this
.
isStageView
;
},
// The show downstream check prevents showing redundant linked columns
// The show downstream check prevents showing redundant linked columns
showDownstreamPipelines
()
{
showDownstreamPipelines
()
{
return
(
return
(
...
@@ -101,6 +116,26 @@ export default {
...
@@ -101,6 +116,26 @@ export default {
this
.
getMeasurements
();
this
.
getMeasurements
();
},
},
methods
:
{
methods
:
{
generateColumnsFromLayersList
()
{
return
this
.
pipelineLayers
.
map
((
layers
,
idx
)
=>
{
/*
look up the groups in each layer,
then add each set of layer groups to a stage-like object
*/
const
groups
=
layers
.
map
((
id
)
=>
{
const
{
stageIdx
,
groupIdx
}
=
this
.
pipeline
.
stagesLookup
[
id
];
return
this
.
pipeline
.
stages
?.[
stageIdx
]?.
groups
?.[
groupIdx
];
});
return
{
name
:
''
,
id
:
`layer-
${
idx
}
`
,
status
:
{
action
:
null
},
groups
:
groups
.
filter
(
Boolean
),
};
});
},
getMeasurements
()
{
getMeasurements
()
{
this
.
measurements
=
{
this
.
measurements
=
{
width
:
this
.
$refs
[
this
.
containerId
].
scrollWidth
,
width
:
this
.
$refs
[
this
.
containerId
].
scrollWidth
,
...
@@ -147,29 +182,31 @@ export default {
...
@@ -147,29 +182,31 @@ export default {
:linked-pipelines=
"upstreamPipelines"
:linked-pipelines=
"upstreamPipelines"
:column-title=
"__('Upstream')"
:column-title=
"__('Upstream')"
:type=
"$options.pipelineTypeConstants.UPSTREAM"
:type=
"$options.pipelineTypeConstants.UPSTREAM"
:view-type=
"viewType"
@
error=
"onError"
@
error=
"onError"
/>
/>
</
template
>
</
template
>
<
template
#main
>
<
template
#main
>
<div
:id=
"containerId"
:ref=
"containerId"
>
<div
:id=
"containerId"
:ref=
"containerId"
>
<links-layer
<links-layer
:pipeline-data=
"
graph
"
:pipeline-data=
"
layout
"
:pipeline-id=
"pipeline.id"
:pipeline-id=
"pipeline.id"
:container-id=
"containerId"
:container-id=
"containerId"
:container-measurements=
"measurements"
:container-measurements=
"measurements"
:highlighted-job=
"hoveredJobName"
:highlighted-job=
"hoveredJobName"
:metrics-config=
"metricsConfig"
:metrics-config=
"metricsConfig"
:never-show-links=
"true"
:never-show-links=
"shouldHideLinks"
:view-type=
"viewType"
default-link-color=
"gl-stroke-transparent"
default-link-color=
"gl-stroke-transparent"
@
error=
"onError"
@
error=
"onError"
@
highlightedJobsChange=
"updateHighlightedJobs"
@
highlightedJobsChange=
"updateHighlightedJobs"
>
>
<stage-column-component
<stage-column-component
v-for=
"
stage in graph
"
v-for=
"
column in layout
"
:key=
"
stage
.name"
:key=
"
column.id || column
.name"
:title=
"
stage
.name"
:title=
"
column
.name"
:groups=
"
stage
.groups"
:groups=
"
column
.groups"
:action=
"
stage
.status.action"
:action=
"
column
.status.action"
:highlighted-jobs=
"highlightedJobs"
:highlighted-jobs=
"highlightedJobs"
:job-hovered=
"hoveredJobName"
:job-hovered=
"hoveredJobName"
:pipeline-expanded=
"pipelineExpanded"
:pipeline-expanded=
"pipelineExpanded"
...
@@ -189,6 +226,7 @@ export default {
...
@@ -189,6 +226,7 @@ export default {
:linked-pipelines=
"downstreamPipelines"
:linked-pipelines=
"downstreamPipelines"
:column-title=
"__('Downstream')"
:column-title=
"__('Downstream')"
:type=
"$options.pipelineTypeConstants.DOWNSTREAM"
:type=
"$options.pipelineTypeConstants.DOWNSTREAM"
:view-type=
"viewType"
@
downstreamHovered=
"setJob"
@
downstreamHovered=
"setJob"
@
pipelineExpandToggle=
"togglePipelineExpanded"
@
pipelineExpandToggle=
"togglePipelineExpanded"
@
scrollContainer=
"slidePipelineContainer"
@
scrollContainer=
"slidePipelineContainer"
...
...
app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
View file @
05f0211d
...
@@ -5,7 +5,8 @@ import { __ } from '~/locale';
...
@@ -5,7 +5,8 @@ import { __ } from '~/locale';
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
{
DEFAULT
,
DRAW_FAILURE
,
LOAD_FAILURE
}
from
'
../../constants
'
;
import
{
DEFAULT
,
DRAW_FAILURE
,
LOAD_FAILURE
}
from
'
../../constants
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
{
IID_FAILURE
,
STAGE_VIEW
}
from
'
./constants
'
;
import
{
listByLayers
}
from
'
../parsing_utils
'
;
import
{
IID_FAILURE
,
LAYER_VIEW
,
STAGE_VIEW
}
from
'
./constants
'
;
import
PipelineGraph
from
'
./graph_component.vue
'
;
import
PipelineGraph
from
'
./graph_component.vue
'
;
import
GraphViewSelector
from
'
./graph_view_selector.vue
'
;
import
GraphViewSelector
from
'
./graph_view_selector.vue
'
;
import
{
import
{
...
@@ -43,6 +44,7 @@ export default {
...
@@ -43,6 +44,7 @@ export default {
alertType
:
null
,
alertType
:
null
,
currentViewType
:
STAGE_VIEW
,
currentViewType
:
STAGE_VIEW
,
pipeline
:
null
,
pipeline
:
null
,
pipelineLayers
:
null
,
showAlert
:
false
,
showAlert
:
false
,
};
};
},
},
...
@@ -155,6 +157,13 @@ export default {
...
@@ -155,6 +157,13 @@ export default {
reportToSentry
(
this
.
$options
.
name
,
`error:
${
err
}
, info:
${
info
}
`
);
reportToSentry
(
this
.
$options
.
name
,
`error:
${
err
}
, info:
${
info
}
`
);
},
},
methods
:
{
methods
:
{
getPipelineLayers
()
{
if
(
this
.
currentViewType
===
LAYER_VIEW
&&
!
this
.
pipelineLayers
)
{
this
.
pipelineLayers
=
listByLayers
(
this
.
pipeline
);
}
return
this
.
pipelineLayers
;
},
hideAlert
()
{
hideAlert
()
{
this
.
showAlert
=
false
;
this
.
showAlert
=
false
;
this
.
alertType
=
null
;
this
.
alertType
=
null
;
...
@@ -192,6 +201,8 @@ export default {
...
@@ -192,6 +201,8 @@ export default {
v-if=
"pipeline"
v-if=
"pipeline"
:config-paths=
"configPaths"
:config-paths=
"configPaths"
:pipeline=
"pipeline"
:pipeline=
"pipeline"
:pipeline-layers=
"getPipelineLayers()"
:view-type=
"currentViewType"
@
error=
"reportFailure"
@
error=
"reportFailure"
@
refreshPipelineGraph=
"refreshPipelineGraph"
@
refreshPipelineGraph=
"refreshPipelineGraph"
/>
/>
...
...
app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
View file @
05f0211d
...
@@ -55,16 +55,16 @@ export default {
...
@@ -55,16 +55,16 @@ export default {
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"gl-display-flex gl-
justify-content-end gl-
align-items-center gl-my-4"
>
<div
class=
"gl-display-flex gl-align-items-center gl-my-4"
>
<span>
{{
$options
.
i18n
.
labelText
}}
</span>
<span>
{{
$options
.
i18n
.
labelText
}}
</span>
<gl-dropdown
class=
"gl-ml-4"
:right=
"true"
>
<gl-dropdown
class=
"gl-ml-4"
>
<template
#button-content
>
<template
#button-content
>
<gl-sprintf
:message=
"currentDropdownText"
>
<gl-sprintf
:message=
"currentDropdownText"
>
<template
#code
="
{ content }">
<template
#code
="
{ content }">
<code>
{{
content
}}
</code>
<code>
{{
content
}}
</code>
</
template
>
</
template
>
</gl-sprintf>
</gl-sprintf>
<gl-icon
class=
"gl-px-2"
name=
"angle-down"
:size=
"1
8
"
/>
<gl-icon
class=
"gl-px-2"
name=
"angle-down"
:size=
"1
6
"
/>
</template>
</template>
<gl-dropdown-item
<gl-dropdown-item
v-for=
"view in $options.views"
v-for=
"view in $options.views"
...
...
app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
View file @
05f0211d
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
import
getPipelineDetails
from
'
shared_queries/pipelines/get_pipeline_details.query.graphql
'
;
import
getPipelineDetails
from
'
shared_queries/pipelines/get_pipeline_details.query.graphql
'
;
import
{
LOAD_FAILURE
}
from
'
../../constants
'
;
import
{
LOAD_FAILURE
}
from
'
../../constants
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
{
ONE_COL_WIDTH
,
UPSTREAM
}
from
'
./constants
'
;
import
{
listByLayers
}
from
'
../parsing_utils
'
;
import
{
ONE_COL_WIDTH
,
UPSTREAM
,
LAYER_VIEW
}
from
'
./constants
'
;
import
LinkedPipeline
from
'
./linked_pipeline.vue
'
;
import
LinkedPipeline
from
'
./linked_pipeline.vue
'
;
import
{
import
{
getQueryHeaders
,
getQueryHeaders
,
...
@@ -35,11 +36,16 @@ export default {
...
@@ -35,11 +36,16 @@ export default {
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
viewType
:
{
type
:
String
,
required
:
true
,
},
},
},
data
()
{
data
()
{
return
{
return
{
currentPipeline
:
null
,
currentPipeline
:
null
,
loadingPipelineId
:
null
,
loadingPipelineId
:
null
,
pipelineLayers
:
{},
pipelineExpanded
:
false
,
pipelineExpanded
:
false
,
};
};
},
},
...
@@ -123,6 +129,13 @@ export default {
...
@@ -123,6 +129,13 @@ export default {
toggleQueryPollingByVisibility
(
this
.
$apollo
.
queries
.
currentPipeline
);
toggleQueryPollingByVisibility
(
this
.
$apollo
.
queries
.
currentPipeline
);
},
},
getPipelineLayers
(
id
)
{
if
(
this
.
viewType
===
LAYER_VIEW
&&
!
this
.
pipelineLayers
[
id
])
{
this
.
pipelineLayers
[
id
]
=
listByLayers
(
this
.
currentPipeline
);
}
return
this
.
pipelineLayers
[
id
];
},
isExpanded
(
id
)
{
isExpanded
(
id
)
{
return
Boolean
(
this
.
currentPipeline
?.
id
&&
id
===
this
.
currentPipeline
.
id
);
return
Boolean
(
this
.
currentPipeline
?.
id
&&
id
===
this
.
currentPipeline
.
id
);
},
},
...
@@ -203,7 +216,9 @@ export default {
...
@@ -203,7 +216,9 @@ export default {
class=
"d-inline-block gl-mt-n2"
class=
"d-inline-block gl-mt-n2"
:config-paths=
"configPaths"
:config-paths=
"configPaths"
:pipeline=
"currentPipeline"
:pipeline=
"currentPipeline"
:pipeline-layers=
"getPipelineLayers(pipeline.id)"
:is-linked-pipeline=
"true"
:is-linked-pipeline=
"true"
:view-type=
"viewType"
/>
/>
</div>
</div>
</li>
</li>
...
...
app/assets/javascripts/pipelines/components/graph/utils.js
View file @
05f0211d
import
Visibility
from
'
visibilityjs
'
;
import
Visibility
from
'
visibilityjs
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
unwrapStagesWithNeeds
}
from
'
../unwrapping_utils
'
;
import
{
unwrapStagesWithNeeds
AndLookup
}
from
'
../unwrapping_utils
'
;
const
addMulti
=
(
mainPipelineProjectPath
,
linkedPipeline
)
=>
{
const
addMulti
=
(
mainPipelineProjectPath
,
linkedPipeline
)
=>
{
return
{
return
{
...
@@ -86,12 +86,13 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => {
...
@@ -86,12 +86,13 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => {
stages
:
{
nodes
:
stages
},
stages
:
{
nodes
:
stages
},
}
=
pipeline
;
}
=
pipeline
;
const
nodes
=
unwrapStagesWithNeeds
(
stages
);
const
{
stages
:
updatedStages
,
lookup
}
=
unwrapStagesWithNeedsAndLookup
(
stages
);
return
{
return
{
...
pipeline
,
...
pipeline
,
id
:
getIdFromGraphQLId
(
pipeline
.
id
),
id
:
getIdFromGraphQLId
(
pipeline
.
id
),
stages
:
nodes
,
stages
:
updatedStages
,
stagesLookup
:
lookup
,
upstream
:
upstream
upstream
:
upstream
?
[
upstream
].
map
(
addMulti
.
bind
(
null
,
mainPipelineProjectPath
)).
map
(
transformId
)
?
[
upstream
].
map
(
addMulti
.
bind
(
null
,
mainPipelineProjectPath
)).
map
(
transformId
)
:
[],
:
[],
...
...
app/assets/javascripts/pipelines/components/graph_shared/links_inner.vue
View file @
05f0211d
...
@@ -11,6 +11,7 @@ import {
...
@@ -11,6 +11,7 @@ import {
import
{
performanceMarkAndMeasure
}
from
'
~/performance/utils
'
;
import
{
performanceMarkAndMeasure
}
from
'
~/performance/utils
'
;
import
{
DRAW_FAILURE
}
from
'
../../constants
'
;
import
{
DRAW_FAILURE
}
from
'
../../constants
'
;
import
{
createJobsHash
,
generateJobNeedsDict
,
reportToSentry
}
from
'
../../utils
'
;
import
{
createJobsHash
,
generateJobNeedsDict
,
reportToSentry
}
from
'
../../utils
'
;
import
{
STAGE_VIEW
}
from
'
../graph/constants
'
;
import
{
parseData
}
from
'
../parsing_utils
'
;
import
{
parseData
}
from
'
../parsing_utils
'
;
import
{
reportPerformance
}
from
'
./api
'
;
import
{
reportPerformance
}
from
'
./api
'
;
import
{
generateLinksData
}
from
'
./drawing_utils
'
;
import
{
generateLinksData
}
from
'
./drawing_utils
'
;
...
@@ -54,11 +55,17 @@ export default {
...
@@ -54,11 +55,17 @@ export default {
required
:
false
,
required
:
false
,
default
:
''
,
default
:
''
,
},
},
viewType
:
{
type
:
String
,
required
:
false
,
default
:
STAGE_VIEW
,
},
},
},
data
()
{
data
()
{
return
{
return
{
links
:
[],
links
:
[],
needsObject
:
null
,
needsObject
:
null
,
parsedData
:
{},
};
};
},
},
computed
:
{
computed
:
{
...
@@ -108,6 +115,15 @@ export default {
...
@@ -108,6 +115,15 @@ export default {
highlightedJobs
(
jobs
)
{
highlightedJobs
(
jobs
)
{
this
.
$emit
(
'
highlightedJobsChange
'
,
jobs
);
this
.
$emit
(
'
highlightedJobsChange
'
,
jobs
);
},
},
viewType
()
{
/*
We need to wait a tick so that the layout reflows
before the links refresh.
*/
this
.
$nextTick
(()
=>
{
this
.
refreshLinks
();
});
},
},
},
errorCaptured
(
err
,
_vm
,
info
)
{
errorCaptured
(
err
,
_vm
,
info
)
{
reportToSentry
(
this
.
$options
.
name
,
`error:
${
err
}
, info:
${
info
}
`
);
reportToSentry
(
this
.
$options
.
name
,
`error:
${
err
}
, info:
${
info
}
`
);
...
@@ -166,14 +182,17 @@ export default {
...
@@ -166,14 +182,17 @@ export default {
this
.
beginPerfMeasure
();
this
.
beginPerfMeasure
();
try
{
try
{
const
arrayOfJobs
=
this
.
pipelineData
.
flatMap
(({
groups
})
=>
groups
);
const
arrayOfJobs
=
this
.
pipelineData
.
flatMap
(({
groups
})
=>
groups
);
const
parsedData
=
parseData
(
arrayOfJobs
);
this
.
parsedData
=
parseData
(
arrayOfJobs
);
this
.
links
=
generateLinksData
(
parsedData
,
this
.
containerId
,
`-
${
this
.
pipelineId
}
`
);
this
.
refreshLinks
(
);
}
catch
(
err
)
{
}
catch
(
err
)
{
this
.
$emit
(
'
error
'
,
{
type
:
DRAW_FAILURE
,
reportToSentry
:
false
});
this
.
$emit
(
'
error
'
,
{
type
:
DRAW_FAILURE
,
reportToSentry
:
false
});
reportToSentry
(
this
.
$options
.
name
,
err
);
reportToSentry
(
this
.
$options
.
name
,
err
);
}
}
this
.
finishPerfMeasureAndSend
();
this
.
finishPerfMeasureAndSend
();
},
},
refreshLinks
()
{
this
.
links
=
generateLinksData
(
this
.
parsedData
,
this
.
containerId
,
`-
${
this
.
pipelineId
}
`
);
},
getLinkClasses
(
link
)
{
getLinkClasses
(
link
)
{
return
[
return
[
this
.
isLinkHighlighted
(
link
.
ref
)
?
'
gl-stroke-blue-400
'
:
this
.
defaultLinkColor
,
this
.
isLinkHighlighted
(
link
.
ref
)
?
'
gl-stroke-blue-400
'
:
this
.
defaultLinkColor
,
...
...
app/assets/javascripts/pipelines/components/parsing_utils.js
View file @
05f0211d
import
{
uniqWith
,
isEqual
}
from
'
lodash
'
;
import
{
uniqWith
,
isEqual
}
from
'
lodash
'
;
import
{
createSankey
}
from
'
./dag/drawing_utils
'
;
/*
/*
The following functions are the main engine in transforming the data as
The following functions are the main engine in transforming the data as
...
@@ -144,3 +145,28 @@ export const getMaxNodes = (nodes) => {
...
@@ -144,3 +145,28 @@ export const getMaxNodes = (nodes) => {
export
const
removeOrphanNodes
=
(
sankeyfiedNodes
)
=>
{
export
const
removeOrphanNodes
=
(
sankeyfiedNodes
)
=>
{
return
sankeyfiedNodes
.
filter
((
node
)
=>
node
.
sourceLinks
.
length
||
node
.
targetLinks
.
length
);
return
sankeyfiedNodes
.
filter
((
node
)
=>
node
.
sourceLinks
.
length
||
node
.
targetLinks
.
length
);
};
};
/*
This utility accepts unwrapped pipeline data in the format returned from
our standard pipeline GraphQL query and returns a list of names by layer
for the layer view. It can be combined with the stageLookup on the pipeline
to generate columns by layer.
*/
export
const
listByLayers
=
({
stages
})
=>
{
const
arrayOfJobs
=
stages
.
flatMap
(({
groups
})
=>
groups
);
const
parsedData
=
parseData
(
arrayOfJobs
);
const
dataWithLayers
=
createSankey
()(
parsedData
);
return
dataWithLayers
.
nodes
.
reduce
((
acc
,
{
layer
,
name
})
=>
{
/* sort groups by layer */
if
(
!
acc
[
layer
])
{
acc
[
layer
]
=
[];
}
acc
[
layer
].
push
(
name
);
return
acc
;
},
[]);
};
app/assets/javascripts/pipelines/components/unwrapping_utils.js
View file @
05f0211d
import
{
reportToSentry
}
from
'
../utils
'
;
import
{
reportToSentry
}
from
'
../utils
'
;
const
unwrapGroups
=
(
stages
)
=>
{
const
unwrapGroups
=
(
stages
)
=>
{
return
stages
.
map
((
stage
)
=>
{
return
stages
.
map
((
stage
,
idx
)
=>
{
const
{
const
{
groups
:
{
nodes
:
groups
},
groups
:
{
nodes
:
groups
},
}
=
stage
;
}
=
stage
;
return
{
...
stage
,
groups
};
return
{
node
:
{
...
stage
,
groups
},
lookup
:
{
stageIdx
:
idx
}
};
});
});
};
};
...
@@ -23,20 +23,34 @@ const unwrapJobWithNeeds = (denodedJobArray) => {
...
@@ -23,20 +23,34 @@ const unwrapJobWithNeeds = (denodedJobArray) => {
return
unwrapNodesWithName
(
denodedJobArray
,
'
needs
'
);
return
unwrapNodesWithName
(
denodedJobArray
,
'
needs
'
);
};
};
const
unwrapStagesWithNeeds
=
(
denodedStages
)
=>
{
const
unwrapStagesWithNeeds
AndLookup
=
(
denodedStages
)
=>
{
const
unwrappedNestedGroups
=
unwrapGroups
(
denodedStages
);
const
unwrappedNestedGroups
=
unwrapGroups
(
denodedStages
);
const
nodes
=
unwrappedNestedGroups
.
map
((
node
)
=>
{
const
lookupMap
=
{};
const
nodes
=
unwrappedNestedGroups
.
map
(({
node
,
lookup
})
=>
{
const
{
groups
}
=
node
;
const
{
groups
}
=
node
;
const
groupsWithJobs
=
groups
.
map
((
group
)
=>
{
const
groupsWithJobs
=
groups
.
map
((
group
,
idx
)
=>
{
const
jobs
=
unwrapJobWithNeeds
(
group
.
jobs
.
nodes
);
const
jobs
=
unwrapJobWithNeeds
(
group
.
jobs
.
nodes
);
lookupMap
[
group
.
name
]
=
{
...
lookup
,
groupIdx
:
idx
};
return
{
...
group
,
jobs
};
return
{
...
group
,
jobs
};
});
});
return
{
...
node
,
groups
:
groupsWithJobs
};
return
{
...
node
,
groups
:
groupsWithJobs
};
});
});
return
nodes
;
return
{
stages
:
nodes
,
lookup
:
lookupMap
}
;
};
};
export
{
unwrapGroups
,
unwrapNodesWithName
,
unwrapJobWithNeeds
,
unwrapStagesWithNeeds
};
const
unwrapStagesWithNeeds
=
(
denodedStages
)
=>
{
return
unwrapStagesWithNeedsAndLookup
(
denodedStages
).
stages
;
};
export
{
unwrapGroups
,
unwrapJobWithNeeds
,
unwrapNodesWithName
,
unwrapStagesWithNeeds
,
unwrapStagesWithNeedsAndLookup
,
};
spec/frontend/pipelines/graph/graph_component_spec.js
View file @
05f0211d
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GRAPHQL
}
from
'
~/pipelines/components/graph/constants
'
;
import
{
GRAPHQL
,
STAGE_VIEW
}
from
'
~/pipelines/components/graph/constants
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
JobItem
from
'
~/pipelines/components/graph/job_item.vue
'
;
import
JobItem
from
'
~/pipelines/components/graph/job_item.vue
'
;
import
LinkedPipelinesColumn
from
'
~/pipelines/components/graph/linked_pipelines_column.vue
'
;
import
LinkedPipelinesColumn
from
'
~/pipelines/components/graph/linked_pipelines_column.vue
'
;
...
@@ -20,6 +20,7 @@ describe('graph component', () => {
...
@@ -20,6 +20,7 @@ describe('graph component', () => {
const
defaultProps
=
{
const
defaultProps
=
{
pipeline
:
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
),
pipeline
:
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
),
viewType
:
STAGE_VIEW
,
configPaths
:
{
configPaths
:
{
metricsPath
:
''
,
metricsPath
:
''
,
graphqlResourceEtag
:
'
this/is/a/path
'
,
graphqlResourceEtag
:
'
this/is/a/path
'
,
...
...
spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
View file @
05f0211d
import
{
GlAlert
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
GlAlert
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
getPipelineDetails
from
'
shared_queries/pipelines/get_pipeline_details.query.graphql
'
;
import
getPipelineDetails
from
'
shared_queries/pipelines/get_pipeline_details.query.graphql
'
;
import
{
IID_FAILURE
}
from
'
~/pipelines/components/graph/constants
'
;
import
{
IID_FAILURE
,
LAYER_VIEW
,
STAGE_VIEW
}
from
'
~/pipelines/components/graph/constants
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
PipelineGraphWrapper
from
'
~/pipelines/components/graph/graph_component_wrapper.vue
'
;
import
PipelineGraphWrapper
from
'
~/pipelines/components/graph/graph_component_wrapper.vue
'
;
import
GraphViewSelector
from
'
~/pipelines/components/graph/graph_view_selector.vue
'
;
import
GraphViewSelector
from
'
~/pipelines/components/graph/graph_view_selector.vue
'
;
import
StageColumnComponent
from
'
~/pipelines/components/graph/stage_column_component.vue
'
;
import
*
as
parsingUtils
from
'
~/pipelines/components/parsing_utils
'
;
import
{
mockPipelineResponse
}
from
'
./mock_data
'
;
import
{
mockPipelineResponse
}
from
'
./mock_data
'
;
const
defaultProvide
=
{
const
defaultProvide
=
{
...
@@ -24,6 +26,9 @@ describe('Pipeline graph wrapper', () => {
...
@@ -24,6 +26,9 @@ describe('Pipeline graph wrapper', () => {
const
getAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
getAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
getLoadingIcon
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
getLoadingIcon
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
getGraph
=
()
=>
wrapper
.
find
(
PipelineGraph
);
const
getGraph
=
()
=>
wrapper
.
find
(
PipelineGraph
);
const
getStageColumnTitle
=
()
=>
wrapper
.
find
(
'
[data-testid="stage-column-title"]
'
);
const
getAllStageColumnGroupsInColumn
=
()
=>
wrapper
.
find
(
StageColumnComponent
).
findAll
(
'
[data-testid="stage-column-group"]
'
);
const
getViewSelector
=
()
=>
wrapper
.
find
(
GraphViewSelector
);
const
getViewSelector
=
()
=>
wrapper
.
find
(
GraphViewSelector
);
const
createComponent
=
({
const
createComponent
=
({
...
@@ -48,12 +53,13 @@ describe('Pipeline graph wrapper', () => {
...
@@ -48,12 +53,13 @@ describe('Pipeline graph wrapper', () => {
const
createComponentWithApollo
=
({
const
createComponentWithApollo
=
({
getPipelineDetailsHandler
=
jest
.
fn
().
mockResolvedValue
(
mockPipelineResponse
),
getPipelineDetailsHandler
=
jest
.
fn
().
mockResolvedValue
(
mockPipelineResponse
),
mountFn
=
shallowMount
,
provide
=
{},
provide
=
{},
}
=
{})
=>
{
}
=
{})
=>
{
const
requestHandlers
=
[[
getPipelineDetails
,
getPipelineDetailsHandler
]];
const
requestHandlers
=
[[
getPipelineDetails
,
getPipelineDetailsHandler
]];
const
apolloProvider
=
createMockApollo
(
requestHandlers
);
const
apolloProvider
=
createMockApollo
(
requestHandlers
);
createComponent
({
apolloProvider
,
provide
});
createComponent
({
apolloProvider
,
provide
,
mountFn
});
};
};
afterEach
(()
=>
{
afterEach
(()
=>
{
...
@@ -223,13 +229,16 @@ describe('Pipeline graph wrapper', () => {
...
@@ -223,13 +229,16 @@ describe('Pipeline graph wrapper', () => {
});
});
describe
(
'
when feature flag is on
'
,
()
=>
{
describe
(
'
when feature flag is on
'
,
()
=>
{
let
layersFn
;
beforeEach
(
async
()
=>
{
beforeEach
(
async
()
=>
{
layersFn
=
jest
.
spyOn
(
parsingUtils
,
'
listByLayers
'
);
createComponentWithApollo
({
createComponentWithApollo
({
provide
:
{
provide
:
{
glFeatures
:
{
glFeatures
:
{
pipelineGraphLayersView
:
true
,
pipelineGraphLayersView
:
true
,
},
},
},
},
mountFn
:
mount
,
});
});
jest
.
runOnlyPendingTimers
();
jest
.
runOnlyPendingTimers
();
...
@@ -239,6 +248,26 @@ describe('Pipeline graph wrapper', () => {
...
@@ -239,6 +248,26 @@ describe('Pipeline graph wrapper', () => {
it
(
'
appears
'
,
()
=>
{
it
(
'
appears
'
,
()
=>
{
expect
(
getViewSelector
().
exists
()).
toBe
(
true
);
expect
(
getViewSelector
().
exists
()).
toBe
(
true
);
});
});
it
(
'
switches between views
'
,
async
()
=>
{
const
groupsInFirstColumn
=
mockPipelineResponse
.
data
.
project
.
pipeline
.
stages
.
nodes
[
0
].
groups
.
nodes
.
length
;
expect
(
getAllStageColumnGroupsInColumn
()).
toHaveLength
(
groupsInFirstColumn
);
expect
(
getStageColumnTitle
().
text
()).
toBe
(
'
Build
'
);
await
getViewSelector
().
vm
.
$emit
(
'
updateViewType
'
,
LAYER_VIEW
);
expect
(
getAllStageColumnGroupsInColumn
()).
toHaveLength
(
groupsInFirstColumn
+
1
);
expect
(
getStageColumnTitle
().
text
()).
toBe
(
''
);
});
it
(
'
calls listByLayers only once no matter how many times view is switched
'
,
async
()
=>
{
expect
(
layersFn
).
not
.
toHaveBeenCalled
();
await
getViewSelector
().
vm
.
$emit
(
'
updateViewType
'
,
LAYER_VIEW
);
expect
(
layersFn
).
toHaveBeenCalledTimes
(
1
);
await
getViewSelector
().
vm
.
$emit
(
'
updateViewType
'
,
STAGE_VIEW
);
await
getViewSelector
().
vm
.
$emit
(
'
updateViewType
'
,
LAYER_VIEW
);
await
getViewSelector
().
vm
.
$emit
(
'
updateViewType
'
,
STAGE_VIEW
);
expect
(
layersFn
).
toHaveBeenCalledTimes
(
1
);
});
});
});
});
});
});
});
spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
View file @
05f0211d
...
@@ -2,10 +2,17 @@ import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
...
@@ -2,10 +2,17 @@ import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import
VueApollo
from
'
vue-apollo
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
getPipelineDetails
from
'
shared_queries/pipelines/get_pipeline_details.query.graphql
'
;
import
getPipelineDetails
from
'
shared_queries/pipelines/get_pipeline_details.query.graphql
'
;
import
{
DOWNSTREAM
,
GRAPHQL
,
UPSTREAM
}
from
'
~/pipelines/components/graph/constants
'
;
import
{
DOWNSTREAM
,
GRAPHQL
,
UPSTREAM
,
LAYER_VIEW
,
STAGE_VIEW
,
}
from
'
~/pipelines/components/graph/constants
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
LinkedPipeline
from
'
~/pipelines/components/graph/linked_pipeline.vue
'
;
import
LinkedPipeline
from
'
~/pipelines/components/graph/linked_pipeline.vue
'
;
import
LinkedPipelinesColumn
from
'
~/pipelines/components/graph/linked_pipelines_column.vue
'
;
import
LinkedPipelinesColumn
from
'
~/pipelines/components/graph/linked_pipelines_column.vue
'
;
import
*
as
parsingUtils
from
'
~/pipelines/components/parsing_utils
'
;
import
{
LOAD_FAILURE
}
from
'
~/pipelines/constants
'
;
import
{
LOAD_FAILURE
}
from
'
~/pipelines/constants
'
;
import
{
import
{
mockPipelineResponse
,
mockPipelineResponse
,
...
@@ -20,6 +27,7 @@ describe('Linked Pipelines Column', () => {
...
@@ -20,6 +27,7 @@ describe('Linked Pipelines Column', () => {
columnTitle
:
'
Downstream
'
,
columnTitle
:
'
Downstream
'
,
linkedPipelines
:
processedPipeline
.
downstream
,
linkedPipelines
:
processedPipeline
.
downstream
,
type
:
DOWNSTREAM
,
type
:
DOWNSTREAM
,
viewType
:
STAGE_VIEW
,
configPaths
:
{
configPaths
:
{
metricsPath
:
''
,
metricsPath
:
''
,
graphqlResourceEtag
:
'
this/is/a/path
'
,
graphqlResourceEtag
:
'
this/is/a/path
'
,
...
@@ -67,7 +75,7 @@ describe('Linked Pipelines Column', () => {
...
@@ -67,7 +75,7 @@ describe('Linked Pipelines Column', () => {
describe
(
'
it renders correctly
'
,
()
=>
{
describe
(
'
it renders correctly
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
();
createComponent
WithApollo
();
});
});
it
(
'
renders the pipeline title
'
,
()
=>
{
it
(
'
renders the pipeline title
'
,
()
=>
{
...
@@ -91,6 +99,27 @@ describe('Linked Pipelines Column', () => {
...
@@ -91,6 +99,27 @@ describe('Linked Pipelines Column', () => {
await
wrapper
.
vm
.
$nextTick
();
await
wrapper
.
vm
.
$nextTick
();
};
};
describe
(
'
layer type rendering
'
,
()
=>
{
let
layersFn
;
beforeEach
(()
=>
{
layersFn
=
jest
.
spyOn
(
parsingUtils
,
'
listByLayers
'
);
createComponentWithApollo
({
mountFn
:
mount
});
});
it
(
'
calls listByLayers only once no matter how many times view is switched
'
,
async
()
=>
{
expect
(
layersFn
).
not
.
toHaveBeenCalled
();
await
clickExpandButtonAndAwaitTimers
();
await
wrapper
.
setProps
({
viewType
:
LAYER_VIEW
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
layersFn
).
toHaveBeenCalledTimes
(
1
);
await
wrapper
.
setProps
({
viewType
:
STAGE_VIEW
});
await
wrapper
.
setProps
({
viewType
:
LAYER_VIEW
});
await
wrapper
.
setProps
({
viewType
:
STAGE_VIEW
});
expect
(
layersFn
).
toHaveBeenCalledTimes
(
1
);
});
});
describe
(
'
downstream
'
,
()
=>
{
describe
(
'
downstream
'
,
()
=>
{
describe
(
'
when successful
'
,
()
=>
{
describe
(
'
when successful
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
...
...
spec/frontend/pipelines/graph/mock_data.js
View file @
05f0211d
...
@@ -434,21 +434,7 @@ export const mockPipelineResponse = {
...
@@ -434,21 +434,7 @@ export const mockPipelineResponse = {
},
},
needs
:
{
needs
:
{
__typename
:
'
CiBuildNeedConnection
'
,
__typename
:
'
CiBuildNeedConnection
'
,
nodes
:
[
nodes
:
[],
{
__typename
:
'
CiBuildNeed
'
,
name
:
'
build_c
'
,
},
{
__typename
:
'
CiBuildNeed
'
,
name
:
'
build_b
'
,
},
{
__typename
:
'
CiBuildNeed
'
,
name
:
'
build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl
'
,
},
],
},
},
},
},
],
],
...
...
spec/frontend/pipelines/unwrapping_utils_spec.js
View file @
05f0211d
...
@@ -96,11 +96,11 @@ const completeMock = [
...
@@ -96,11 +96,11 @@ const completeMock = [
describe
(
'
Shared pipeline unwrapping utils
'
,
()
=>
{
describe
(
'
Shared pipeline unwrapping utils
'
,
()
=>
{
describe
(
'
unwrapGroups
'
,
()
=>
{
describe
(
'
unwrapGroups
'
,
()
=>
{
it
(
'
takes stages without nodes and returns the unwrapped groups
'
,
()
=>
{
it
(
'
takes stages without nodes and returns the unwrapped groups
'
,
()
=>
{
expect
(
unwrapGroups
(
stagesAndGroups
)[
0
].
groups
).
toEqual
(
groupsArray
);
expect
(
unwrapGroups
(
stagesAndGroups
)[
0
].
node
.
groups
).
toEqual
(
groupsArray
);
});
});
it
(
'
keeps other stage properties intact
'
,
()
=>
{
it
(
'
keeps other stage properties intact
'
,
()
=>
{
expect
(
unwrapGroups
(
stagesAndGroups
)[
0
]).
toMatchObject
(
basicStageInfo
);
expect
(
unwrapGroups
(
stagesAndGroups
)[
0
]
.
node
).
toMatchObject
(
basicStageInfo
);
});
});
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment