2121import org .sourcelab .buildkite .api .client .exception .InvalidAccessTokenException ;
2222import org .sourcelab .buildkite .api .client .exception .InvalidAllowedIpAddressException ;
2323import org .sourcelab .buildkite .api .client .exception .InvalidPagingRequestException ;
24+ import org .sourcelab .buildkite .api .client .exception .InvalidRequestException ;
2425import org .sourcelab .buildkite .api .client .exception .NotFoundException ;
2526import org .sourcelab .buildkite .api .client .http .Client ;
2627import org .sourcelab .buildkite .api .client .http .HttpResult ;
2728import org .sourcelab .buildkite .api .client .request .BuildFilters ;
2829import 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 ;
2934import org .sourcelab .buildkite .api .client .request .DeleteAccessTokenRequest ;
3035import 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 ;
3139import org .sourcelab .buildkite .api .client .request .GetMetaRequest ;
3240import org .sourcelab .buildkite .api .client .request .GetOrganizationRequest ;
3341import org .sourcelab .buildkite .api .client .request .GetPipelineRequest ;
4149import org .sourcelab .buildkite .api .client .request .PageOptions ;
4250import org .sourcelab .buildkite .api .client .request .PageableRequest ;
4351import org .sourcelab .buildkite .api .client .request .PingRequest ;
44- import org .sourcelab .buildkite .api .client .request .PipelineFiltersBuilder ;
4552import 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 ;
4655import org .sourcelab .buildkite .api .client .request .Request ;
4756import org .sourcelab .buildkite .api .client .response .AccessTokenResponse ;
57+ import org .sourcelab .buildkite .api .client .response .Build ;
4858import org .sourcelab .buildkite .api .client .response .CurrentUserResponse ;
4959import org .sourcelab .buildkite .api .client .response .Emoji ;
60+ import org .sourcelab .buildkite .api .client .response .Error ;
5061import org .sourcelab .buildkite .api .client .response .ErrorResponse ;
5162import org .sourcelab .buildkite .api .client .response .ListBuildsResponse ;
5263import org .sourcelab .buildkite .api .client .response .ListOrganizationsResponse ;
6071import org .sourcelab .buildkite .api .client .response .parser .ErrorResponseParser ;
6172
6273import java .io .IOException ;
74+ import java .util .Collections ;
6375import java .util .List ;
6476import java .util .Objects ;
6577import 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
0 commit comments