Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Aniszczyk2011-03-23 19:04:34 -0400
committerChris Aniszczyk2011-03-23 19:04:34 -0400
commit8758fb2dd13ed6031ead593e4fc092c9c2e7f1eb (patch)
treec20c312bdf7da473e6fe3e435c0670cd8dc0b82a /org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github
downloadegit-github-8758fb2dd13ed6031ead593e4fc092c9c2e7f1eb.tar.gz
egit-github-8758fb2dd13ed6031ead593e4fc092c9c2e7f1eb.tar.xz
egit-github-8758fb2dd13ed6031ead593e4fc092c9c2e7f1eb.zip
Initial commit of Mylyn GitHub Integration
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
Diffstat (limited to 'org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github')
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHub.java96
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubCredentials.java44
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssue.java172
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssues.java31
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubRepositoryConnector.java217
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubService.java557
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubServiceException.java58
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubShowIssue.java26
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributeMapper.java49
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributes.java71
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskDataHandler.java265
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskOperation.java48
-rw-r--r--org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/PermissionDeniedException.java37
13 files changed, 1671 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHub.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHub.java
new file mode 100644
index 00000000..708f39c5
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHub.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+
+public class GitHub {
+ public static final String BUNDLE_ID = "org.eclipse.mylyn.github.core";
+ public static final String CONNECTOR_KIND = "github";
+
+ public static final String HTTP_WWW_GITHUB_ORG = "http://www.github.org";
+ public static final String HTTP_GITHUB_COM = "http://github.com";
+
+ public static final Pattern URL_PATTERN = Pattern.compile("(?:"+Pattern.quote(HTTP_WWW_GITHUB_ORG)+"|"+Pattern.quote(HTTP_GITHUB_COM)+")/([^/]+)/([^/]+)");
+
+ public static IStatus createStatus(int severity, String message) {
+ return new Status(severity, BUNDLE_ID, message);
+ }
+
+ public static IStatus createStatus(int severity, String message, Throwable e) {
+ return new Status(severity, BUNDLE_ID, message, e);
+ }
+
+ public static IStatus createErrorStatus(String message) {
+ return createStatus(IStatus.ERROR, message);
+ }
+
+ public static IStatus createErrorStatus(String message, Throwable t) {
+ return createStatus(IStatus.ERROR, message, t);
+ }
+
+ public static IStatus createErrorStatus(Throwable e) {
+ return createStatus(IStatus.ERROR, "Unexpected error: "
+ + e.getMessage(), e);
+ }
+
+ public static ILog getLog() {
+ return Platform.getLog(Platform.getBundle(BUNDLE_ID));
+ }
+
+ public static void logError(String message,Throwable t) {
+ getLog().log(createErrorStatus(message, t));
+ }
+
+ public static void logError(Throwable t) {
+ getLog().log(createErrorStatus(t.getMessage(), t));
+ }
+
+ public static String computeTaskRepositoryUser(String repositoryUrl) {
+ Matcher matcher = URL_PATTERN.matcher(repositoryUrl);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ return null;
+ }
+
+ public static String computeTaskRepositoryProject(String repositoryUrl) {
+ Matcher matcher = URL_PATTERN.matcher(repositoryUrl);
+ if (matcher.matches()) {
+ return matcher.group(2);
+ }
+ return null;
+ }
+
+ /**
+ * uses github.com
+ * @see #createGitHubUrlAlternate(String, String)
+ */
+ public static String createGitHubUrl(String user,String project) {
+ return HTTP_GITHUB_COM+'/'+user+'/'+project;
+ }
+
+ /**
+ * Uses www.github.org
+ * @see #createGitHubUrl(String, String)
+ */
+ public static String createGitHubUrlAlternate(String user,String project) {
+ return HTTP_WWW_GITHUB_ORG+'/'+user+'/'+project;
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubCredentials.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubCredentials.java
new file mode 100644
index 00000000..19cd580c
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubCredentials.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import org.eclipse.mylyn.commons.net.AuthenticationCredentials;
+import org.eclipse.mylyn.commons.net.AuthenticationType;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+
+public class GitHubCredentials {
+ private final String username;
+ private final String apiToken;
+
+
+ public GitHubCredentials(String username, String apiToken) {
+ this.username = username;
+ this.apiToken = apiToken;
+ }
+
+ public GitHubCredentials(AuthenticationCredentials credentials) {
+ this(credentials.getUserName(),credentials.getPassword());
+ }
+
+ public static GitHubCredentials create(TaskRepository repository) {
+ return new GitHubCredentials(repository.getCredentials(AuthenticationType.REPOSITORY));
+ }
+
+ public String getUsername() {
+ return username;
+ }
+ public String getApiToken() {
+ return apiToken;
+ }
+
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssue.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssue.java
new file mode 100644
index 00000000..9a989e00
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssue.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+
+/**
+ * GitHub Issue object to hold all the properties of an individual issue.
+ */
+public class GitHubIssue {
+
+ private String number;
+
+ private String user;
+
+ private String title;
+
+ private String body;
+
+ /**
+ * open, closed
+ */
+ private String state;
+
+ private String created_at;
+ private String updated_at;
+ private String closed_at;
+
+ /**
+ * Create a new GitHub Issue Object
+ *
+ * @param number
+ * - GitHub Issue number
+ * @param user
+ * - User who the posted issue belongs too.
+ * @param title
+ * - Issue title
+ * @param body
+ * - The text body of the issue;
+ */
+ public GitHubIssue(final String number, final String user,
+ final String title, final String body) {
+ this.number = number;
+ this.user = user;
+ this.title = title;
+ this.body = body;
+ }
+
+ /**
+ * Create a GitHub Issue with all parameters set to empty.
+ */
+ public GitHubIssue() {
+ this.number = "";
+ this.user = "";
+ this.title = "";
+ this.body = "";
+ }
+
+ /**
+ * Getter for the issue number
+ *
+ * @return The string representation of the issue number.
+ */
+ public String getNumber() {
+ return number;
+ }
+
+ /**
+ * Set the issues's number
+ *
+ * @param number
+ * - String representation of the number to set to.
+ */
+ public void setNumber(final String number) {
+ this.number = number;
+ }
+
+ /**
+ * Getter for the user name of the issue creator
+ *
+ * @return The user name of the person who created the issue
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * Set the issue user name to
+ *
+ * @param user
+ * - The user name to set the issue creator to.
+ */
+ public void setUser(final String user) {
+ this.user = user;
+ }
+
+ /**
+ * Getter for the issue Title
+ *
+ * @return The title text of this issue
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * @param title
+ */
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+
+ /**
+ * Getter of the body of an issue
+ *
+ * @return The text body of the issue
+ */
+ public String getBody() {
+ return body;
+ }
+
+ /**
+ * Setter for the body of an issue
+ *
+ * @param body
+ * - The text body to set for this issue
+ */
+ public void setBody(final String body) {
+ this.body = body;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public String getCreated_at() {
+ return created_at;
+ }
+
+ public void setCreated_at(String created_at) {
+ this.created_at = created_at;
+ }
+
+ public String getUpdated_at() {
+ return updated_at;
+ }
+
+ public void setUpdated_at(String updated_at) {
+ this.updated_at = updated_at;
+ }
+
+ public String getClosed_at() {
+ return closed_at;
+ }
+
+ public void setClosed_at(String closed_at) {
+ this.closed_at = closed_at;
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssues.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssues.java
new file mode 100644
index 00000000..02100590
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubIssues.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+/**
+ * Container of multiple GitHub Issues, used when returning JSON objects
+ */
+public class GitHubIssues {
+
+ private GitHubIssue[] issues;
+
+ /**
+ * Getter for all issues inside this object
+ *
+ * @return The array of individual GitHub Issues
+ */
+ public GitHubIssue[] getIssues() {
+ return issues;
+ }
+
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubRepositoryConnector.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubRepositoryConnector.java
new file mode 100644
index 00000000..c263cb9a
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubRepositoryConnector.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
+import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler;
+import org.eclipse.mylyn.tasks.core.data.TaskData;
+import org.eclipse.mylyn.tasks.core.data.TaskDataCollector;
+import org.eclipse.mylyn.tasks.core.data.TaskMapper;
+import org.eclipse.mylyn.tasks.core.sync.ISynchronizationSession;
+
+/**
+ * GitHub connector.
+ */
+public class GitHubRepositoryConnector extends AbstractRepositoryConnector {
+
+
+ /**
+ * GitHub kind.
+ */
+ protected static final String KIND = GitHub.CONNECTOR_KIND;
+
+ /**
+ * GitHub service which creates, lists, deletes, etc. GitHub tasks.
+ */
+ private final GitHubService service = new GitHubService();
+
+ /**
+ * GitHub specific {@link AbstractTaskDataHandler}.
+ */
+ private final GitHubTaskDataHandler taskDataHandler;
+
+ public GitHubRepositoryConnector() {
+ taskDataHandler = new GitHubTaskDataHandler(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return always {@code true}
+ */
+ @Override
+ public boolean canCreateNewTask(TaskRepository repository) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return always {@code true}
+ */
+ @Override
+ public boolean canCreateTaskFromKey(TaskRepository repository) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see #KIND
+ */
+ @Override
+ public String getConnectorKind() {
+ return KIND;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getLabel() {
+ return "GitHub";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AbstractTaskDataHandler getTaskDataHandler() {
+ return this.taskDataHandler;
+ }
+
+ @Override
+ public IStatus performQuery(TaskRepository repository,
+ IRepositoryQuery query, TaskDataCollector collector,
+ ISynchronizationSession session, IProgressMonitor monitor) {
+
+ IStatus result = Status.OK_STATUS;
+ String queryStatus = query.getAttribute("status");
+
+ String[] statuses;
+ if (queryStatus.equals("all")) {
+ statuses = new String[] {"open","closed"};
+ } else {
+ statuses = new String[] { queryStatus };
+ }
+
+ monitor.beginTask("Querying repository ...", statuses.length);
+ try {
+ String user = GitHub.computeTaskRepositoryUser(repository.getUrl());
+ String project = GitHub.computeTaskRepositoryProject(repository.getUrl());
+
+ // perform query
+
+ for (String status: statuses) {
+ GitHubIssues issues = service.searchIssues(user,project,
+ status, query
+ .getAttribute("queryText"));
+
+ // collect task data
+ for (GitHubIssue issue : issues.getIssues()) {
+ TaskData taskData = taskDataHandler.createPartialTaskData(
+ repository, monitor,user, project, issue);
+ collector.accept(taskData);
+ }
+ monitor.worked(1);
+ }
+
+ result = Status.OK_STATUS;
+ } catch (GitHubServiceException e) {
+ result = GitHub.createErrorStatus(e);
+ }
+
+ monitor.done();
+ return result;
+ }
+
+
+ @Override
+ public TaskData getTaskData(TaskRepository repository, String taskId,
+ IProgressMonitor monitor) throws CoreException {
+
+ String user = GitHub.computeTaskRepositoryUser(repository.getUrl());
+ String project = GitHub.computeTaskRepositoryProject(repository.getUrl());
+
+ try {
+ GitHubIssue issue = service.showIssue(user, project, taskId);
+ TaskData taskData = taskDataHandler.createTaskData(repository, monitor, user, project, issue);
+
+ return taskData;
+ } catch (GitHubServiceException e) {
+ throw new CoreException(GitHub.createErrorStatus(e));
+ }
+ }
+
+
+ @Override
+ public String getRepositoryUrlFromTaskUrl(String taskFullUrl) {
+ if (taskFullUrl != null) {
+ Matcher matcher = Pattern.compile("(http://.+?)/issues/issue/([^/]+)").matcher(taskFullUrl);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getTaskIdFromTaskUrl(String taskFullUrl) {
+ if (taskFullUrl != null) {
+ Matcher matcher = Pattern.compile(".+?/issues/issue/([^/]+)").matcher(taskFullUrl);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getTaskUrl(String repositoryUrl, String taskId) {
+ return repositoryUrl+"/issues/issue/"+taskId;
+ }
+
+ @Override
+ public void updateRepositoryConfiguration(TaskRepository taskRepository,
+ IProgressMonitor monitor) throws CoreException {
+ }
+
+ @Override
+ public boolean hasTaskChanged(TaskRepository repository, ITask task,
+ TaskData taskData) {
+ return new TaskMapper(taskData).hasChanges(task);
+ }
+
+ @Override
+ public void updateTaskFromTaskData(TaskRepository taskRepository,
+ ITask task, TaskData taskData) {
+ if (!taskData.isNew()) {
+ task.setUrl(getTaskUrl(taskRepository.getUrl(), taskData.getTaskId()));
+ }
+ new TaskMapper(taskData).applyTo(task);
+ }
+
+ public GitHubService getService() {
+ return service;
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubService.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubService.java
new file mode 100644
index 00000000..07d884d8
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubService.java
@@ -0,0 +1,557 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+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();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubServiceException.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubServiceException.java
new file mode 100644
index 00000000..3e9a51c2
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubServiceException.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import org.apache.commons.httpclient.StatusLine;
+
+/**
+ * Exception generated by the GitHubService
+ */
+public class GitHubServiceException extends Exception {
+
+ /**
+ * Auto generated serialVersionUID
+ */
+ private static final long serialVersionUID = -6287902058352190022L;
+
+ private int httpStatusCode = Integer.MIN_VALUE;
+
+ /**
+ * Constructor for the GitHubServiceException
+ *
+ * @param exception
+ * - Exception to wrap around
+ */
+ protected GitHubServiceException(final Exception exception) {
+ super(exception);
+ }
+
+ protected GitHubServiceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ protected GitHubServiceException(String message) {
+ super(message);
+ }
+
+ protected GitHubServiceException(StatusLine statusLine) {
+ this(String.format("HTTP %s: %s",statusLine.getStatusCode(),statusLine.getReasonPhrase()));
+ httpStatusCode = statusLine.getStatusCode();
+ }
+
+ /**
+ * the HTTP status code, or -1 if unknown or not applicable.
+ */
+ public int getHttpStatusCode() {
+ return httpStatusCode;
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubShowIssue.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubShowIssue.java
new file mode 100644
index 00000000..f249c53f
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubShowIssue.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+public class GitHubShowIssue {
+ private GitHubIssue issue;
+
+ public GitHubIssue getIssue() {
+ return issue;
+ }
+
+ public void setIssue(GitHubIssue issue) {
+ this.issue = issue;
+ }
+
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributeMapper.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributeMapper.java
new file mode 100644
index 00000000..2d42de8d
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributeMapper.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper;
+
+public class GitHubTaskAttributeMapper extends TaskAttributeMapper {
+
+ private DateFormat dateFormat = SimpleDateFormat.getDateTimeInstance();
+
+ public GitHubTaskAttributeMapper(TaskRepository taskRepository) {
+ super(taskRepository);
+ }
+
+ @Override
+ public String mapToRepositoryKey(TaskAttribute parent, String key) {
+ return key;
+ }
+
+ @Override
+ public Date getDateValue(TaskAttribute attribute) {
+ String value = attribute.getValue();
+ if (value != null) {
+ try {
+ return dateFormat.parse(value);
+ } catch (ParseException e) {
+ return super.getDateValue(attribute);
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributes.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributes.java
new file mode 100644
index 00000000..b43694d0
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskAttributes.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+
+public enum GitHubTaskAttributes {
+
+ KEY("Key",TaskAttribute.TASK_KEY,TaskAttribute.TYPE_SHORT_TEXT,true,true,true),
+ TITLE("Summary",TaskAttribute.SUMMARY,TaskAttribute.TYPE_SHORT_TEXT,true,false,true),
+ BODY("Description",TaskAttribute.DESCRIPTION,TaskAttribute.TYPE_LONG_RICH_TEXT,true,false,true),
+
+ CREATION_DATE("Created",TaskAttribute.DATE_CREATION,TaskAttribute.TYPE_DATETIME,true,true,false),
+ MODIFICATION_DATE("Modified",TaskAttribute.DATE_MODIFICATION,TaskAttribute.TYPE_DATETIME,true,true,false),
+ CLOSED_DATE("Closed",TaskAttribute.DATE_COMPLETION,TaskAttribute.TYPE_DATETIME,false,true,false),
+
+ STATUS("Status",TaskAttribute.STATUS,TaskAttribute.TYPE_SHORT_TEXT,true,false,true)
+ ;
+
+
+ private final String id;
+ private final String label;
+ private final boolean readOnly;
+ private final boolean initTask;
+ private final boolean requiredForFullTaskData;
+ private final String type;
+
+ private GitHubTaskAttributes(String label, String id,String type,boolean requiredForFullTaskData, boolean readOnly, boolean initTask) {
+ this.label = label;
+ this.requiredForFullTaskData = requiredForFullTaskData;
+ this.id = id==null?"github."+name():id;
+ this.type = type;
+ this.readOnly = readOnly;
+ this.initTask = initTask;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+ public String getId() {
+ return id;
+ }
+ public String getType() {
+ return type;
+ }
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public String getKind() {
+ return TaskAttribute.KIND_DEFAULT;
+ }
+
+ public boolean isInitTask() {
+ return initTask;
+ }
+
+ public boolean isRequiredForFullTaskData() {
+ return requiredForFullTaskData;
+ }
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskDataHandler.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskDataHandler.java
new file mode 100644
index 00000000..62649e79
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskDataHandler.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.mylyn.tasks.core.ITaskMapping;
+import org.eclipse.mylyn.tasks.core.RepositoryResponse;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.core.RepositoryResponse.ResponseKind;
+import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler;
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper;
+import org.eclipse.mylyn.tasks.core.data.TaskAttributeMetaData;
+import org.eclipse.mylyn.tasks.core.data.TaskData;
+import org.eclipse.mylyn.tasks.core.data.TaskOperation;
+
+public class GitHubTaskDataHandler extends AbstractTaskDataHandler {
+
+ private static final String DATA_VERSION = "1";
+ /**
+ *
+ */
+ private GitHubTaskAttributeMapper taskAttributeMapper = null;
+ private final GitHubRepositoryConnector connector;
+ private DateFormat dateFormat = SimpleDateFormat.getDateTimeInstance();
+
+ private DateFormat githubDateFormat = new SimpleDateFormat("yyyy/mm/dd HH:MM:ss Z");
+
+ public GitHubTaskDataHandler(GitHubRepositoryConnector connector) {
+ this.connector = connector;
+ }
+
+ @Override
+ public TaskAttributeMapper getAttributeMapper(TaskRepository taskRepository) {
+ if (this.taskAttributeMapper == null)
+ this.taskAttributeMapper = new GitHubTaskAttributeMapper(
+ taskRepository);
+ return this.taskAttributeMapper;
+ }
+
+ public TaskData createPartialTaskData(TaskRepository repository,
+ IProgressMonitor monitor,String user, String project, GitHubIssue issue) {
+
+ TaskData data = new TaskData(getAttributeMapper(repository),
+ GitHubRepositoryConnector.KIND, repository.getRepositoryUrl(),
+ issue.getNumber());
+ data.setVersion(DATA_VERSION);
+
+ createOperations(data,issue);
+
+
+ createAttribute(data, GitHubTaskAttributes.KEY,issue.getNumber());
+ createAttribute(data, GitHubTaskAttributes.TITLE, issue.getTitle());
+ createAttribute(data, GitHubTaskAttributes.BODY, issue.getBody());
+ createAttribute(data, GitHubTaskAttributes.STATUS, issue.getState());
+ createAttribute(data, GitHubTaskAttributes.CREATION_DATE, toLocalDate(issue.getCreated_at()));
+ createAttribute(data, GitHubTaskAttributes.MODIFICATION_DATE, toLocalDate(issue.getCreated_at()));
+ createAttribute(data, GitHubTaskAttributes.CLOSED_DATE, toLocalDate(issue.getClosed_at()));
+
+ if (isPartial(data)) {
+ data.setPartial(true);
+ }
+
+ return data;
+ }
+
+
+ private boolean isPartial(TaskData data) {
+ for (GitHubTaskAttributes attribute: GitHubTaskAttributes.values()) {
+ if (attribute.isRequiredForFullTaskData()) {
+ TaskAttribute taskAttribute = data.getRoot().getAttribute(attribute.getId());
+ if (taskAttribute == null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void createOperations(TaskData data, GitHubIssue issue) {
+ TaskAttribute operationAttribute = data.getRoot().createAttribute(TaskAttribute.OPERATION);
+ operationAttribute.getMetaData().setType(TaskAttribute.TYPE_OPERATION);
+
+ if (!data.isNew()) {
+ if (issue.getState() != null) {
+ addOperation(data,issue,GitHubTaskOperation.LEAVE,true);
+ if (issue.getState().equals("open")) {
+ addOperation(data,issue,GitHubTaskOperation.CLOSE,false);
+ } else if (issue.getState().equals("closed")) {
+ addOperation(data,issue,GitHubTaskOperation.REOPEN,false);
+ }
+ }
+ }
+ }
+
+ private void addOperation(TaskData data, GitHubIssue issue, GitHubTaskOperation operation,boolean asDefault) {
+ TaskAttribute attribute = data.getRoot().createAttribute(TaskAttribute.PREFIX_OPERATION + operation.getId());
+ String label = createOperationLabel(issue, operation);
+ TaskOperation.applyTo(attribute, operation.getId(), label);
+
+ if (asDefault) {
+ TaskAttribute operationAttribute = data.getRoot().getAttribute(TaskAttribute.OPERATION);
+ TaskOperation.applyTo(operationAttribute, operation.getId(), label);
+ }
+ }
+
+ private String createOperationLabel(GitHubIssue issue,
+ GitHubTaskOperation operation) {
+ return operation==GitHubTaskOperation.LEAVE?operation.getLabel()+issue.getState():operation.getLabel();
+ }
+
+ private String toLocalDate(String date) {
+ if (date != null && date.trim().length() > 0) {
+ // expect "2010/02/02 22:58:39 -0800"
+ try {
+ Date d = githubDateFormat.parse(date);
+ date = dateFormat.format(d);
+ } catch (ParseException e) {
+ // ignore
+ }
+ }
+ return date;
+ }
+
+ private String toGitHubDate(TaskData taskData,
+ GitHubTaskAttributes attr) {
+ TaskAttribute attribute = taskData.getRoot().getAttribute(attr.name());
+ String value = attribute==null?null:attribute.getValue();
+ if (value != null) {
+ try {
+ Date d = dateFormat.parse(value);
+ value = githubDateFormat.format(d);
+ } catch (ParseException e) {
+ // ignore
+ }
+ }
+ return value;
+ }
+
+ public TaskData createTaskData(TaskRepository repository,
+ IProgressMonitor monitor, String user, String project,
+ GitHubIssue issue) {
+ TaskData taskData = createPartialTaskData(repository, monitor, user, project, issue);
+ taskData.setPartial(false);
+
+ return taskData;
+ }
+
+ private GitHubIssue createIssue(TaskData taskData) {
+ GitHubIssue issue = new GitHubIssue();
+ if (!taskData.isNew()) {
+ issue.setNumber(taskData.getTaskId());
+ }
+ issue.setBody(getAttributeValue(taskData,GitHubTaskAttributes.BODY));
+ issue.setTitle(getAttributeValue(taskData,GitHubTaskAttributes.TITLE));
+ issue.setState(getAttributeValue(taskData,GitHubTaskAttributes.STATUS));
+ issue.setCreated_at(toGitHubDate(taskData,GitHubTaskAttributes.CREATION_DATE));
+ issue.setCreated_at(toGitHubDate(taskData,GitHubTaskAttributes.MODIFICATION_DATE));
+ issue.setCreated_at(toGitHubDate(taskData,GitHubTaskAttributes.CLOSED_DATE));
+ return issue;
+ }
+
+ private String getAttributeValue(TaskData taskData,
+ GitHubTaskAttributes attr) {
+ TaskAttribute attribute = taskData.getRoot().getAttribute(attr.getId());
+ return attribute==null?null:attribute.getValue();
+ }
+
+ private void createAttribute(TaskData data, GitHubTaskAttributes attribute, String value) {
+ TaskAttribute attr = data.getRoot().createAttribute(attribute.getId());
+ TaskAttributeMetaData metaData = attr.getMetaData();
+ metaData.defaults()
+ .setType(attribute.getType())
+ .setKind(attribute.getKind())
+ .setLabel(attribute.getLabel())
+ .setReadOnly(attribute.isReadOnly());
+
+ if (value != null) {
+ attr.addValue(value);
+ }
+ }
+
+ @Override
+ public boolean initializeTaskData(TaskRepository repository, TaskData data,
+ ITaskMapping initializationData, IProgressMonitor monitor)
+ throws CoreException {
+
+ data.setVersion(DATA_VERSION);
+
+ for (GitHubTaskAttributes attr: GitHubTaskAttributes.values()) {
+ if (attr.isInitTask()) {
+ createAttribute(data, attr,null);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public RepositoryResponse postTaskData(TaskRepository repository,
+ TaskData taskData, Set<TaskAttribute> oldAttributes,
+ IProgressMonitor monitor) throws CoreException {
+
+ GitHubIssue issue = createIssue(taskData);
+ String user = GitHub.computeTaskRepositoryUser(repository.getUrl());
+ String repo = GitHub.computeTaskRepositoryProject(repository.getUrl());
+ try {
+
+ GitHubService service = connector.getService();
+ GitHubCredentials credentials = GitHubCredentials.create(repository);
+ if (taskData.isNew()) {
+ issue = service.openIssue(user , repo, issue, credentials);
+ } else {
+ TaskAttribute operationAttribute = taskData.getRoot().getAttribute(TaskAttribute.OPERATION);
+
+ GitHubTaskOperation operation = null;
+
+
+ if (operationAttribute != null) {
+ String opId = operationAttribute.getValue();
+ operation = GitHubTaskOperation.fromId(opId);
+
+ }
+ if (operation != null && operation != GitHubTaskOperation.LEAVE) {
+ service.editIssue(user , repo, issue, credentials);
+ switch (operation) {
+ case REOPEN:
+ service.reopenIssue(user,repo,issue,credentials);
+ break;
+ case CLOSE:
+ service.closeIssue(user,repo,issue,credentials);
+ break;
+ default:
+ throw new IllegalStateException("not implemented: "+operation);
+ }
+ } else {
+ service.editIssue(user , repo, issue, credentials);
+ }
+ }
+ return new RepositoryResponse(taskData.isNew()?ResponseKind.TASK_CREATED:ResponseKind.TASK_UPDATED,issue.getNumber());
+ } catch (GitHubServiceException e) {
+ throw new CoreException(GitHub.createErrorStatus(e));
+ }
+
+ }
+
+
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskOperation.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskOperation.java
new file mode 100644
index 00000000..7043d477
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/GitHubTaskOperation.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+public enum GitHubTaskOperation {
+ LEAVE("Leave as "),
+ REOPEN("Reopen"),
+ CLOSE("Close");
+
+ private final String label;
+
+ private GitHubTaskOperation(String label) {
+ this.label = label;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public String getId() {
+ return name();
+ }
+
+ /**
+ * get the operation by its id
+ * @param opId the id, or null
+ * @return the operation, or null if the id was null or did not match any operation
+ */
+ public static GitHubTaskOperation fromId(String opId) {
+ for (GitHubTaskOperation op: values()) {
+ if (op.getId().equals(opId)) {
+ return op;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/PermissionDeniedException.java b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/PermissionDeniedException.java
new file mode 100644
index 00000000..f2401048
--- /dev/null
+++ b/org.eclipse.mylyn.github.core/src/org/eclipse/mylyn/github/internal/PermissionDeniedException.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Red Hat and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Green <david.green@tasktop.com> - initial contribution
+ * Christian Trutz <christian.trutz@gmail.com> - initial contribution
+ * Chris Aniszczyk <caniszczyk@gmail.com> - initial contribution
+ *******************************************************************************/
+package org.eclipse.mylyn.github.internal;
+
+import org.apache.commons.httpclient.StatusLine;
+
+public class PermissionDeniedException extends GitHubServiceException {
+
+ private static final long serialVersionUID = -4635370712942848361L;
+
+ protected PermissionDeniedException(Exception exception) {
+ super(exception);
+ }
+
+ protected PermissionDeniedException(StatusLine statusLine) {
+ super(statusLine);
+ }
+
+ protected PermissionDeniedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ protected PermissionDeniedException(String message) {
+ super(message);
+ }
+
+}

Back to the top