Skip to content

Commit aeb316a

Browse files
authored
Introduce a State enum for the Pipeline/Stage representations (#636)
1 parent 8e357c4 commit aeb316a

File tree

12 files changed

+160
-64
lines changed

12 files changed

+160
-64
lines changed

src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsoleModel.tsx

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { Result } from "../../../pipeline-graph-view/pipeline-graph/main/PipelineGraphModel";
22

33
// re-export Result so the relative path exists in one location.
4-
export {
5-
Result,
6-
decodeResultValue,
7-
} from "../../../pipeline-graph-view/pipeline-graph/main/PipelineGraphModel";
4+
export { Result } from "../../../pipeline-graph-view/pipeline-graph/main/PipelineGraphModel";
85

96
export type {
107
StageInfo,

src/main/frontend/pipeline-graph-view/pipeline-graph/main/PipelineGraphModel.tsx

+1-12
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,7 @@ export enum Result {
88
aborted = "aborted",
99
not_built = "not_built", // May be pending, or job was ended before this point
1010
skipped = "skipped", // excluded via pipeline "when" clause
11-
unknown = "unknown", // bad data or client code needs updating for new values
12-
}
13-
14-
export function decodeResultValue(resultMaybe: any): Result {
15-
const lcase = String(resultMaybe).toLowerCase();
16-
17-
// TODO: validate this
18-
if ((Object.values(Result) as any).includes(lcase)) {
19-
return lcase as Result;
20-
}
21-
22-
return Result.unknown;
11+
unknown = "unknown", // bad data
2312
}
2413

2514
// Dimensions used for layout, px

src/main/frontend/pipeline-graph-view/pipeline-graph/main/support/nodes.tsx

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from "react";
2+
import { CSSProperties } from "react";
23

34
import {
4-
decodeResultValue,
55
LayoutInfo,
66
NodeColumn,
77
NodeInfo,
@@ -10,7 +10,6 @@ import {
1010
import StatusIcon, {
1111
resultToColor,
1212
} from "../../../../common/components/status-icon";
13-
import { CSSProperties } from "react";
1413

1514
type SVGChildren = Array<any>; // Fixme: Maybe refine this? Not sure what should go here, we have working code I can't make typecheck
1615

@@ -40,8 +39,6 @@ export function Node({ node }: NodeProps) {
4039
}
4140

4241
const { title, state, url } = node.stage ?? {};
43-
const resultClean = decodeResultValue(state);
44-
4542
groupChildren.push(
4643
<StatusIcon
4744
key={`icon-${node.id}`}
@@ -67,7 +64,7 @@ export function Node({ node }: NodeProps) {
6764
} as CSSProperties,
6865
className:
6966
"PWGx-pipeline-node PWGx-pipeline-node--" +
70-
resultClean +
67+
state +
7168
" " +
7269
resultToColor(node.stage.state, node.stage.skeleton),
7370
};

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/AbstractPipelineNode.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package io.jenkins.plugins.pipelinegraphview.utils;
22

3-
import java.util.Locale;
43
import org.jenkinsci.plugins.workflow.pipelinegraphanalysis.TimingInfo;
54

65
public class AbstractPipelineNode {
76
private String name;
8-
private String state; // TODO enum
7+
private PipelineState state;
98
private String type; // TODO enum
109
private String title;
1110
private String id;
@@ -14,10 +13,10 @@ public class AbstractPipelineNode {
1413
private TimingInfo timingInfo;
1514

1615
public AbstractPipelineNode(
17-
String id, String name, String state, String type, String title, TimingInfo timingInfo) {
16+
String id, String name, PipelineState state, String type, String title, TimingInfo timingInfo) {
1817
this.id = id;
1918
this.name = name;
20-
this.state = state.toLowerCase(Locale.ROOT);
19+
this.state = state;
2120
this.type = type;
2221
this.title = title;
2322
this.timingInfo = timingInfo;
@@ -46,7 +45,7 @@ public String getName() {
4645
return name;
4746
}
4847

49-
public String getState() {
48+
public PipelineState getState() {
5049
return state;
5150
}
5251

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineGraphApi.java

+13-20
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,19 @@ public Integer replay() throws ExecutionException, InterruptedException, Timeout
5050

5151
private List<PipelineStageInternal> getPipelineNodes(PipelineGraphBuilderApi builder) {
5252
return builder.getPipelineNodes().stream()
53-
.map(flowNodeWrapper -> {
54-
String state =
55-
flowNodeWrapper.getStatus().getResult().name().toLowerCase(Locale.ROOT);
56-
if (flowNodeWrapper.getStatus().getState() != BlueRun.BlueRunState.FINISHED) {
57-
state = flowNodeWrapper.getStatus().getState().name().toLowerCase(Locale.ROOT);
58-
}
59-
return new PipelineStageInternal(
60-
flowNodeWrapper.getId(), // TODO no need to parse it BO returns a string even though the
61-
// datatype is number on the frontend
62-
flowNodeWrapper.getDisplayName(),
63-
flowNodeWrapper.getParents().stream()
64-
.map(FlowNodeWrapper::getId)
65-
.collect(Collectors.toList()),
66-
state,
67-
flowNodeWrapper.getType().name(),
68-
flowNodeWrapper.getDisplayName(), // TODO blue ocean uses timing information: "Passed in 0s"
69-
flowNodeWrapper.isSynthetic(),
70-
flowNodeWrapper.getTiming(),
71-
getStageNode(flowNodeWrapper));
72-
})
53+
.map(flowNodeWrapper -> new PipelineStageInternal(
54+
flowNodeWrapper.getId(), // TODO no need to parse it BO returns a string even though the
55+
// datatype is number on the frontend
56+
flowNodeWrapper.getDisplayName(),
57+
flowNodeWrapper.getParents().stream()
58+
.map(FlowNodeWrapper::getId)
59+
.collect(Collectors.toList()),
60+
PipelineState.of(flowNodeWrapper.getStatus()),
61+
flowNodeWrapper.getType().name(),
62+
flowNodeWrapper.getDisplayName(), // TODO blue ocean uses timing information: "Passed in 0s"
63+
flowNodeWrapper.isSynthetic(),
64+
flowNodeWrapper.getTiming(),
65+
getStageNode(flowNodeWrapper)))
7366
.collect(Collectors.toList());
7467
}
7568

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineStage.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public PipelineStage(
1717
String id,
1818
String name,
1919
List<PipelineStage> children,
20-
String state,
20+
PipelineState state,
2121
String type,
2222
String title,
2323
String seqContainerName,

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineStageInternal.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import java.util.Collections;
44
import java.util.List;
5-
import java.util.Locale;
65
import org.jenkinsci.plugins.workflow.pipelinegraphanalysis.TimingInfo;
76

8-
public class PipelineStageInternal {
7+
class PipelineStageInternal {
98

109
private String name;
1110
private List<String> parents;
12-
private String state; // TODO enum
11+
private PipelineState state;
1312
private String type; // TODO enum
1413
private String title;
1514
private String id;
@@ -24,7 +23,7 @@ public PipelineStageInternal(
2423
String id,
2524
String name,
2625
List<String> parents,
27-
String state,
26+
PipelineState state,
2827
String type,
2928
String title,
3029
boolean synthetic,
@@ -33,7 +32,7 @@ public PipelineStageInternal(
3332
this.id = id;
3433
this.name = name;
3534
this.parents = parents;
36-
this.state = state.toLowerCase(Locale.ROOT);
35+
this.state = state;
3736
this.type = type;
3837
this.title = title;
3938
this.synthetic = synthetic;
@@ -53,7 +52,7 @@ public void setSequential(boolean sequential) {
5352
this.sequential = sequential;
5453
}
5554

56-
public void setState(String state) {
55+
public void setState(PipelineState state) {
5756
this.state = state;
5857
}
5958

@@ -93,7 +92,7 @@ public List<String> getParents() {
9392
return parents;
9493
}
9594

96-
public String getState() {
95+
public PipelineState getState() {
9796
return state;
9897
}
9998

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.jenkins.plugins.pipelinegraphview.utils;
2+
3+
import com.fasterxml.jackson.annotation.JsonValue;
4+
import java.util.Locale;
5+
6+
public enum PipelineState {
7+
// BlueRunState
8+
QUEUED,
9+
RUNNING,
10+
PAUSED,
11+
SKIPPED,
12+
NOT_BUILT,
13+
FINISHED,
14+
// BlueRunResult
15+
SUCCESS,
16+
UNSTABLE,
17+
FAILURE,
18+
UNKNOWN,
19+
ABORTED;
20+
21+
public static PipelineState of(NodeRunStatus status) {
22+
if (status.getState() == BlueRun.BlueRunState.FINISHED) {
23+
return switch (status.getResult()) {
24+
case SUCCESS -> SUCCESS;
25+
case UNSTABLE -> UNSTABLE;
26+
case FAILURE -> FAILURE;
27+
case NOT_BUILT -> NOT_BUILT;
28+
case UNKNOWN -> UNKNOWN;
29+
case ABORTED -> ABORTED;
30+
};
31+
}
32+
return switch (status.getState()) {
33+
case QUEUED -> QUEUED;
34+
case RUNNING -> RUNNING;
35+
case PAUSED -> PAUSED;
36+
case SKIPPED -> SKIPPED;
37+
case NOT_BUILT -> NOT_BUILT;
38+
case FINISHED -> FINISHED; // not reached but required for compiler
39+
};
40+
}
41+
42+
@JsonValue
43+
@Override
44+
public String toString() {
45+
return name().toLowerCase(Locale.ROOT);
46+
}
47+
}

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineStep.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ public class PipelineStep extends AbstractPipelineNode {
66
private String stageId;
77

88
public PipelineStep(
9-
String id, String name, String state, String type, String title, String stageId, TimingInfo timingInfo) {
9+
String id,
10+
String name,
11+
PipelineState state,
12+
String type,
13+
String title,
14+
String stageId,
15+
TimingInfo timingInfo) {
1016
super(id, name, state, type, title, timingInfo);
1117
this.stageId = stageId;
1218
}

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/PipelineStepApi.java

+1-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import io.jenkins.plugins.pipelinegraphview.treescanner.PipelineNodeGraphAdapter;
44
import io.jenkins.plugins.pipelinegraphview.utils.legacy.PipelineStepVisitor;
55
import java.util.List;
6-
import java.util.Locale;
76
import java.util.Map;
87
import java.util.stream.Collectors;
98
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
@@ -24,12 +23,6 @@ private List<PipelineStep> parseSteps(List<FlowNodeWrapper> stepNodes, String st
2423
}
2524
List<PipelineStep> steps = stepNodes.stream()
2625
.map(flowNodeWrapper -> {
27-
String state =
28-
flowNodeWrapper.getStatus().getResult().name().toLowerCase(Locale.ROOT);
29-
if (flowNodeWrapper.getStatus().getState() != BlueRun.BlueRunState.FINISHED) {
30-
state = flowNodeWrapper.getStatus().getState().name().toLowerCase(Locale.ROOT);
31-
}
32-
3326
String displayName = flowNodeWrapper.getDisplayName();
3427
String title = "";
3528
if (flowNodeWrapper.getType() == FlowNodeWrapper.NodeType.UNHANDLED_EXCEPTION) {
@@ -55,7 +48,7 @@ private List<PipelineStep> parseSteps(List<FlowNodeWrapper> stepNodes, String st
5548
return new PipelineStep(
5649
flowNodeWrapper.getId(),
5750
displayName,
58-
state,
51+
PipelineState.of(flowNodeWrapper.getStatus()),
5952
flowNodeWrapper.getType().name(),
6053
title, // TODO blue ocean uses timing information: "Passed in
6154
// 0s"

src/main/java/io/jenkins/plugins/pipelinegraphview/utils/TreeBuilder.java

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.jenkins.plugins.pipelinegraphview.utils;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import java.util.stream.Stream;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.Arguments;
10+
import org.junit.jupiter.params.provider.CsvSource;
11+
import org.junit.jupiter.params.provider.MethodSource;
12+
13+
class PipelineStateTest {
14+
15+
@ParameterizedTest
16+
@MethodSource("whenFinished")
17+
void of_usesResultWhenStateIsFinished(BlueRun.BlueRunResult result, PipelineState expected) {
18+
NodeRunStatus runStatus = new NodeRunStatus(result, BlueRun.BlueRunState.FINISHED);
19+
20+
PipelineState status = PipelineState.of(runStatus);
21+
22+
assertEquals(expected, status);
23+
}
24+
25+
private static Stream<Arguments> whenFinished() {
26+
return Stream.of(
27+
Arguments.arguments(BlueRun.BlueRunResult.SUCCESS, PipelineState.SUCCESS),
28+
Arguments.arguments(BlueRun.BlueRunResult.UNSTABLE, PipelineState.UNSTABLE),
29+
Arguments.arguments(BlueRun.BlueRunResult.FAILURE, PipelineState.FAILURE),
30+
Arguments.arguments(BlueRun.BlueRunResult.NOT_BUILT, PipelineState.NOT_BUILT),
31+
Arguments.arguments(BlueRun.BlueRunResult.UNKNOWN, PipelineState.UNKNOWN),
32+
Arguments.arguments(BlueRun.BlueRunResult.ABORTED, PipelineState.ABORTED));
33+
}
34+
35+
@ParameterizedTest
36+
@MethodSource("whenNotFinished")
37+
void of_usesStateWhenStateIsNotFinished(BlueRun.BlueRunState state, PipelineState expected) {
38+
NodeRunStatus runStatus = new NodeRunStatus(null, state);
39+
40+
PipelineState status = PipelineState.of(runStatus);
41+
42+
assertEquals(expected, status);
43+
}
44+
45+
private static Stream<Arguments> whenNotFinished() {
46+
return Stream.of(
47+
Arguments.arguments(BlueRun.BlueRunState.QUEUED, PipelineState.QUEUED),
48+
Arguments.arguments(BlueRun.BlueRunState.RUNNING, PipelineState.RUNNING),
49+
Arguments.arguments(BlueRun.BlueRunState.PAUSED, PipelineState.PAUSED),
50+
Arguments.arguments(BlueRun.BlueRunState.SKIPPED, PipelineState.SKIPPED),
51+
Arguments.arguments(BlueRun.BlueRunState.NOT_BUILT, PipelineState.NOT_BUILT));
52+
}
53+
54+
private static final ObjectMapper MAPPER = new ObjectMapper();
55+
56+
@ParameterizedTest
57+
@CsvSource({
58+
"QUEUED, queued",
59+
"RUNNING, running",
60+
"PAUSED, paused",
61+
"SKIPPED, skipped",
62+
"NOT_BUILT, not_built",
63+
"FINISHED, finished",
64+
"SUCCESS, success",
65+
"UNSTABLE, unstable",
66+
"FAILURE, failure",
67+
"UNKNOWN, unknown",
68+
"ABORTED, aborted"
69+
})
70+
void serialization(String input, String expected) throws JsonProcessingException {
71+
PipelineState status = PipelineState.valueOf(input);
72+
73+
String serialized = MAPPER.writeValueAsString(status);
74+
75+
assertEquals("\"%s\"".formatted(expected), serialized);
76+
}
77+
}

0 commit comments

Comments
 (0)