Skip to content

Commit d82cdab

Browse files
committed
Add additional end points
1 parent 29407dc commit d82cdab

38 files changed

+1451
-108
lines changed

src/main/java/org/sourcelab/buildkite/api/client/BuildkiteClient.java

Lines changed: 125 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,21 @@
2121
import org.sourcelab.buildkite.api.client.exception.InvalidAccessTokenException;
2222
import org.sourcelab.buildkite.api.client.exception.InvalidAllowedIpAddressException;
2323
import org.sourcelab.buildkite.api.client.exception.InvalidPagingRequestException;
24+
import org.sourcelab.buildkite.api.client.exception.InvalidRequestException;
2425
import org.sourcelab.buildkite.api.client.exception.NotFoundException;
2526
import org.sourcelab.buildkite.api.client.http.Client;
2627
import org.sourcelab.buildkite.api.client.http.HttpResult;
2728
import org.sourcelab.buildkite.api.client.request.BuildFilters;
2829
import org.sourcelab.buildkite.api.client.request.BuildFiltersBuilder;
30+
import org.sourcelab.buildkite.api.client.request.CancelBuildRequest;
31+
import org.sourcelab.buildkite.api.client.request.CreateBuildOptions;
32+
import org.sourcelab.buildkite.api.client.request.CreateBuildOptionsBuilder;
33+
import org.sourcelab.buildkite.api.client.request.CreateBuildRequest;
2934
import org.sourcelab.buildkite.api.client.request.DeleteAccessTokenRequest;
3035
import org.sourcelab.buildkite.api.client.request.GetAccessTokenRequest;
36+
import org.sourcelab.buildkite.api.client.request.GetBuildFilters;
37+
import org.sourcelab.buildkite.api.client.request.GetBuildFiltersBuilder;
38+
import org.sourcelab.buildkite.api.client.request.GetBuildRequest;
3139
import org.sourcelab.buildkite.api.client.request.GetMetaRequest;
3240
import org.sourcelab.buildkite.api.client.request.GetOrganizationRequest;
3341
import org.sourcelab.buildkite.api.client.request.GetPipelineRequest;
@@ -41,12 +49,15 @@
4149
import org.sourcelab.buildkite.api.client.request.PageOptions;
4250
import org.sourcelab.buildkite.api.client.request.PageableRequest;
4351
import org.sourcelab.buildkite.api.client.request.PingRequest;
44-
import org.sourcelab.buildkite.api.client.request.PipelineFiltersBuilder;
4552
import org.sourcelab.buildkite.api.client.request.PipelineFilters;
53+
import org.sourcelab.buildkite.api.client.request.PipelineFiltersBuilder;
54+
import org.sourcelab.buildkite.api.client.request.RebuildBuildRequest;
4655
import org.sourcelab.buildkite.api.client.request.Request;
4756
import org.sourcelab.buildkite.api.client.response.AccessTokenResponse;
57+
import org.sourcelab.buildkite.api.client.response.Build;
4858
import org.sourcelab.buildkite.api.client.response.CurrentUserResponse;
4959
import org.sourcelab.buildkite.api.client.response.Emoji;
60+
import org.sourcelab.buildkite.api.client.response.Error;
5061
import org.sourcelab.buildkite.api.client.response.ErrorResponse;
5162
import org.sourcelab.buildkite.api.client.response.ListBuildsResponse;
5263
import org.sourcelab.buildkite.api.client.response.ListOrganizationsResponse;
@@ -60,9 +71,11 @@
6071
import org.sourcelab.buildkite.api.client.response.parser.ErrorResponseParser;
6172

6273
import java.io.IOException;
74+
import java.util.Collections;
6375
import java.util.List;
6476
import java.util.Objects;
6577
import java.util.Optional;
78+
import java.util.stream.Collectors;
6679

6780
/**
6881
* API Client for Buildkite's REST Api.
@@ -296,6 +309,101 @@ public ListBuildsResponse listBuilds(final BuildFilters filters) {
296309
return executeRequest(new ListBuildsRequest(filters));
297310
}
298311

312+
/**
313+
* Retrieve a specific build based on the filter criteria.
314+
*
315+
* @param filters Filter criteria.
316+
* @return The build which matches the criteria if found.
317+
*
318+
* @throws BuildkiteException if API returns an error response.
319+
*/
320+
public Optional<Build> getBuild(final GetBuildFiltersBuilder filters) {
321+
return getBuild(filters.build());
322+
}
323+
324+
/**
325+
* Retrieve a specific build based on the filter criteria.
326+
*
327+
* @param organizationSlugId Organization associated with the build.
328+
* @param pipelineSlugId Pipeline associated with the build.
329+
* @param buildNumber The build number.
330+
* @return The build which matches the criteria if found.
331+
*
332+
* @throws BuildkiteException if API returns an error response.
333+
*/
334+
public Optional<Build> getBuild(final String organizationSlugId, final String pipelineSlugId, final long buildNumber) {
335+
return getBuild(GetBuildFilters.newBuilder()
336+
.withOrgIdSlug(organizationSlugId)
337+
.withPipelineIdSlug(pipelineSlugId)
338+
.withBuildNumber(buildNumber)
339+
);
340+
}
341+
342+
/**
343+
* Retrieve a specific build based on the filter criteria.
344+
*
345+
* @param filters Filter criteria.
346+
* @return The build which matches the criteria if found.
347+
*
348+
* @throws BuildkiteException if API returns an error response.
349+
*/
350+
public Optional<Build> getBuild(final GetBuildFilters filters) {
351+
final Build build = executeRequest(new GetBuildRequest(filters));
352+
return Optional.ofNullable(build);
353+
}
354+
355+
/**
356+
* Cancels the build if its state is either scheduled or running.
357+
*
358+
* @param organizationSlugId Organization associated with the build.
359+
* @param pipelineSlugId Pipeline associated with the build.
360+
* @param buildNumber The build number.
361+
* @return Updated Build instance.
362+
*
363+
* @throws BuildkiteException if API returns an error response.
364+
*/
365+
public Build cancelBuild(final String organizationSlugId, final String pipelineSlugId, final long buildNumber) {
366+
return executeRequest(new CancelBuildRequest(organizationSlugId, pipelineSlugId, buildNumber));
367+
}
368+
369+
/**
370+
* Creates a new build to be executed.
371+
*
372+
* @param createBuildOptions Defines the build to be created.
373+
* @return Created Build instance.
374+
*
375+
* @throws BuildkiteException if API returns an error response.
376+
*/
377+
public Build createBuild(final CreateBuildOptionsBuilder createBuildOptions) {
378+
return createBuild(createBuildOptions.build());
379+
}
380+
381+
/**
382+
* Creates a new build to be executed.
383+
*
384+
* @param createBuildOptions Defines the build to be created.
385+
* @return Created Build instance.
386+
*
387+
* @throws BuildkiteException if API returns an error response.
388+
*/
389+
public Build createBuild(final CreateBuildOptions createBuildOptions) {
390+
return executeRequest(new CreateBuildRequest(createBuildOptions));
391+
}
392+
393+
/**
394+
* Retries a build.
395+
*
396+
* @param organizationSlugId Organization associated with the build.
397+
* @param pipelineSlugId Pipeline associated with the build.
398+
* @param buildNumber The build number.
399+
* @return Updated Build instance.
400+
*
401+
* @throws BuildkiteException if API returns an error response.
402+
*/
403+
public Build rebuildBuild(final String organizationSlugId, final String pipelineSlugId, final long buildNumber) {
404+
return executeRequest(new RebuildBuildRequest(organizationSlugId, pipelineSlugId, buildNumber));
405+
}
406+
299407
/**
300408
* Retrieve the next page of results from the previously retrieved request.
301409
*
@@ -444,7 +552,7 @@ public <T> T executeRequest(final Request<T> request) throws BuildkiteException
444552
final HttpResult result = httpClient.executeRequest(request);
445553

446554
// Handle Errors based on HttpCode.
447-
if (result.getStatus() != 200 && result.getStatus() != 204) {
555+
if (result.getStatus() != 200 && result.getStatus() != 201 && result.getStatus() != 204) {
448556
handleError(result);
449557
}
450558

@@ -460,9 +568,11 @@ public <T> T executeRequest(final Request<T> request) throws BuildkiteException
460568
private void handleError(final HttpResult errorResult) throws BuildkiteException {
461569
// Attempt to parse error response.
462570
String errorMessage = null;
571+
List<Error> errors = Collections.emptyList();
463572
try {
464573
final ErrorResponse errorResponse = new ErrorResponseParser().parseResponse(errorResult);
465574
errorMessage = errorResponse.getMessage();
575+
errors = errorResponse.getErrors();
466576
} catch (final IOException e) {
467577
errorMessage = errorResult.getContent();
468578
}
@@ -478,18 +588,24 @@ private void handleError(final HttpResult errorResult) throws BuildkiteException
478588
case 403:
479589
throw new InvalidAllowedIpAddressException(
480590
errorMessage == null
481-
?
482-
"API requested from an IP address not specifically allowed by your AccessToken. "
483-
+ "Check the 'Allowed IP Addresses' field on your Access Token"
484-
: errorMessage
591+
?
592+
"API requested from an IP address not specifically allowed by your AccessToken. "
593+
+ "Check the 'Allowed IP Addresses' field on your Access Token"
594+
: errorMessage
485595
);
486596
case 404:
487597
throw new NotFoundException(
488598
errorMessage == null
489-
?
490-
"The URL or Resource Request could not be found"
491-
: errorMessage
599+
?
600+
"The URL or Resource Request could not be found"
601+
: errorMessage
492602
);
603+
case 422:
604+
String validationErrorMessage = (( errorMessage != null) ? errorMessage : "The submitted request was invalid. ");
605+
validationErrorMessage += "\n" + errors.stream()
606+
.map((error) -> error.getField() + ": " + error.getCode())
607+
.collect(Collectors.joining("\n"));
608+
throw new InvalidRequestException(validationErrorMessage, errors);
493609
default:
494610
throw new BuildkiteException(
495611
errorMessage == null ? "Unknown/Unhandled Error HttpCode: " + errorResult.getStatus() : errorMessage

src/main/java/org/sourcelab/buildkite/api/client/ConfigurationBuilder.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.sourcelab.buildkite.api.client;
1919

20+
import org.sourcelab.buildkite.api.client.exception.BuilderValidationException;
2021
import org.sourcelab.buildkite.api.client.http.ClientFactory;
2122
import org.sourcelab.buildkite.api.client.http.DefaultClientFactory;
2223

@@ -56,17 +57,17 @@ public ConfigurationBuilder withClientFactory(final ClientFactory clientFactory)
5657

5758
/**
5859
* Validates that the supplied values are correct.
59-
* @throws IllegalStateException if improper values defined.
60+
* @throws BuilderValidationException if not valid or complete.
6061
*/
6162
private void validate() {
6263
if (apiToken == null || apiToken.trim().isEmpty()) {
63-
throw new IllegalStateException("The 'ApiToken' property must be configured.");
64+
throw new BuilderValidationException("The 'ApiToken' property must be configured.");
6465
}
6566
if (clientFactory == null) {
66-
throw new IllegalStateException("The 'ClientFactory' property must be configured.");
67+
throw new BuilderValidationException("The 'ClientFactory' property must be configured.");
6768
}
6869
if (apiUrl == null || apiUrl.trim().isEmpty()) {
69-
throw new IllegalStateException("The 'ApiUrl' property must be configured.");
70+
throw new BuilderValidationException("The 'ApiUrl' property must be configured.");
7071
}
7172
}
7273

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Copyright 2023 SourceLab.org https://github.com/SourceLabOrg/Buildkite-Api-Client
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7+
* persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package org.sourcelab.buildkite.api.client.exception;
19+
20+
/**
21+
* Thrown if a builder fails validation or is not complete.
22+
*/
23+
public class BuilderValidationException extends BuildkiteException {
24+
/**
25+
* Constructor.
26+
* @param message Error message.
27+
*/
28+
public BuilderValidationException(final String message) {
29+
super(message);
30+
}
31+
32+
/**
33+
* Constructor.
34+
* @param message Error message.
35+
* @param cause Underlying exception.
36+
*/
37+
public BuilderValidationException(final String message, final Throwable cause) {
38+
super(message, cause);
39+
}
40+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Copyright 2023 SourceLab.org https://github.com/SourceLabOrg/Buildkite-Api-Client
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7+
* persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package org.sourcelab.buildkite.api.client.exception;
19+
20+
import org.sourcelab.buildkite.api.client.response.Error;
21+
22+
import java.util.ArrayList;
23+
import java.util.Collections;
24+
import java.util.List;
25+
26+
/**
27+
* Thrown if request submitted to API comes back as invalid.
28+
*/
29+
public class InvalidRequestException extends BuildkiteException {
30+
private final List<Error> errors;
31+
32+
/**
33+
* Constructor.
34+
* @param message Error message.
35+
*/
36+
public InvalidRequestException(final String message) {
37+
this(message, Collections.emptyList());
38+
}
39+
40+
/**
41+
* Constructor.
42+
* @param message Error message.
43+
* @param cause Underlying error cause.
44+
*/
45+
public InvalidRequestException(final String message, final Exception cause) {
46+
super(message, cause);
47+
errors = Collections.emptyList();
48+
}
49+
50+
/**
51+
* Constructor.
52+
* @param message Error message.
53+
* @param errors Underlying errors, if any.
54+
*/
55+
public InvalidRequestException(final String message, final List<Error> errors) {
56+
super(message);
57+
this.errors = Collections.unmodifiableList(new ArrayList<>(errors));
58+
}
59+
60+
/**
61+
* Get errors associated with the request.
62+
* @return Errors associated with the request.
63+
*/
64+
public List<Error> getErrors() {
65+
return errors;
66+
}
67+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright 2023 SourceLab.org https://github.com/SourceLabOrg/Buildkite-Api-Client
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7+
* persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package org.sourcelab.buildkite.api.client.exception;
19+
20+
/**
21+
* Thrown if the library is unable to serialize a request body.
22+
* This likely results from a bug in the Buildkite Api Client library, with it not understanding
23+
* how to properly parse a given response.
24+
*/
25+
public class RequestParsingException extends BuildkiteException {
26+
/**
27+
* Constructor.
28+
* @param message Error message.
29+
*/
30+
public RequestParsingException(final String message) {
31+
super(message);
32+
}
33+
34+
/**
35+
* Constructor.
36+
* @param message Error message.
37+
* @param cause Underlying exception.
38+
*/
39+
public RequestParsingException(final String message, final Throwable cause) {
40+
super(message, cause);
41+
}
42+
}

0 commit comments

Comments
 (0)