All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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