Skip to content

Commit b9407f8

Browse files
authored
Recurse into child stages to flatten the tree (#83)
1 parent 727a202 commit b9407f8

File tree

2 files changed

+160
-6
lines changed

2 files changed

+160
-6
lines changed

src/main/frontend/pipeline-graph-view/pipeline-graph/main/PipelineGraphLayout.spec.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,5 +240,121 @@ describe("PipelineGraphLayout", () => {
240240
},
241241
]);
242242
});
243+
244+
it("returns proper columns (GH#50)", () => {
245+
const columns = createNodeColumns(
246+
[
247+
makeStage(3, "Parallel", [
248+
makeParallel(4, "parallel:0", [
249+
makeStage(6, "parent:0", [
250+
makeStage(9, "child:0"),
251+
makeStage(14, "child:1"),
252+
makeStage(19, "child:3"),
253+
]),
254+
]),
255+
]),
256+
makeStage(29, "parent:1"),
257+
],
258+
false
259+
);
260+
261+
expect(columns).toMatchObject([
262+
{
263+
hasBranchLabels: true,
264+
topStage: { name: "Parallel", id: 3 },
265+
rows: [
266+
[
267+
makeNode(6, "parent:0", "parallel:0"),
268+
makeNode(9, "child:0", "parent:0"),
269+
makeNode(14, "child:1", "parent:0"),
270+
makeNode(19, "child:3", "parent:0"),
271+
],
272+
],
273+
},
274+
{
275+
hasBranchLabels: false,
276+
topStage: { name: "parent:1", id: 29 },
277+
rows: [[makeNode(29, "parent:1")]],
278+
},
279+
]);
280+
});
281+
282+
it("returns proper columns (GH#18)", () => {
283+
const columns = createNodeColumns(
284+
[
285+
makeStage(6, "Stage 1"),
286+
makeStage(11, "Stage 2"),
287+
makeStage(31, "Stage6, When anyOf", [
288+
makeStage(12, "Stage 3"),
289+
makeStage(33, "Parallel", [
290+
makeParallel(36, "Parallel Stage 1", [
291+
makeStage(43, "Parallel Stage 1.1"),
292+
makeStage(61, "Parallel Stage 1.2"),
293+
]),
294+
makeParallel(37, "Parallel Stage 2", [
295+
makeStage(45, "Parallel Stage 2.1"),
296+
makeStage(63, "Parallel Stage 2.2"),
297+
]),
298+
]),
299+
makeStage(13, "Stage 4", [
300+
makeStage(14, "Stage 5", [makeStage(15, "Stage 7")]),
301+
]),
302+
]),
303+
],
304+
false
305+
);
306+
307+
expect(columns).toMatchObject([
308+
{
309+
hasBranchLabels: false,
310+
topStage: { name: "Stage 1", id: 6 },
311+
rows: [[makeNode(6, "Stage 1")]],
312+
},
313+
{
314+
hasBranchLabels: false,
315+
topStage: { name: "Stage 2", id: 11 },
316+
rows: [[makeNode(11, "Stage 2")]],
317+
},
318+
{
319+
hasBranchLabels: false,
320+
topStage: { name: "Stage6, When anyOf", id: 31 },
321+
rows: [[makeNode(31, "Stage6, When anyOf")]],
322+
},
323+
{
324+
hasBranchLabels: false,
325+
topStage: { name: "Stage 3", id: 12 },
326+
rows: [[makeNode(12, "Stage 3")]],
327+
},
328+
{
329+
hasBranchLabels: true,
330+
topStage: { name: "Parallel", id: 33 },
331+
rows: [
332+
[
333+
makeNode(43, "Parallel Stage 1.1", "Parallel Stage 1"),
334+
makeNode(61, "Parallel Stage 1.2", "Parallel Stage 1"),
335+
],
336+
[
337+
makeNode(45, "Parallel Stage 2.1", "Parallel Stage 2"),
338+
makeNode(63, "Parallel Stage 2.2", "Parallel Stage 2"),
339+
],
340+
],
341+
},
342+
{
343+
hasBranchLabels: false,
344+
topStage: { name: "Stage 4", id: 13 },
345+
rows: [[makeNode(13, "Stage 4")]],
346+
},
347+
{
348+
hasBranchLabels: false,
349+
topStage: { name: "Stage 5", id: 14 },
350+
rows: [[makeNode(14, "Stage 5")]],
351+
},
352+
{
353+
hasBranchLabels: false,
354+
topStage: { name: "Stage 7", id: 15 },
355+
rows: [[makeNode(15, "Stage 7")]],
356+
},
357+
]);
358+
});
243359
});
244360
});

src/main/frontend/pipeline-graph-view/pipeline-graph/main/PipelineGraphLayout.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@ export function createNodeColumns(
109109
};
110110
};
111111

112-
for (const topStage of topLevelStages) {
112+
const processTopStage = (topStage: StageInfo, willRecurse: boolean) => {
113113
// If stage has children, we don't draw a node for it, just its children
114114
const stagesForColumn =
115-
topStage.children && topStage.children.length
115+
!willRecurse && stageHasChildren(topStage)
116116
? topStage.children
117117
: [topStage];
118118

@@ -126,11 +126,11 @@ export function createNodeColumns(
126126

127127
for (const nodeStage of stagesForColumn) {
128128
const rowNodes: Array<NodeInfo> = [];
129-
if (!collasped && nodeStage.children && nodeStage.children.length) {
129+
if (!collasped && !willRecurse && stageHasChildren(nodeStage)) {
130130
column.hasBranchLabels = true;
131-
for (const childStage of nodeStage.children) {
132-
rowNodes.push(makeNodeForStage(childStage, nodeStage.name));
133-
}
131+
forEachChildStage(nodeStage, (parentStage, childStage, _) =>
132+
rowNodes.push(makeNodeForStage(childStage, parentStage.name))
133+
);
134134
} else {
135135
rowNodes.push(makeNodeForStage(nodeStage));
136136
}
@@ -141,11 +141,49 @@ export function createNodeColumns(
141141
}
142142

143143
nodeColumns.push(column);
144+
};
145+
146+
for (const protoTopStage of topLevelStages) {
147+
const selfParentTopStage = { ...protoTopStage, children: [protoTopStage] };
148+
149+
forEachChildStage(selfParentTopStage, (_, topStage, willRecurse) =>
150+
processTopStage(topStage, willRecurse)
151+
);
144152
}
145153

146154
return nodeColumns;
147155
}
148156

157+
/**
158+
* Check if stage has children.
159+
*/
160+
function stageHasChildren(stage: StageInfo): boolean {
161+
return !!(stage.children && stage.children.length);
162+
}
163+
164+
/**
165+
* Walk the children of the stage recursively (depth first), invoking callback for each child.
166+
*
167+
* Don't recurse into parallel children as those are processed separately.
168+
* If one child of the stage is parallel, we assume all of its children are.
169+
*/
170+
function forEachChildStage(
171+
topStage: StageInfo,
172+
callback: (parent: StageInfo, child: StageInfo, willRecurse: boolean) => void
173+
) {
174+
if (!stageHasChildren(topStage)) {
175+
return;
176+
}
177+
for (const stage of topStage.children) {
178+
const needToRecurse =
179+
stageHasChildren(stage) && stage.children[0].type != "PARALLEL";
180+
callback(topStage, stage, needToRecurse);
181+
if (needToRecurse) {
182+
forEachChildStage(stage, callback);
183+
}
184+
}
185+
}
186+
149187
/**
150188
* Walks the columns of nodes giving them x and y positions. Mutates the node objects in place for now.
151189
*/

0 commit comments

Comments
 (0)