
org.sourcelab.github.client.GithubClient Maven / Gradle / Ivy
The newest version!
package org.sourcelab.github.client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sourcelab.github.client.exception.BuilderValidationException;
import org.sourcelab.github.client.exception.GithubException;
import org.sourcelab.github.client.exception.InvalidAccessTokenException;
import org.sourcelab.github.client.exception.InvalidPagingRequestException;
import org.sourcelab.github.client.exception.InvalidPermissionsException;
import org.sourcelab.github.client.exception.InvalidRequestException;
import org.sourcelab.github.client.exception.NotFoundException;
import org.sourcelab.github.client.http.Client;
import org.sourcelab.github.client.http.HttpResult;
import org.sourcelab.github.client.objects.AuthenticatedUser;
import org.sourcelab.github.client.request.CancelWorkflowOptions;
import org.sourcelab.github.client.request.CancelWorkflowRequest;
import org.sourcelab.github.client.request.GetAuthenticatedUserRequest;
import org.sourcelab.github.client.request.GetWorkflowJobsForWorkflowRunRequest;
import org.sourcelab.github.client.request.GetWorkflowRunsForWorkflowRequest;
import org.sourcelab.github.client.request.GetWorkflowsForRepositoryRequest;
import org.sourcelab.github.client.request.OctoCatRequest;
import org.sourcelab.github.client.request.PageOptions;
import org.sourcelab.github.client.request.PageableRequest;
import org.sourcelab.github.client.request.Request;
import org.sourcelab.github.client.request.RerunJobFromWorkflowOptions;
import org.sourcelab.github.client.request.RerunJobFromWorkflowRequest;
import org.sourcelab.github.client.request.RerunJobsFromWorkflowOptions;
import org.sourcelab.github.client.request.RerunWorkflowOptions;
import org.sourcelab.github.client.request.RerunWorkflowRequest;
import org.sourcelab.github.client.request.UserReposFilterCriteria;
import org.sourcelab.github.client.request.UserReposRequest;
import org.sourcelab.github.client.request.WorkflowFilterCriteria;
import org.sourcelab.github.client.request.WorkflowJobFilterCriteria;
import org.sourcelab.github.client.request.WorkflowRunFilterCriteria;
import org.sourcelab.github.client.response.Error;
import org.sourcelab.github.client.response.ErrorResponse;
import org.sourcelab.github.client.response.PageableResponse;
import org.sourcelab.github.client.response.PagingLinks;
import org.sourcelab.github.client.response.RerunMultipleJobsResponse;
import org.sourcelab.github.client.response.UserReposResponse;
import org.sourcelab.github.client.response.WorkflowJobsResponse;
import org.sourcelab.github.client.response.WorkflowRunsResponse;
import org.sourcelab.github.client.response.WorkflowsForRepositoryResponse;
import org.sourcelab.github.client.response.parser.ErrorResponseParser;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* API Client for Buildkite's REST Api.
*
* See API Documentation: {@see https://buildkite.com/docs/apis/rest-api}
*/
public class GithubClient {
private static final Logger logger = LoggerFactory.getLogger(GithubClient.class);
/**
* User provided configuration.
*/
private final Configuration configuration;
/**
* Underlying HTTP org.sourcelab.github.client.
*/
private final Client httpClient;
/**
* Constructor.
* @param configuration The configuration for the org.sourcelab.github.client.
*/
public GithubClient(final Configuration configuration) {
this.configuration = configuration;
this.httpClient = configuration.getClientFactory().createClient(configuration);
}
/**
* Make a 'test' or 'hello world' request to the Github API. Can be used to validate
* connectivity to the API.
*
* @return Response details from the ping request.
* @throws GithubException if API returns an error response.
*/
public String octoCat() throws GithubException {
return executeRequest(new OctoCatRequest());
}
/**
* Get the currently authenticated user.
* @return Current authenticated user.
* @throws GithubException if API returns an error response.
*/
public AuthenticatedUser getAuthenticatedUser() throws GithubException {
return executeRequest(new GetAuthenticatedUserRequest());
}
/**
* Get current authenticated user's repositories.
* @param options Filter criteria.
* @return Response object.
* @throws GithubException if API returns an error response.
*/
public UserReposResponse userRepos(final UserReposFilterCriteria options) throws GithubException {
return executeRequest(new UserReposRequest(options));
}
/**
* Get workflows for a given repository.
* @param options Filter criteria.
* @return Response object.
* @throws GithubException if API returns an error response.
*/
public WorkflowsForRepositoryResponse getWorkflowsForRepository(final WorkflowFilterCriteria options) throws GithubException {
return executeRequest(new GetWorkflowsForRepositoryRequest(options));
}
/**
* Get runs for a given workflow..
* @param options Filter criteria.
* @return Response object.
* @throws GithubException if API returns an error response.
*/
public WorkflowRunsResponse getRunsForWorkflow(final WorkflowRunFilterCriteria options) throws GithubException {
if (options.getWorkflowId() == null) {
throw new BuilderValidationException("WorkflowId property is required!");
}
return executeRequest(new GetWorkflowRunsForWorkflowRequest(options));
}
/**
* Get runs for a given repository.
* @param options Filter criteria.
* @return Response object.
* @throws GithubException if API returns an error response.
*/
public WorkflowRunsResponse getWorkflowRunsForRepository(final WorkflowRunFilterCriteria options) throws GithubException {
return executeRequest(new GetWorkflowRunsForWorkflowRequest(options));
}
/**
* Re-run a Workflow.
* @param options Defines which workflow to re-run.
* @return true on success, false on error.
* @throws GithubException if API returns an error response.
*/
public boolean rerunWorkflow(final RerunWorkflowOptions options) throws GithubException {
return executeRequest(new RerunWorkflowRequest(options));
}
/**
* Cancel a Workflow.
* @param options Defines which workflow to cancel
* @return true on success, false on error.
* @throws GithubException if API returns an error response.
*/
public boolean cancelWorkflow(final CancelWorkflowOptions options) throws GithubException {
return executeRequest(new CancelWorkflowRequest(options));
}
/**
* Get runs for a given workflow..
* @param options Filter criteria.
* @return Response object.
* @throws GithubException if API returns an error response.
*/
public WorkflowJobsResponse getJobsForWorkflowRun(final WorkflowJobFilterCriteria options) throws GithubException {
return executeRequest(new GetWorkflowJobsForWorkflowRunRequest(options));
}
/**
* Request a re-run of a specific job Id for a given workflow.
* @param options Criteria of which job to re-run.
* @return true on success, false on error.
* @throws GithubException if API returns an error response.
*/
public boolean rerunJobFromWorkflow(final RerunJobFromWorkflowOptions options) throws GithubException {
return executeRequest(new RerunJobFromWorkflowRequest(options));
}
/**
* Request a re-run of a specific job Id for a given workflow.
* @param options Criteria of which job to re-run.
* @return true on success, false on error.
* @throws GithubException if API returns an error response.
*/
public RerunMultipleJobsResponse rerunJobsFromWorkflow(final RerunJobsFromWorkflowOptions options) throws GithubException {
final Map results = new HashMap<>();
for (final Long jobId : options.getJobIds()) {
final boolean result = executeRequest(new RerunJobFromWorkflowRequest(RerunJobFromWorkflowOptions.newBuilder()
.withOwner(options.getOwner())
.withRepo(options.getRepo())
.withJobId(jobId)
.build()
));
results.put(jobId, result);
}
return new RerunMultipleJobsResponse(results);
}
/**
* Retrieve the next page of results from the previously retrieved request.
*
* @param The parsed return object representing the result.
* @param response Previously retrieved result/response to retrieve the next page of results for.
* @return The next page of results.
* @throws InvalidPagingRequestException if no next page exists to retrieve.
* @throws GithubException if API returns an error response.
*/
public T nextPage(final PageableResponse response) throws GithubException {
// Validate
Objects.requireNonNull(response);
final PagingLinks pagingLinks = Objects.requireNonNull(response.getPagingLinks());
if (!pagingLinks.hasNextUrl()) {
throw new InvalidPagingRequestException(
"Requested 'Next' page on response " + response.getClass().getSimpleName() + ", but no Next page is available."
);
}
// Update request with appropriate page options.
final PageOptions pageOptions;
try {
pageOptions = PageOptions.fromUrl(pagingLinks.getNextUrl());
} catch (final IllegalArgumentException ex) {
throw new InvalidPagingRequestException("Unable to parse URL for paging information", ex);
}
final PageableRequest request = response.getOriginalRequest();
request.updatePageOptions(pageOptions);
// Execute and return.
return executeRequest(request);
}
/**
* Execute the given request, returning the parsed response, or throwing the appropriate
* exception if an error was returned from the API.
*
* This method scoped public to allow for user defined requests to be executed by the library
* as an extension point.
*
* @param The parsed response object.
* @param request The request to execute.
* @return The parsed response object.
* @throws GithubException if API returns an error response.
*/
public T executeRequest(final Request request) throws GithubException {
final HttpResult result = httpClient.executeRequest(request);
// Debug logging of the result.
logger.trace("HttpResult: {}", result);
// Handle Errors based on HttpCode.
if (result.getStatus() != 200 && result.getStatus() != 201 && result.getStatus() != 202 && result.getStatus() != 204) {
handleError(result);
}
// Success response code, parse response into object and return.
return request.parseResponse(result);
}
/**
* Handle error responses from the API by throwing the appropriate exception.
* @param errorResult Error response from REST API.
* @throws GithubException relating to specific underlying API error.
*/
private void handleError(final HttpResult errorResult) throws GithubException {
// Attempt to parse error response.
String errorMessage = null;
List errors = Collections.emptyList();
try {
final ErrorResponse errorResponse = new ErrorResponseParser().parseResponse(errorResult);
errorMessage = errorResponse.getMessage();
errors = errorResponse.getErrors();
} catch (final IOException e) {
errorMessage = errorResult.getContent();
}
if (errorMessage != null && errorMessage.trim().isEmpty()) {
errorMessage = null;
}
switch (errorResult.getStatus()) {
case 401:
throw new InvalidAccessTokenException(
errorMessage == null ? "Invalid Access Token" : errorMessage
);
case 403:
throw new InvalidPermissionsException(
errorMessage == null
?
"The resource requested is not available with the access token provided."
: errorMessage
);
case 404:
throw new NotFoundException(
errorMessage == null
?
"The URL or Resource Request could not be found"
: errorMessage
);
case 422:
String validationErrorMessage = (( errorMessage != null) ? errorMessage : "The submitted request was invalid. ");
validationErrorMessage += "\n" + errors.stream()
.map((error) -> error.getField() + ": " + error.getCode())
.collect(Collectors.joining("\n"));
throw new InvalidRequestException(validationErrorMessage, errors);
default:
throw new GithubException(
errorMessage == null ? "Unknown/Unhandled Error HttpCode: " + errorResult.getStatus() : errorMessage
);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy