org.eclipse.mylyn.github.internal.GitHubService.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mylyn.github.internal.GitHubService.java

Source

/*
 * Copyright 2009 Christian Trutz 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *  
 */
package org.eclipse.mylyn.github.internal;

import java.io.IOException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.gson.Gson;

/**
 * Facility to perform API operations on a GitHub issue tracker.
 */
public class GitHubService {

    private static final Log LOG = LogFactory.getLog(GitHubService.class);

    /**
     * GitHub Issues API Documentation: http://develop.github.com/p/issues.html
     */
    private final String gitURLBase = "https://github.com/api/v2/json/";

    private final String gitIssueRoot = "issues/";
    private final String gitUserRoot = "user/";

    private final HttpClient httpClient;

    private final Gson gson;

    /**
     * Helper class, describing all of the possible GitHub API actions.
     */
    private final static String OPEN = "open/"; // Implemented
    private static final String REOPEN = "reopen/";
    private final static String CLOSE = "close/";
    private final static String EDIT = "edit/"; // Implemented
    // private final static String VIEW = "view/";
    private final static String SHOW = "show/"; // :user/:repo/:number
    private final static String LIST = "list/"; // Implemented
    private final static String SEARCH = "search/"; // Implemented
    // private final static String REOPEN = "reopen/";
    // private final static String COMMENT = "comment/";
    private final static String ADD_LABEL = "label/add/"; // Implemented
    private final static String REMOVE_LABEL = "label/remove/"; // Implemented

    private static final String EMAILS = "emails";

    /**
     * Constructor, create the client and JSON/Java interface object.
     */
    public GitHubService() {
        httpClient = new HttpClient();
        gson = new Gson();
    }

    /**
     * Verify that the provided credentials are correct
     * @param credentials
     *
     * @return true if and only if the credentials are correct
     */
    public boolean verifyCredentials(GitHubCredentials credentials) throws GitHubServiceException {
        PostMethod method = null;

        boolean success = false;

        try {
            method = new PostMethod(gitURLBase + gitUserRoot + EMAILS);

            // Set the users login and API token
            final NameValuePair login = new NameValuePair("login", credentials.getUsername());
            final NameValuePair token = new NameValuePair("token", credentials.getApiToken());
            method.setRequestBody(new NameValuePair[] { login, token });

            executeMethod(method);

            // if we reach here we know that credentials were good
            success = true;
        } catch (PermissionDeniedException e) {
            // if we provide bad credentials, GitHub will return 403 or 401
            return false;
        } catch (GitHubServiceException e) {
            throw e;
        } catch (final RuntimeException runtimeException) {
            throw runtimeException;
        } catch (final Exception exception) {
            throw new GitHubServiceException(exception);
        } finally {
            if (method != null)
                method.releaseConnection();
        }
        return success;
    }

    /**
     * Search the GitHub Issues API for a given search term
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The Git repository where the issue tracker is hosted
     * @param state
     *            - The issue state you want to filter your search by
     * @param searchTerm
     *            - The text search term to find in the issues.
     * 
     * @return A GitHubIssues object containing all issues from the search
     *         results
     * 
     * @throws GitHubServiceException
     * 
     * @note API Doc: /issues/search/:user/:repo/:state/:search_term
     */
    public GitHubIssues searchIssues(final String user, final String repo, final String state,
            final String searchTerm) throws GitHubServiceException {
        GitHubIssues issues = null;
        GetMethod method = null;
        try {
            // build HTTP GET method
            if (searchTerm.trim().length() == 0) { // no search term: list all
                method = new GetMethod(gitURLBase + gitIssueRoot + LIST + user + "/" + repo + "/" + state);
            } else {
                method = new GetMethod(
                        gitURLBase + gitIssueRoot + SEARCH + user + "/" + repo + "/" + state + "/" + searchTerm);
            }
            // execute HTTP GET method
            executeMethod(method);
            // transform JSON to Java object
            issues = gson.fromJson(new String(method.getResponseBody()), GitHubIssues.class);
        } catch (GitHubServiceException e) {
            throw e;
        } catch (final RuntimeException runtimeException) {
            throw runtimeException;
        } catch (final Exception exception) {
            throw new GitHubServiceException(exception);
        } finally {
            if (method != null)
                method.releaseConnection();
        }
        return issues;
    }

    /**
     * Add a label to an existing GitHub issue.
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The git repository where the issue tracker is hosted
     * @param label
     *            - The text label to add to the existing issue
     * @param issueNumber
     *            - The issue number to add a label to
     * @param api
     *            - The users GitHub
     * 
     * @return A boolean representing the success of the function call
     * 
     * @throws GitHubServiceException
     * 
     * @note API Doc: issues/label/add/:user/:repo/:label/:number API POST
     *       Variables: login, api-token
     */
    public boolean addLabel(final String user, final String repo, final String label, final int issueNumber,
            final GitHubCredentials credentials) throws GitHubServiceException {
        PostMethod method = null;

        boolean success = false;

        try {
            // build HTTP GET method
            method = new PostMethod(gitURLBase + gitIssueRoot + ADD_LABEL + user + "/" + repo + "/" + label + "/"
                    + Integer.toString(issueNumber));

            // Set the users login and API token
            final NameValuePair login = new NameValuePair("login", credentials.getUsername());
            final NameValuePair token = new NameValuePair("token", credentials.getApiToken());
            method.setRequestBody(new NameValuePair[] { login, token });

            // execute HTTP GET method
            executeMethod(method);
            // Check the response, make sure the action was successful
            final String response = method.getResponseBodyAsString();
            if (response.contains(label.subSequence(0, label.length()))) {
                success = true;
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("Response: " + method.getResponseBodyAsString());
                LOG.debug("URL: " + method.getURI());
            }
        } catch (GitHubServiceException e) {
            throw e;
        } catch (final RuntimeException runtimeException) {
            throw runtimeException;
        } catch (final Exception exception) {
            throw new GitHubServiceException(exception);
        } finally {
            if (method != null)
                method.releaseConnection();
        }
        return success;
    }

    /**
     * Remove an existing label from an existing GitHub issue.
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The git repository where the issue tracker is hosted
     * @param label
     * @param issueNumber
     * @param api
     * 
     * @return A list of GitHub issues in the response text.
     * 
     * @throws GitHubServiceException
     * 
     *             API Doc: issues/label/remove/:user/:repo/:label/:number API
     *             POST Variables: login, api-token
     */
    public boolean removeLabel(final String user, final String repo, final String label, final int issueNumber,
            final GitHubCredentials credentials) throws GitHubServiceException {
        PostMethod method = null;
        boolean success = false;
        try {
            // build HTTP GET method
            method = new PostMethod(gitURLBase + gitIssueRoot + REMOVE_LABEL + user + "/" + repo + "/" + label + "/"
                    + Integer.toString(issueNumber));

            // Set the users login and API token
            final NameValuePair login = new NameValuePair("login", credentials.getUsername());
            final NameValuePair token = new NameValuePair("token", credentials.getUsername());
            method.setRequestBody(new NameValuePair[] { login, token });

            // execute HTTP GET method
            executeMethod(method);
            // Check the response, make sure the action was successful
            final String response = method.getResponseBodyAsString();
            if (!response.contains(label.subSequence(0, label.length()))) {
                success = true;
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Response: " + method.getResponseBodyAsString());
                LOG.debug("URL: " + method.getURI());
            }
        } catch (GitHubServiceException e) {
            throw e;
        } catch (final RuntimeException runtimeException) {
            throw runtimeException;
        } catch (final Exception exception) {
            throw new GitHubServiceException(exception);
        } finally {
            if (method != null)
                method.releaseConnection();
        }
        return success;
    }

    /**
     * Open a new issue using the GitHub Issues API.
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The git repository where the issue tracker is hosted
     * @param issue
     *            - The GitHub issue object to create on the issue tracker.
     * 
     * @return the issue that was created
     * 
     * @throws GitHubServiceException
     * 
     *             API Doc: issues/open/:user/:repo API POST Variables: login,
     *             api-token, title, body
     */
    public GitHubIssue openIssue(final String user, final String repo, final GitHubIssue issue,
            final GitHubCredentials credentials) throws GitHubServiceException {

        GitHubShowIssue showIssue = null;

        PostMethod method = null;
        try {
            // Create the HTTP POST method
            method = new PostMethod(gitURLBase + gitIssueRoot + OPEN + user + "/" + repo);
            // Set the users login and API token
            final NameValuePair login = new NameValuePair("login", credentials.getUsername());
            final NameValuePair token = new NameValuePair("token", credentials.getApiToken());
            final NameValuePair body = new NameValuePair("body", issue.getBody());
            final NameValuePair title = new NameValuePair("title", issue.getTitle());

            method.setRequestBody(new NameValuePair[] { login, token, body, title });

            executeMethod(method);
            showIssue = gson.fromJson(new String(method.getResponseBody()), GitHubShowIssue.class);

            if (showIssue == null || showIssue.getIssue() == null) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Unexpected server response: " + method.getResponseBodyAsString());
                }
                throw new GitHubServiceException("Unexpected server response");
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Response: " + method.getResponseBodyAsString());
                LOG.debug("URL: " + method.getURI());
            }
            return showIssue.getIssue();
        } catch (GitHubServiceException e) {
            throw e;
        } catch (final RuntimeException runTimeException) {
            throw runTimeException;
        } catch (final Exception e) {
            throw new GitHubServiceException(e);
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }
    }

    /**
     * Edit an existing issue using the GitHub Issues API.
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The git repository where the issue tracker is hosted
     * @param issue
     *            - The GitHub issue object to create on the issue tracker.
     * 
     * @return the issue with changes
     * 
     * @throws GitHubServiceException
     * 
     *             API Doc: issues/edit/:user/:repo/:number API POST Variables:
     *             login, api-token, title, body
     */
    public GitHubIssue editIssue(final String user, final String repo, final GitHubIssue issue,
            final GitHubCredentials credentials) throws GitHubServiceException {
        PostMethod method = null;
        try {

            // Create the HTTP POST method
            method = new PostMethod(gitURLBase + gitIssueRoot + EDIT + user + "/" + repo + "/" + issue.getNumber());
            // Set the users login and API token
            final NameValuePair login = new NameValuePair("login", credentials.getUsername());
            final NameValuePair token = new NameValuePair("token", credentials.getApiToken());
            final NameValuePair body = new NameValuePair("body", issue.getBody());
            final NameValuePair title = new NameValuePair("title", issue.getTitle());

            method.setRequestBody(new NameValuePair[] { login, token, body, title });

            executeMethod(method);
            GitHubShowIssue showIssue = gson.fromJson(method.getResponseBodyAsString(), GitHubShowIssue.class);

            // Make sure the changes were made properly
            if (showIssue == null || showIssue.getIssue() == null) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Unexpected server response: " + method.getResponseBodyAsString());
                }
                throw new GitHubServiceException("Unexpected server response");
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("Response: " + method.getResponseBodyAsString());
                LOG.debug("URL: " + method.getURI());
            }
            return showIssue.getIssue();
        } catch (final RuntimeException runTimeException) {
            throw runTimeException;
        } catch (final Exception e) {
            throw new GitHubServiceException(e);
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }
    }

    public GitHubIssue showIssue(final String user, final String repo, final String issueNumber)
            throws GitHubServiceException {
        GetMethod method = null;
        try {
            // build HTTP GET method
            method = new GetMethod(gitURLBase + gitIssueRoot + SHOW + user + "/" + repo + "/" + issueNumber);

            // execute HTTP GET method
            executeMethod(method);
            // transform JSON to Java object
            GitHubShowIssue issue = gson.fromJson(new String(method.getResponseBody()), GitHubShowIssue.class);

            return issue.getIssue();
        } catch (GitHubServiceException e) {
            throw e;
        } catch (final RuntimeException runtimeException) {
            throw runtimeException;
        } catch (final Exception exception) {
            throw new GitHubServiceException(exception);
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }
    }

    private void executeMethod(HttpMethod method) throws GitHubServiceException {
        int status;
        try {
            status = httpClient.executeMethod(method);
        } catch (HttpException e) {
            throw new GitHubServiceException(e);
        } catch (IOException e) {
            throw new GitHubServiceException(e);
        }
        if (status != HttpStatus.SC_OK) {
            switch (status) {
            case HttpStatus.SC_UNAUTHORIZED:
            case HttpStatus.SC_FORBIDDEN:
                throw new PermissionDeniedException(method.getStatusLine());
            default:
                throw new GitHubServiceException(method.getStatusLine());
            }
        }
    }

    /**
     * Edit an existing issue using the GitHub Issues API and change its status to open.
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The git repository where the issue tracker is hosted
     * @param issue
     *            - The GitHub issue object to create on the issue tracker.
     * 
     * @return the issue with changes
     * 
     * @throws GitHubServiceException
     * 
     *             API Doc: issues/reopen/:user/:repo/:number API POST Variables:
     *             login, api-token, title, body
     */
    public GitHubIssue reopenIssue(String user, String repo, GitHubIssue issue, GitHubCredentials credentials)
            throws GitHubServiceException {
        issue = editIssue(user, repo, issue, credentials);
        return changeIssueStatus(user, repo, REOPEN, issue, credentials);
    }

    /**
     * Edit an existing issue using the GitHub Issues API and change its status to closed.
     * 
     * @param user
     *            - The user the repository is owned by
     * @param repo
     *            - The git repository where the issue tracker is hosted
     * @param issue
     *            - The GitHub issue object to create on the issue tracker.
     * 
     * @return the issue with changes
     * 
     * @throws GitHubServiceException
     * 
     *             API Doc: issues/close/:user/:repo/:number API POST Variables:
     *             login, api-token, title, body
     */
    public GitHubIssue closeIssue(String user, String repo, GitHubIssue issue, GitHubCredentials credentials)
            throws GitHubServiceException {
        issue = editIssue(user, repo, issue, credentials);
        return changeIssueStatus(user, repo, CLOSE, issue, credentials);

    }

    private GitHubIssue changeIssueStatus(final String user, final String repo, String githubOperation,
            final GitHubIssue issue, final GitHubCredentials credentials) throws GitHubServiceException {
        PostMethod method = null;
        try {

            // Create the HTTP POST method
            method = new PostMethod(
                    gitURLBase + gitIssueRoot + githubOperation + user + "/" + repo + "/" + issue.getNumber());
            // Set the users login and API token
            final NameValuePair login = new NameValuePair("login", credentials.getUsername());
            final NameValuePair token = new NameValuePair("token", credentials.getApiToken());

            method.setRequestBody(new NameValuePair[] { login, token });

            executeMethod(method);
            GitHubShowIssue showIssue = gson.fromJson(method.getResponseBodyAsString(), GitHubShowIssue.class);

            // Make sure the changes were made properly
            if (showIssue == null || showIssue.getIssue() == null) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Unexpected server response: " + method.getResponseBodyAsString());
                }
                throw new GitHubServiceException("Unexpected server response");
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("Response: " + method.getResponseBodyAsString());
                LOG.debug("URL: " + method.getURI());
            }
            return showIssue.getIssue();
        } catch (final RuntimeException runTimeException) {
            throw runTimeException;
        } catch (final Exception e) {
            throw new GitHubServiceException(e);
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }
    }
}