-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathGitHubChecksDetails.java
More file actions
234 lines (211 loc) · 9.35 KB
/
GitHubChecksDetails.java
File metadata and controls
234 lines (211 loc) · 9.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package io.jenkins.plugins.checks.github;
import java.net.URI;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import hudson.EnvVars;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.GHCheckRun.AnnotationLevel;
import org.kohsuke.github.GHCheckRun.Conclusion;
import org.kohsuke.github.GHCheckRun.Status;
import org.kohsuke.github.GHCheckRunBuilder;
import org.kohsuke.github.GHCheckRunBuilder.Action;
import org.kohsuke.github.GHCheckRunBuilder.Annotation;
import org.kohsuke.github.GHCheckRunBuilder.Image;
import org.kohsuke.github.GHCheckRunBuilder.Output;
import io.jenkins.plugins.checks.api.ChecksAction;
import io.jenkins.plugins.checks.api.ChecksAnnotation;
import io.jenkins.plugins.checks.api.ChecksAnnotation.ChecksAnnotationLevel;
import io.jenkins.plugins.checks.api.ChecksConclusion;
import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksImage;
import io.jenkins.plugins.checks.api.ChecksOutput;
import io.jenkins.plugins.checks.api.ChecksStatus;
/**
* An adaptor which adapts the generic checks objects of {@link ChecksDetails} to the specific GitHub checks run
* objects of {@link GHCheckRunBuilder}.
*/
class GitHubChecksDetails {
private final ChecksDetails details;
private final EnvVars envVars;
private static final int MAX_MESSAGE_SIZE_TO_CHECKS_API = 65_535;
/**
* Construct with the given {@link ChecksDetails}.
*
* @param details the details of a generic check run
*/
GitHubChecksDetails(final ChecksDetails details, final EnvVars envVars) {
if (details.getConclusion() == ChecksConclusion.NONE) {
if (details.getStatus() == ChecksStatus.COMPLETED) {
throw new IllegalArgumentException("No conclusion has been set when status is completed.");
}
if (details.getCompletedAt().isPresent()) {
throw new IllegalArgumentException("No conclusion has been set when \"completedAt\" is provided.");
}
}
this.details = details;
this.envVars = envVars;
}
/**
* Returns the name of a GitHub check run.
*
* @return the name of the check
*/
public String getName() {
return envVars.expand(details.getName()
.filter(StringUtils::isNotBlank)
.orElseThrow(() -> new IllegalArgumentException("The check name is blank.")));
}
/**
* Returns the {@link Status} of a GitHub check run.
*
*
* @return the status of a check run
* @throws IllegalArgumentException if the status of the {@code details} is not one of {@link ChecksStatus}
*/
public Status getStatus() {
return switch (details.getStatus()) {
case NONE, QUEUED -> Status.QUEUED;
case IN_PROGRESS -> Status.IN_PROGRESS;
case COMPLETED -> Status.COMPLETED;
};
}
/**
* Returns the URL of site which contains details of a GitHub check run.
*
* @return an URL of the site
*/
public Optional<String> getDetailsURL() {
if (details.getDetailsURL().filter(StringUtils::isBlank).isPresent()) {
return Optional.empty();
}
details.getDetailsURL().ifPresent(url -> {
if (!StringUtils.equalsAny(URI.create(url).getScheme(), "http", "https")) {
throw new IllegalArgumentException("The details url is not http or https scheme: " + url);
}
}
);
return details.getDetailsURL();
}
/**
* Returns the UTC time when the check started.
*
* @return the start time of a check
*/
public Optional<Date> getStartedAt() {
if (details.getStartedAt().isPresent()) {
return Optional.of(Date.from(
details.getStartedAt().get()
.toInstant(ZoneOffset.UTC)));
}
return Optional.empty();
}
/**
* Returns the {@link Conclusion} of a completed GitHub check run.
*
* @return the conclusion of a completed check run
* @throws IllegalArgumentException if the conclusion of the {@code details} is not one of {@link ChecksConclusion}
*/
@SuppressWarnings("PMD.CyclomaticComplexity")
public Optional<Conclusion> getConclusion() {
return switch (details.getConclusion()) {
case SKIPPED ->
Optional.of(Conclusion.SKIPPED); // TODO use CANCELLED if https://github.com/github/feedback/discussions/10255 is fixed
case FAILURE, CANCELED, TIME_OUT -> // TODO TIMED_OUT as above
Optional.of(Conclusion.FAILURE);
case NEUTRAL -> Optional.of(Conclusion.NEUTRAL);
case SUCCESS -> Optional.of(Conclusion.SUCCESS);
case ACTION_REQUIRED -> Optional.of(Conclusion.ACTION_REQUIRED);
case NONE -> Optional.empty();
};
}
/**
* Returns the UTC time when the check completed.
*
* @return the completed time of a check
*/
public Optional<Date> getCompletedAt() {
if (details.getCompletedAt().isPresent()) {
return Optional.of(Date.from(
details.getCompletedAt().get()
.toInstant(ZoneOffset.UTC)));
}
return Optional.empty();
}
/**
* Returns the {@link Output} of a GitHub check run.
*
* @return the output of a check run
*/
public Optional<Output> getOutput() {
if (details.getOutput().isPresent()) {
ChecksOutput checksOutput = details.getOutput().get();
Output output = new Output(
checksOutput.getTitle().orElseThrow(
() -> new IllegalArgumentException("Title of output is required but not provided")),
checksOutput.getSummary(MAX_MESSAGE_SIZE_TO_CHECKS_API).orElseThrow(
() -> new IllegalArgumentException("Summary of output is required but not provided")))
.withText(checksOutput.getText(MAX_MESSAGE_SIZE_TO_CHECKS_API).orElse(null));
checksOutput.getChecksAnnotations().stream().map(this::getAnnotation).forEach(output::add);
checksOutput.getChecksImages().stream().map(this::getImage).forEach(output::add);
return Optional.of(output);
}
return Optional.empty();
}
/**
* Returns the {@link Action} of a GitHub check run.
*
* @return the actions list of a check run.
*/
public List<Action> getActions() {
return details.getActions().stream()
.map(this::getAction)
.collect(Collectors.toList());
}
private Action getAction(final ChecksAction checksAction) {
return new Action(
checksAction.getLabel()
.orElseThrow(() ->
new IllegalArgumentException("Label of action is required but not provided")),
checksAction.getDescription()
.orElseThrow(() ->
new IllegalArgumentException("Description of action is required but not provided")),
checksAction.getIdentifier()
.orElseThrow(() ->
new IllegalArgumentException("Identifier of action is required but not provided")));
}
private Annotation getAnnotation(final ChecksAnnotation checksAnnotation) {
return new Annotation(
checksAnnotation.getPath()
.orElseThrow(() -> new IllegalArgumentException("Path is required but not provided.")),
checksAnnotation.getStartLine()
.orElseThrow(() -> new IllegalArgumentException("Start line is required but not provided.")),
checksAnnotation.getEndLine().
orElseThrow(() -> new IllegalArgumentException("End line is required but not provided.")),
getAnnotationLevel(checksAnnotation.getAnnotationLevel()),
checksAnnotation.getMessage()
.orElseThrow(() -> new IllegalArgumentException("Message is required but not provided.")))
.withTitle(checksAnnotation.getTitle().orElse(null))
.withRawDetails(checksAnnotation.getRawDetails().orElse(null))
.withStartColumn(checksAnnotation.getStartColumn().orElse(null))
.withEndColumn(checksAnnotation.getEndColumn().orElse(null));
}
private Image getImage(final ChecksImage checksImage) {
return new Image(
checksImage.getAlt()
.orElseThrow(() -> new IllegalArgumentException("alt of image is required but not provided.")),
checksImage.getImageUrl()
.orElseThrow(() -> new IllegalArgumentException("url of image is required but not provided.")))
.withCaption(checksImage.getCaption().orElse(null));
}
private AnnotationLevel getAnnotationLevel(final ChecksAnnotationLevel checksLevel) {
return switch (checksLevel) {
case NOTICE -> AnnotationLevel.NOTICE;
case FAILURE -> AnnotationLevel.FAILURE;
case WARNING -> AnnotationLevel.WARNING;
case NONE -> throw new IllegalArgumentException("Annotation level is required but not set.");
};
}
}