Skip to content

Commit 9a9cd63

Browse files
committed
Convert stages to use card
Remove old graph code
1 parent 9b2f0b8 commit 9a9cd63

File tree

14 files changed

+70
-204
lines changed

14 files changed

+70
-204
lines changed

Diff for: src/main/frontend/multi-pipeline-graph-view/app.scss

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import '../pipeline-graph-view/cards';

Diff for: src/main/frontend/multi-pipeline-graph-view/multi-pipeline-graph/main/MultiPipelineGraph.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,7 @@ export const MultiPipelineGraph = () => {
1616
}
1717
}, [runs, poll]);
1818
return (
19-
<table className="jenkins-table sortable">
20-
<thead>
21-
<tr>
22-
<th className="jenkins-table__cell--tight">id</th>
23-
<th data-sort-disable="true">pipeline</th>
24-
</tr>
25-
</thead>
19+
<table>
2620
<tbody>
2721
{runs.map((run) => (
2822
<SingleRun key={run.id} run={run} />

Diff for: src/main/frontend/multi-pipeline-graph-view/multi-pipeline-graph/main/SingleRun.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,23 @@ interface Props {
1111

1212
export const SingleRun: (data: Props) => JSX.Element = ({ run }) => {
1313
const [stages, setStages] = useState<Array<StageInfo>>([]);
14-
const path = `graph?runId=${run.id}`;
14+
const path = `tree?runId=${run.id}`;
1515
const singleRunPage = `../${run.id}/pipeline-graph/`;
1616

1717
const handleNodeClick = (nodeName: string, id: number) => {
1818
console.log(nodeName, id);
1919
window.location.href = `../${run.id}/pipeline-console?selected-node=${id}`;
2020
};
21+
22+
const buildNameStyle: React.CSSProperties = {
23+
verticalAlign: "middle"
24+
};
25+
2126
return (
2227
<tr>
23-
<td>
28+
<td style={buildNameStyle}>
2429
<a href={singleRunPage} className="jenkins-table__link">
25-
{run.id}
30+
#{run.id}
2631
</a>
2732
</td>
2833
<td>

Diff for: src/main/frontend/multi-pipeline-graph-view/multi-pipeline-graph/main/support/startPollingRunsStatus.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { RunInfo } from "../MultiPipelineGraphModel";
77
export default function startPollingRunsStatus(
88
onFetchSuccess: (data: Array<RunInfo>) => void,
99
onFetchError: (err: Error) => void,
10-
interval = 10000
1110
) {
1211
const path = "runs";
1312
async function fetchPipelineData() {
@@ -19,7 +18,8 @@ export default function startPollingRunsStatus(
1918
// TODO: implement exponential backoff of the timeout interval
2019
onFetchError(err);
2120
} finally {
22-
setTimeout(() => fetchPipelineData(), interval);
21+
// TODO reimplement live loading but only while run is actually running, we don't want to be continuously hitting the controller for this
22+
// setTimeout(() => fetchPipelineData(), interval);
2323
}
2424
}
2525
fetchPipelineData();

Diff for: src/main/frontend/pipeline-graph-view/_cards.scss

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
:root {
2+
--card-background: hsl(212, 30%, 96%);
3+
--card-gap: 1rem;
4+
}
5+
6+
[data-theme="dark"] {
7+
--card-background: hsl(230deg 14% 23%);
8+
}
9+
10+
@media (prefers-color-scheme: dark) {
11+
[data-theme="dark-system"],
12+
[data-theme="dark-system"] {
13+
--card-background: hsl(230deg 14% 23%);
14+
}
15+
}
16+
117
.pgv-cards {
218
@media (max-width: 699px) {
319
&__item {

Diff for: src/main/frontend/pipeline-graph-view/app.scss

-16
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,3 @@
1-
:root {
2-
--card-background: hsl(212, 30%, 96%);
3-
--card-gap: 1rem;
4-
}
5-
6-
[data-theme="dark"] {
7-
--card-background: hsl(230deg 14% 23%);
8-
}
9-
10-
@media (prefers-color-scheme: dark) {
11-
[data-theme="dark-system"],
12-
[data-theme="dark-system"] {
13-
--card-background: hsl(230deg 14% 23%);
14-
}
15-
}
16-
171
@import "cards";
182

193
.app-details__item svg {

Diff for: src/main/java/io/jenkins/plugins/pipelinegraphview/multipipelinegraphview/MultiPipelineGraphViewAction.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ public Permission getPermission() {
4343
}
4444

4545
@GET
46-
@WebMethod(name = "graph")
47-
public HttpResponse getGraph(StaplerRequest req) throws JsonProcessingException {
46+
@WebMethod(name = "tree")
47+
public HttpResponse getTree(StaplerRequest req) throws JsonProcessingException {
4848
String runId = req.getParameter("runId");
4949
WorkflowRun run = target.getBuildByNumber(Integer.parseInt(runId));
5050
PipelineGraphApi api = new PipelineGraphApi(run);
51-
JSONObject graph = createGraphJson(api.createGraph());
52-
return HttpResponses.okJSON(graph);
51+
JSONObject tree = createGraphJson(api.createTree());
52+
return HttpResponses.okJSON(tree);
5353
}
5454

5555
protected JSONObject createGraphJson(PipelineGraph pipelineGraph) throws JsonProcessingException {

Diff for: src/main/java/io/jenkins/plugins/pipelinegraphview/utils/AbstractPipelineViewAction.java

-10
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,6 @@ protected JSONObject createJson(PipelineGraph pipelineGraph) throws JsonProcessi
5555
return JSONObject.fromObject(graph);
5656
}
5757

58-
@GET
59-
@WebMethod(name = "graph")
60-
public HttpResponse getGraph() throws JsonProcessingException {
61-
// TODO for automatic json serialisation look at:
62-
// https://github.com/jenkinsci/blueocean-plugin/blob/4f2aa260fca22604a087629dc0da5c80735e0548/blueocean-commons/src/main/java/io/jenkins/blueocean/commons/stapler/Export.java#L101
63-
// https://github.com/jenkinsci/blueocean-plugin/blob/4f2aa260fca22604a087629dc0da5c80735e0548/blueocean-commons/src/main/java/io/jenkins/blueocean/commons/stapler/TreeResponse.java#L48
64-
JSONObject graph = createJson(api.createGraph());
65-
return HttpResponses.okJSON(graph);
66-
}
67-
6858
@WebMethod(name = "tree")
6959
public HttpResponse getTree() throws JsonProcessingException {
7060
// TODO: This need to be updated to return a tree representation of the graph, not the graph.

Diff for: src/main/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineGraphApi.java

-77
Original file line numberDiff line numberDiff line change
@@ -75,83 +75,6 @@ private List<PipelineStageInternal> getPipelineNodes() {
7575
.collect(Collectors.toList());
7676
}
7777

78-
public PipelineGraph createGraph() {
79-
List<PipelineStageInternal> stages = getPipelineNodes();
80-
81-
// id => stage
82-
Map<String, PipelineStageInternal> stageMap =
83-
stages.stream()
84-
.collect(
85-
Collectors.toMap(
86-
PipelineStageInternal::getId, stage -> stage, (u, v) -> u, LinkedHashMap::new));
87-
88-
Map<String, List<String>> stageToChildrenMap = new HashMap<>();
89-
90-
List<String> stagesThatAreNested = new ArrayList<>();
91-
92-
Map<String, String> nextSiblingToOlderSibling = new HashMap<>();
93-
94-
List<String> stagesThatAreChildrenOrNestedStages = new ArrayList<>();
95-
stages.forEach(
96-
stage -> {
97-
if (stage.getParents().isEmpty()) {
98-
stageToChildrenMap.put(stage.getId(), new ArrayList<>());
99-
} else if (stage.getType().equals("PARALLEL")) {
100-
String parentId = stage.getParents().get(0); // assume one parent for now
101-
List<String> childrenOfParent =
102-
stageToChildrenMap.getOrDefault(parentId, new ArrayList<>());
103-
childrenOfParent.add(stage.getId());
104-
stageToChildrenMap.put(parentId, childrenOfParent);
105-
stagesThatAreChildrenOrNestedStages.add(stage.getId());
106-
} else if (stageMap.get(stage.getParents().get(0)).getType().equals("PARALLEL")) {
107-
String parentId = stage.getParents().get(0);
108-
PipelineStageInternal parent = stageMap.get(parentId);
109-
parent.setSeqContainerName(parent.getName());
110-
parent.setName(stage.getName());
111-
parent.setSequential(true);
112-
parent.setType(stage.getType());
113-
parent.setTitle(stage.getTitle());
114-
parent.setCompletePercent(stage.getCompletePercent());
115-
stage.setSequential(true);
116-
117-
nextSiblingToOlderSibling.put(stage.getId(), parentId);
118-
stagesThatAreNested.add(stage.getId());
119-
stagesThatAreChildrenOrNestedStages.add(stage.getId());
120-
// nested stage of nested stage
121-
} else if (stagesThatAreNested.contains(
122-
stageMap.get(stage.getParents().get(0)).getId())) {
123-
PipelineStageInternal parent =
124-
stageMap.get(nextSiblingToOlderSibling.get(stage.getParents().get(0)));
125-
// shouldn't happen but found it after restarting a matrix build
126-
// this breaks the layout badly but prevents a null pointer
127-
if (parent != null) {
128-
stage.setSequential(true);
129-
parent.setNextSibling(stage);
130-
stagesThatAreNested.add(stage.getId());
131-
stagesThatAreChildrenOrNestedStages.add(stage.getId());
132-
}
133-
}
134-
});
135-
136-
List<PipelineStage> stageResults =
137-
stageMap.values().stream()
138-
.map(
139-
pipelineStageInternal -> {
140-
List<PipelineStage> children =
141-
stageToChildrenMap.getOrDefault(pipelineStageInternal.getId(), emptyList())
142-
.stream()
143-
.map(mapper(stageMap, stageToChildrenMap))
144-
.collect(Collectors.toList());
145-
146-
return pipelineStageInternal.toPipelineStage(children);
147-
})
148-
.filter(stage -> !stagesThatAreChildrenOrNestedStages.contains(stage.getId()))
149-
.collect(Collectors.toList());
150-
151-
FlowExecution execution = run.getExecution();
152-
return new PipelineGraph(stageResults, execution != null && execution.isComplete());
153-
}
154-
15578
private Function<String, PipelineStage> mapper(
15679
Map<String, PipelineStageInternal> stageMap, Map<String, List<String>> stageToChildrenMap) {
15780

Diff for: src/main/resources/io/jenkins/plugins/pipelinegraphview/PipelineGraphViewAction/index.jelly

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<?jelly escape-by-default='true'?>
44
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:p="/lib/pipeline-graph-view">
5-
<l:layout title="${%Graph} [${it.buildDisplayName}]" type="one-column">
5+
<l:layout title="${%Details} [${it.buildDisplayName}]" type="one-column">
66
<l:main-panel>
77
<div class="jenkins-app-bar">
88
<div class="jenkins-app-bar__content">

Diff for: src/main/resources/io/jenkins/plugins/pipelinegraphview/multipipelinegraphview/MultiPipelineGraphViewAction/index.jelly

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22

33
<?jelly escape-by-default='true'?>
4-
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
5-
<l:layout title="Stages [${it.jobDisplayName}]" type="one-column">
4+
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:p="/lib/pipeline-graph-view">
5+
<l:layout title="${%Stages} [${it.jobDisplayName}]" type="one-column">
66
<l:main-panel>
77
<div class="jenkins-app-bar">
88
<div class="jenkins-app-bar__content">
@@ -23,8 +23,12 @@
2323
</a>
2424
</div>
2525
</div>
26-
<div id="multiple-pipeline-root"/>
27-
<script src="${rootURL}/plugin/pipeline-graph-view/js/bundles/multi-pipeline-graph-view-bundle.js"/>
26+
<div class="pgv-cards">
27+
<p:card title="${%Stages}" type="wide" expandable="true">
28+
<div id="multiple-pipeline-root"/>
29+
<script src="${rootURL}/plugin/pipeline-graph-view/js/bundles/multi-pipeline-graph-view-bundle.js"/>
30+
</p:card>
31+
</div>
2832
<script src="${rootURL}/plugin/pipeline-graph-view/js/build.js"/>
2933
</l:main-panel>
3034
</l:layout>

Diff for: src/main/resources/lib/pipeline-graph-view/card.jelly

+17-4
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@
1111
The title for the card
1212
</st:attribute>
1313
<st:attribute name="expandable">
14-
Set true if you want to allow users to click a button to pop out a larger version of the card
14+
Set true if you want to allow users to expand the card (by default it opens a modal, see expandableLink to link to
15+
a different page)
16+
</st:attribute>
17+
<st:attribute name="expandableLink">
18+
Set path for the expandable link to go to.
1519
</st:attribute>
1620
</st:documentation>
17-
<div class="pgv-cards__item${attrs.type == 'wide' ? ' pgv-cards__item--wide' : ''}${attrs.expandable == 'true' ? ' pgv-modal' : ''}">
21+
<div
22+
class="pgv-cards__item${attrs.type == 'wide' ? ' pgv-cards__item--wide' : ''}${attrs.expandable == 'true' ? ' pgv-modal' : ''}">
1823
<j:choose>
19-
<j:when test="${attrs.expandable == 'true'}">
24+
<j:when test="${attrs.expandable == 'true' and attrs.expandableLink == null}">
2025
<div class="pgv-modal__content">
2126
<p class="pgv-cards__item__title">${attrs.title}
2227
<a href="#" class="pgv-modal__expander">
@@ -34,7 +39,15 @@
3439
</div>
3540
</j:when>
3641
<j:otherwise>
37-
<p class="pgv-cards__item__title">${attrs.title}</p>
42+
<p class="pgv-cards__item__title">${attrs.title}
43+
<j:if test="${attrs.expandable == 'true' and attrs.expandableLink != null}">
44+
<a href="${attrs.expandableLink}" class="pgv-modal__expander">
45+
<span class="icon-sm">
46+
<l:icon src="symbol-resize-outline plugin-ionicons-api" tooltip="${%Expand} ${attrs.title}"/>
47+
</span>
48+
</a>
49+
</j:if>
50+
</p>
3851
<d:invokeBody/>
3952
</j:otherwise>
4053
</j:choose>

Diff for: src/main/webapp/js/card.js

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
document.addEventListener('DOMContentLoaded', () => {
22
document.querySelectorAll('.pgv-modal__expander')
33
.forEach(expander => {
4-
expander.addEventListener('click', (event) => {
5-
event.preventDefault();
6-
7-
const modal = expander.closest('.pgv-modal')
8-
modal.classList.add('pgv-modal__open');
4+
if (expander.getAttribute('href') === '#') {
5+
expander.addEventListener('click', (event) => {
6+
event.preventDefault();
97

10-
window.addEventListener('keydown', (event) => {
11-
if (event.key === 'Escape') {
12-
modal.classList.remove('pgv-modal__open');
13-
}
14-
}, { once: true });
8+
const modal = expander.closest('.pgv-modal')
9+
modal.classList.add('pgv-modal__open');
1510

16-
})
11+
window.addEventListener('keydown', (event) => {
12+
if (event.key === 'Escape') {
13+
modal.classList.remove('pgv-modal__open');
14+
}
15+
}, { once: true });
16+
})
17+
}
1718
})
1819

1920
document.querySelectorAll('.pgv-modal__closer')

Diff for: src/test/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineGraphApiTest.java

-65
Original file line numberDiff line numberDiff line change
@@ -16,71 +16,6 @@ public class PipelineGraphApiTest {
1616

1717
@Rule public JenkinsRule j = new JenkinsRule();
1818

19-
@Test
20-
public void createGraph_unstableSmokes() throws Exception {
21-
WorkflowRun run =
22-
TestUtils.createAndRunJob(
23-
j, "unstableSmokes", "unstableSmokes.jenkinsfile", Result.FAILURE);
24-
PipelineGraphApi api = new PipelineGraphApi(run);
25-
PipelineGraph graph = api.createGraph();
26-
27-
List<PipelineStage> stages = graph.getStages();
28-
29-
String stagesString =
30-
TestUtils.collectStagesAsString(
31-
stages,
32-
(PipelineStage stage) ->
33-
String.format(
34-
"{%d,%s,%s,%s,%s}",
35-
stage.getCompletePercent(),
36-
stage.getName(),
37-
stage.getTitle(),
38-
stage.getType(),
39-
stage.getState()));
40-
assertThat(
41-
stagesString,
42-
is(
43-
String.join(
44-
"",
45-
"{50,unstable-one,unstable-one,STAGE,UNSTABLE},",
46-
"{50,success,success,STAGE,SUCCESS},",
47-
"{50,unstable-two,unstable-two,STAGE,UNSTABLE},",
48-
"{50,failure,failure,STAGE,FAILURE}")));
49-
}
50-
51-
@Test
52-
public void createGraph_complexSmokes() throws Exception {
53-
WorkflowRun run =
54-
TestUtils.createAndRunJob(j, "complexSmokes", "complexSmokes.jenkinsfile", Result.SUCCESS);
55-
PipelineGraphApi api = new PipelineGraphApi(run);
56-
PipelineGraph graph = api.createGraph();
57-
58-
List<PipelineStage> stages = graph.getStages();
59-
60-
String stagesString =
61-
TestUtils.collectStagesAsString(
62-
stages,
63-
(PipelineStage stage) ->
64-
String.format(
65-
"{%s,%s}",
66-
stage.getName(), Optional.ofNullable(stage.getSeqContainerName()).orElse("-")));
67-
68-
// As this is a graph view, and 'Branch C' doesn't have and steps, it doesn't get added as a
69-
// node.
70-
// Instead, its first child with steps 'Nested 1' gets added as a node, and all other children
71-
// get added as siblings. The 'getSeqContainerName' property of the 'Nested 1' now gets set to
72-
// its parent's display name ('Branch C') so the frontend can add a label.
73-
assertThat(
74-
stagesString,
75-
is(
76-
String.join(
77-
"",
78-
"{Non-Parallel Stage,-},",
79-
"{Parallel Stage,-}[{Branch A,-},{Branch B,-},{Nested 1,Branch C}],",
80-
"{Skipped stage,-},",
81-
"{Parallel Stage 2,-}[{Branch A,-},{Branch B,-},{Nested 1,Branch C}]")));
82-
}
83-
8419
@Test
8520
public void createTree_unstableSmokes() throws Exception {
8621
WorkflowRun run =

0 commit comments

Comments
 (0)