/******************************************************************************* * 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.internal.github.core.issue; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.egit.github.core.Comment; import org.eclipse.egit.github.core.Issue; import org.eclipse.egit.github.core.Label; import org.eclipse.egit.github.core.Milestone; import org.eclipse.egit.github.core.RepositoryId; import org.eclipse.egit.github.core.User; import org.eclipse.egit.github.core.client.GitHubClient; import org.eclipse.egit.github.core.service.IssueService; import org.eclipse.egit.github.core.service.LabelService; import org.eclipse.mylyn.internal.github.core.GitHub; import org.eclipse.mylyn.internal.github.core.GitHubTaskDataHandler; import org.eclipse.mylyn.tasks.core.ITaskMapping; import org.eclipse.mylyn.tasks.core.RepositoryResponse; import org.eclipse.mylyn.tasks.core.RepositoryResponse.ResponseKind; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.core.data.TaskAttribute; import org.eclipse.mylyn.tasks.core.data.TaskData; /** * GitHub issue task data handler */ public class IssueTaskDataHandler extends GitHubTaskDataHandler { private static final String DATA_VERSION = "1"; //$NON-NLS-1$ private static final String MILESTONE_NONE_KEY = "0"; //$NON-NLS-1$ private final IssueConnector connector; /** * Create GitHub issue task data handler for connector * * @param connector */ public IssueTaskDataHandler(IssueConnector connector) { this.connector = connector; } /** * Create task data * * @param repository * @param monitor * @param user * @param project * @param issue * @return task data */ public TaskData createTaskData(TaskRepository repository, IProgressMonitor monitor, String user, String project, Issue issue) { String key = Integer.toString(issue.getNumber()); TaskData data = new TaskData(getAttributeMapper(repository), IssueConnector.KIND, repository.getRepositoryUrl(), key); data.setVersion(DATA_VERSION); createOperations(data, issue); createAttribute(data, IssueAttribute.KEY.getMetadata(), key); createAttribute(data, IssueAttribute.TITLE.getMetadata(), issue.getTitle()); createAttribute(data, IssueAttribute.BODY.getMetadata(), issue.getBody()); createAttribute(data, IssueAttribute.STATUS.getMetadata(), issue.getState()); createAttribute(data, IssueAttribute.CREATION_DATE.getMetadata(), issue.getCreatedAt()); createAttribute(data, IssueAttribute.MODIFICATION_DATE.getMetadata(), issue.getUpdatedAt()); createAttribute(data, IssueAttribute.CLOSED_DATE.getMetadata(), issue.getClosedAt()); User reporter = issue.getUser(); createAttribute(data, IssueAttribute.REPORTER.getMetadata(), reporter, repository); String reporterGravatar = reporter != null ? reporter.getAvatarUrl() : null; createAttribute(data, IssueAttribute.REPORTER_GRAVATAR.getMetadata(), reporterGravatar); User assignee = issue.getAssignee(); createAttribute(data, IssueAttribute.ASSIGNEE.getMetadata(), assignee, repository); String assigneeGravatar = assignee != null ? assignee.getAvatarUrl() : null; createAttribute(data, IssueAttribute.ASSIGNEE_GRAVATAR.getMetadata(), assigneeGravatar); createAttribute(data, IssueAttribute.COMMENT_NEW.getMetadata()); createLabels(repository, data, issue); createMilestones(repository, data, issue); return data; } private void createMilestones(TaskRepository repository, TaskData data, Issue issue) { Milestone current = issue.getMilestone(); String number = current != null ? Integer.toString(current.getNumber()) : MILESTONE_NONE_KEY; TaskAttribute milestoneAttribute = createAttribute(data, IssueAttribute.MILESTONE.getMetadata(), number); if (!connector.hasCachedMilestones(repository)) try { connector.refreshMilestones(repository); } catch (CoreException ignore) { // Ignored } List<Milestone> cachedMilestones = connector.getMilestones(repository); milestoneAttribute.putOption(MILESTONE_NONE_KEY, Messages.IssueAttribute_MilestoneNone); for (Milestone milestone : cachedMilestones) milestoneAttribute.putOption( Integer.toString(milestone.getNumber()), milestone.getTitle()); } private void createLabels(TaskRepository repository, TaskData data, Issue issue) { TaskAttribute labels = createAttribute(data, IssueAttribute.LABELS, issue.getLabels()); if (!connector.hasCachedLabels(repository)) try { connector.refreshLabels(repository); } catch (CoreException ignore) { // Ignored } List<Label> cachedLabels = connector.getLabels(repository); for (Label label : cachedLabels) labels.putOption(label.getName(), label.getName()); } private void createOperations(TaskData data, Issue issue) { createOperationAttribute(data); if (!data.isNew()) { String state = issue.getState(); if (state != null) { addOperation(data, issue, IssueOperation.LEAVE, true); if (state.equals(IssueService.STATE_OPEN)) addOperation(data, issue, IssueOperation.CLOSE, false); else if (state.equals(IssueService.STATE_CLOSED)) addOperation(data, issue, IssueOperation.REOPEN, false); } } } private void addOperation(TaskData data, Issue issue, IssueOperation operation, boolean isDefault) { String id = operation.getId(); String label = createOperationLabel(issue, operation); addOperation(data, id, label, isDefault); } private String createOperationLabel(Issue issue, IssueOperation operation) { return operation == IssueOperation.LEAVE ? operation.getLabel() + issue.getState() : operation.getLabel(); } /** * Create task data for issue * * @param repository * @param monitor * @param user * @param project * @param issue * @param comments * @return task data */ public TaskData createTaskData(TaskRepository repository, IProgressMonitor monitor, String user, String project, Issue issue, List<Comment> comments) { TaskData taskData = createTaskData(repository, monitor, user, project, issue); taskData.setPartial(false); addComments(taskData.getRoot(), comments, repository); return taskData; } private Issue createIssue(TaskData taskData) { Issue issue = new Issue(); if (!taskData.isNew()) issue.setNumber(Integer.parseInt(taskData.getTaskId())); issue.setBody(getAttributeValue(taskData, IssueAttribute.BODY.getMetadata())); issue.setTitle(getAttributeValue(taskData, IssueAttribute.TITLE.getMetadata())); String assigneeValue = getAttributeValue(taskData, IssueAttribute.ASSIGNEE.getMetadata()); if (assigneeValue != null) { if (assigneeValue.trim().length() == 0) assigneeValue = null; User assignee = new User().setLogin(assigneeValue); issue.setAssignee(assignee); } String milestoneValue = getAttributeValue(taskData, IssueAttribute.MILESTONE.getMetadata()); if (milestoneValue != null) { Milestone milestone = new Milestone(); if (milestoneValue.length() > 0) milestone.setNumber(Integer.parseInt(milestoneValue)); issue.setMilestone(milestone); } return issue; } private TaskAttribute createAttribute(TaskData data, IssueAttribute attribute, List<Label> values) { TaskAttribute attr = createAttribute(data, attribute.getMetadata()); if (values != null) { List<String> labels = new ArrayList<String>(values.size()); for (Label label : values) labels.add(label.getName()); data.getAttributeMapper().setValues(attr, labels); } return attr; } @Override public boolean initializeTaskData(TaskRepository repository, TaskData data, ITaskMapping initializationData, IProgressMonitor monitor) throws CoreException { data.setVersion(DATA_VERSION); for (IssueAttribute attr : IssueAttribute.values()) if (attr.getMetadata().isInitTask()) createAttribute(data, attr.getMetadata(), (String) null); Issue dummy = new Issue(); createLabels(repository, data, dummy); createMilestones(repository, data, dummy); return true; } /** * Create any new labels that have been added to the issue and set the * issues labels to the current value of labels attribute. * * @param user * @param repo * @param client * @param repository * @param data * @param oldAttributes * @param issue */ protected void updateLabels(String user, String repo, GitHubClient client, TaskRepository repository, TaskData data, Set<TaskAttribute> oldAttributes, Issue issue) { TaskAttribute labelsAttribute = data.getRoot().getAttribute( IssueAttribute.LABELS.getMetadata().getId()); if (oldAttributes.contains(labelsAttribute) || data.isNew()) { LabelService labelService = new LabelService(client); if (!connector.hasCachedLabels(repository)) try { connector.refreshLabels(repository); } catch (CoreException ignore) { // Ignore } List<Label> currentLabels = connector.getLabels(repository); List<Label> newLabels = new LinkedList<Label>(); List<Label> labels = new LinkedList<Label>(); for (String value : labelsAttribute.getValues()) { Label label = new Label().setName(value); if (!currentLabels.contains(label)) newLabels.add(label); labels.add(label); } issue.setLabels(labels); for (Label label : newLabels) try { labelService.createLabel(user, repo, label); } catch (IOException e) { // TODO detect failure and handle label already created } if (!newLabels.isEmpty()) try { connector.refreshLabels(repository); } catch (CoreException ignore) { // Ignore } } } @Override public RepositoryResponse postTaskData(TaskRepository repository, TaskData taskData, Set<TaskAttribute> oldAttributes, IProgressMonitor monitor) throws CoreException { String taskId = taskData.getTaskId(); Issue issue = createIssue(taskData); RepositoryId repo = GitHub.getRepository(repository.getRepositoryUrl()); try { GitHubClient client = IssueConnector.createClient(repository); boolean collaborator = isCollaborator(client, repo); if (collaborator) updateLabels(repo.getOwner(), repo.getName(), client, repository, taskData, oldAttributes, issue); IssueService service = new IssueService(client); if (taskData.isNew()) { issue.setState(IssueService.STATE_OPEN); issue = service.createIssue(repo.getOwner(), repo.getName(), issue); taskId = Integer.toString(issue.getNumber()); } else { // Handle new comment String comment = getAttributeValue(taskData, IssueAttribute.COMMENT_NEW.getMetadata()); if (comment != null && comment.length() > 0) service.createComment(repo.getOwner(), repo.getName(), taskId, comment); boolean reporter = attributeMatchesUser(client, IssueAttribute.REPORTER.getMetadata(), taskData); if (collaborator || reporter) { // Handle state change TaskAttribute operationAttribute = taskData.getRoot() .getAttribute(TaskAttribute.OPERATION); if (operationAttribute != null) { IssueOperation operation = IssueOperation .fromId(operationAttribute.getValue()); if (operation == IssueOperation.REOPEN) issue.setState(IssueService.STATE_OPEN); else if (operation == IssueOperation.CLOSE) issue.setState(IssueService.STATE_CLOSED); } service.editIssue(repo.getOwner(), repo.getName(), issue); } } return new RepositoryResponse( taskData.isNew() ? ResponseKind.TASK_CREATED : ResponseKind.TASK_UPDATED, taskId); } catch (IOException e) { throw new CoreException(GitHub.createWrappedStatus(e)); } } }