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
c771b661
Commit
c771b661
authored
May 22, 2020
by
Sarah GP
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Destructure all the functions
Also applies linting fixes
parent
a5fe808a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
68 additions
and
78 deletions
+68
-78
app/assets/javascripts/pipelines/components/dag/dag.vue
app/assets/javascripts/pipelines/components/dag/dag.vue
+0
-5
app/assets/javascripts/pipelines/components/dag/utils.js
app/assets/javascripts/pipelines/components/dag/utils.js
+63
-66
spec/frontend/pipelines/components/dag/mock_data.js
spec/frontend/pipelines/components/dag/mock_data.js
+2
-2
spec/frontend/pipelines/components/dag/utils_spec.js
spec/frontend/pipelines/components/dag/utils_spec.js
+3
-5
No files found.
app/assets/javascripts/pipelines/components/dag/dag.vue
View file @
c771b661
...
...
@@ -40,11 +40,6 @@ export default {
})
.
catch
(
reportFailure
);
},
computed
:
{
shouldDisplayGraph
()
{
return
!
this
.
showFailureAlert
;
},
},
methods
:
{
drawGraph
(
data
)
{
return
data
;
...
...
app/assets/javascripts/pipelines/components/dag/utils.js
View file @
c771b661
import
*
as
d3
from
'
d3
'
;
import
{
sankey
,
sankeyLeft
}
from
'
d3-sankey
'
;
import
{
uniqWith
,
isEqual
}
from
'
lodash
'
;
/*
*
The following functions are the main engine in transforming the data as
received from the endpoint into the format the d3 graph expects.
Input is of the form:
[stages]
stages: {name, groups}
groups: [{ name, size, jobs }]
name is a group name; in the case that the group has one job, it is
also the job name
size is the number of parallel jobs
jobs: [{ name, needs}]
job name is either the same as the group name or group x/y
Output is of the form:
{ nodes: [node], links: [link] }
node: { name, category }, + unused info passed through
link: { source, target, value }, with source & target being node names
and value being a constant
We create nodes, create links, and then dedupe the links, so that in the case where
job 4 depends on job 1 and job 2, and job 2 depends on job 1, we show only a single link
from job 1 to job 2 then another from job 2 to job 4.
CREATE NODES
stage.name -> node.category
stage.group.name -> node.name (this is the group name if there are parallel jobs)
stage.group.jobs -> node.jobs
stage.group.size -> node.size
CREATE LINKS
stages.groups.name -> target
stages.groups.needs.each -> source (source is the name of the group, not the parallel job)
10 -> value (constant)
*
*/
/*
The following functions are the main engine in transforming the data as
received from the endpoint into the format the d3 graph expects.
Input is of the form:
[stages]
stages: {name, groups}
groups: [{ name, size, jobs }]
name is a group name; in the case that the group has one job, it is
also the job name
size is the number of parallel jobs
jobs: [{ name, needs}]
job name is either the same as the group name or group x/y
Output is of the form:
{ nodes: [node], links: [link] }
node: { name, category }, + unused info passed through
link: { source, target, value }, with source & target being node names
and value being a constant
We create nodes, create links, and then dedupe the links, so that in the case where
job 4 depends on job 1 and job 2, and job 2 depends on job 1, we show only a single link
from job 1 to job 2 then another from job 2 to job 4.
CREATE NODES
stage.name -> node.category
stage.group.name -> node.name (this is the group name if there are parallel jobs)
stage.group.jobs -> node.jobs
stage.group.size -> node.size
CREATE LINKS
stages.groups.name -> target
stages.groups.needs.each -> source (source is the name of the group, not the parallel job)
10 -> value (constant)
*/
export
const
createNodes
=
data
=>
{
return
data
.
map
(({
groups
},
idx
,
stages
)
=>
{
return
groups
.
map
(
group
=>
{
return
{
...
group
,
category
:
stages
[
idx
].
name
};
});
})
.
flat
();
return
data
.
flatMap
(({
groups
,
name
})
=>
{
return
groups
.
map
(
group
=>
{
return
{
...
group
,
category
:
name
};
});
});
};
export
const
createNodeDict
=
nodes
=>
{
...
...
@@ -110,24 +107,24 @@ export const getAllAncestors = (nodes, nodeDict) => {
};
export
const
filterByAncestors
=
(
links
,
nodeDict
)
=>
links
.
filter
(
link
=>
{
links
.
filter
(
({
target
,
source
})
=>
{
/*
for every link, check out it's target
for every target, get the target node's needs
then drop the current link source from that list
for every link, check out it's target
for every target, get the target node's needs
then drop the current link source from that list
call a function to get all ancestors, recursively
is the current link's source in the list of all parents?
then we drop this link
call a function to get all ancestors, recursively
is the current link's source in the list of all parents?
then we drop this link
*/
const
targetNode
=
link
.
target
;
const
targetNode
=
target
;
const
targetNodeNeeds
=
nodeDict
[
targetNode
].
needs
;
const
targetNodeNeedsMinusSource
=
targetNodeNeeds
.
filter
(
need
=>
need
!==
link
.
source
);
const
targetNodeNeedsMinusSource
=
targetNodeNeeds
.
filter
(
need
=>
need
!==
source
);
const
allAncestors
=
getAllAncestors
(
targetNodeNeedsMinusSource
,
nodeDict
);
return
!
allAncestors
.
includes
(
link
.
source
);
return
!
allAncestors
.
includes
(
source
);
});
export
const
parseData
=
data
=>
{
...
...
@@ -139,14 +136,14 @@ export const parseData = data => {
return
{
nodes
,
links
};
};
/*
*
createSankey calls the d3 layout to generate the relationships and positioning
values for the nodes and links in the graph.
*
*/
/*
createSankey calls the d3 layout to generate the relationships and positioning
values for the nodes and links in the graph.
*/
export
const
createSankey
=
({
width
,
height
,
nodeWidth
,
nodePadding
,
paddingForLabels
})
=>
{
const
sankeyGenerator
=
sankey
()
.
nodeId
(
d
=>
d
.
name
)
.
nodeId
(
({
name
})
=>
name
)
.
nodeAlign
(
sankeyLeft
)
.
nodeWidth
(
nodeWidth
)
.
nodePadding
(
nodePadding
)
...
...
@@ -161,17 +158,17 @@ export const createSankey = ({ width, height, nodeWidth, nodePadding, paddingFor
});
};
/*
*
/*
The number of nodes in the most populous generation drives the height of the graph.
*
*
/
*/
export
const
getMaxNodes
=
nodes
=>
{
const
counts
=
nodes
.
reduce
((
acc
,
currentNode
)
=>
{
if
(
!
acc
[
currentNode
.
layer
])
{
acc
[
currentNode
.
layer
]
=
0
;
const
counts
=
nodes
.
reduce
((
acc
,
{
layer
}
)
=>
{
if
(
!
acc
[
layer
])
{
acc
[
layer
]
=
0
;
}
acc
[
currentNode
.
layer
]
+=
1
;
acc
[
layer
]
+=
1
;
return
acc
;
},
[]);
...
...
@@ -179,11 +176,11 @@ export const getMaxNodes = nodes => {
return
Math
.
max
(...
counts
);
};
/*
*
/*
Because we cannot know if a node is part of a relationship until after we
generate the links with createSankey, this function is used after the first call
to find nodes that have no relations.
*
*
/
*/
export
const
removeOrphanNodes
=
sankeyfiedNodes
=>
{
return
sankeyfiedNodes
.
filter
(
node
=>
node
.
sourceLinks
.
length
||
node
.
targetLinks
.
length
);
...
...
spec/frontend/pipelines/components/dag/mock
-
data.js
→
spec/frontend/pipelines/components/dag/mock
_
data.js
View file @
c771b661
/*
*
/*
It is important that the simple base include parallel jobs
as well as non-parallel jobs with spaces in the name to prevent
us relying on spaces as an indicator.
*
*
/
*/
export
default
{
stages
:
[
{
...
...
spec/frontend/pipelines/components/dag/utils_spec.js
View file @
c771b661
...
...
@@ -8,7 +8,7 @@ import {
getMaxNodes
,
}
from
'
~/pipelines/components/dag/utils
'
;
import
mockGraphData
from
'
./mock
-data.js
'
;
import
mockGraphData
from
'
./mock
_data
'
;
describe
(
'
DAG visualization parsing utilities
'
,
()
=>
{
const
{
nodes
,
nodeDict
}
=
createNodesStructure
(
mockGraphData
.
stages
);
...
...
@@ -71,7 +71,7 @@ describe('DAG visualization parsing utilities', () => {
const
dedupedLinks
=
[{
source
:
'
job1
'
,
target
:
'
job2
'
},
{
source
:
'
job2
'
,
target
:
'
job4
'
}];
const
node
Dict
=
{
const
node
Lookup
=
{
job1
:
{
name
:
'
job1
'
,
},
...
...
@@ -87,14 +87,12 @@ describe('DAG visualization parsing utilities', () => {
};
it
(
'
dedupes links
'
,
()
=>
{
expect
(
filterByAncestors
(
allLinks
,
node
Dict
)).
toMatchObject
(
dedupedLinks
);
expect
(
filterByAncestors
(
allLinks
,
node
Lookup
)).
toMatchObject
(
dedupedLinks
);
});
});
describe
(
'
parseData parent function
'
,
()
=>
{
it
(
'
returns an object containing a list of nodes and links
'
,
()
=>
{
const
parsed
=
parseData
(
mockGraphData
.
stages
);
// an array of nodes exist and the values are defined
expect
(
parsed
).
toHaveProperty
(
'
nodes
'
);
expect
(
Array
.
isArray
(
parsed
.
nodes
)).
toBe
(
true
);
...
...
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