/*******************************************************************************
* Copyright (c) 2014, Paul Weingardt
* 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:
* Paul Weingardt - initial API and implementation
*******************************************************************************/
package de.weingardt.mylyn.gitlab.core;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.tasks.core.ITask.PriorityLevel;
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.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.TaskCommentMapper;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskOperation;
import org.gitlab.api.GitlabAPI;
import org.gitlab.api.models.GitlabIssue;
import org.gitlab.api.models.GitlabMilestone;
import org.gitlab.api.models.GitlabNote;
import org.gitlab.api.models.GitlabProjectMember;
import de.weingardt.mylyn.gitlab.core.exceptions.GitlabException;
/**
* Handles the issues. It maps the attributes from the GitlabAPI to real Mylyn issue attributes,
* downloads the issues for a specific task repository, and creates new issues.
* @author paul
*
*/
public class GitlabTaskDataHandler extends AbstractTaskDataHandler {
private static Pattern priorityPattern = Pattern.compile("(?:priority:)?(high|normal|low)");
private static Pattern typePattern = Pattern.compile("(?:type:)?(bug|feature|story)");
public GitlabTaskDataHandler() {
}
@Override
public TaskAttributeMapper getAttributeMapper(TaskRepository repository) {
try {
return ConnectionManager.get(repository).mapper;
} catch (CoreException e) {
throw new Error(e);
}
}
@Override
public boolean initializeTaskData(TaskRepository repository, TaskData data,
ITaskMapping mapping, IProgressMonitor monitor) throws CoreException {
createDefaultAttributes(data, false);
GitlabConnection connection = ConnectionManager.get(repository);
TaskAttribute root = data.getRoot();
root.getAttribute(GitlabAttribute.PROJECT.getTaskKey()).setValue(connection.project.getName());
root.getAttribute(GitlabAttribute.LABELS.getTaskKey()).setValue("");
root.getAttribute(GitlabAttribute.STATUS.getTaskKey()).setValue("open");
root.getAttribute(GitlabAttribute.MILESTONE.getTaskKey()).setValue("");
return true;
}
@Override
public RepositoryResponse postTaskData(TaskRepository repository, TaskData data,
Set<TaskAttribute> attributes, IProgressMonitor monitor)
throws CoreException {
GitlabAttributeMapper attributeMapper = (GitlabAttributeMapper) data.getAttributeMapper();
TaskAttribute root = data.getRoot();
String labels = root.getAttribute(GitlabAttribute.LABELS.getTaskKey()).getValue();
String title = root.getAttribute(GitlabAttribute.TITLE.getTaskKey()).getValue();
String body = root.getAttribute(GitlabAttribute.BODY.getTaskKey()).getValue();
Integer assigneeId = 0;
// We have to check, if the assignee has changed. The gitlab api leaves three posiblities for the assignee ID:
// 0: leave as it is
// -1: unassign
// real id: assign
// If we didnt do this, Gitlab would create a comment everytime we edit the issue and there is still no
// assignee
for(TaskAttribute a : attributes) {
if(a.getId().equals(GitlabAttribute.ASSIGNEE.getTaskKey())) {
GitlabProjectMember assignee = attributeMapper.findProjectMemberByName(
root.getAttribute(GitlabAttribute.ASSIGNEE.getTaskKey()).getValue());
assigneeId = (assignee == null ? -1 : assignee.getId());
}
}
GitlabMilestone milestone = attributeMapper.findMilestoneByName(
root.getAttribute(GitlabAttribute.MILESTONE.getTaskKey()).getValue());
Integer milestoneId = (milestone == null ? 0 : milestone.getId());
GitlabConnection connection = ConnectionManager.get(repository);
GitlabAPI api = connection.api();
try {
monitor.beginTask("Uploading task", IProgressMonitor.UNKNOWN);
GitlabIssue issue = null;
if(data.isNew()) {
issue = api.createIssue(connection.project.getId(), assigneeId, milestoneId, labels, body, title);
return new RepositoryResponse(ResponseKind.TASK_CREATED, "" + issue.getId());
} else {
if(root.getAttribute(TaskAttribute.COMMENT_NEW) != null &&
!root.getAttribute(TaskAttribute.COMMENT_NEW).getValue().equals("")) {
api.createNote(connection.project.getId(), GitlabConnector.getTicketId(data.getTaskId()),
root.getAttribute(TaskAttribute.COMMENT_NEW).getValue());
}
String action = root.getAttribute(TaskAttribute.OPERATION).getValue();
issue = api.editIssue(connection.project.getId(), GitlabConnector.getTicketId(data.getTaskId()), assigneeId,
milestoneId, labels, body, title, GitlabAction.find(action).getGitlabIssueAction());
return new RepositoryResponse(ResponseKind.TASK_UPDATED, "" + issue.getId());
}
} catch (IOException e) {
throw new GitlabException("Unknown connection error!");
} finally {
monitor.done();
}
}
public TaskData downloadTaskData(TaskRepository repository, Integer ticketId) throws CoreException {
try {
GitlabConnection connection = ConnectionManager.get(repository);
GitlabAPI api = connection.api();
GitlabIssue issue = api.getIssue(connection.project.getId(), ticketId);
List<GitlabNote> notes = api.getNotes(issue);
return createTaskDataFromGitlabIssue(issue, repository, notes);
} catch (IOException e) {
throw new GitlabException("Unknown connection error!");
}
}
public TaskData createTaskDataFromGitlabIssue(GitlabIssue issue, TaskRepository repository,
List<GitlabNote> notes) throws CoreException {
GitlabConnection connection = ConnectionManager.get(repository);
TaskData data = new TaskData(connection.mapper, GitlabPluginCore.CONNECTOR_KIND, repository.getUrl(),
"" + issue.getId());
String labels = StringUtils.join(issue.getLabels(), ", ");
createDefaultAttributes(data, true);
TaskAttribute root = data.getRoot();
root.getAttribute(GitlabAttribute.AUTHOR.getTaskKey()).setValue(issue.getAuthor().getName());
root.getAttribute(GitlabAttribute.CREATED.getTaskKey()).setValue("" + issue.getCreatedAt().getTime());
root.getAttribute(GitlabAttribute.BODY.getTaskKey()).setValue(issue.getDescription() == null ? "" : issue.getDescription());
root.getAttribute(GitlabAttribute.LABELS.getTaskKey()).setValue(labels);
root.getAttribute(GitlabAttribute.PROJECT.getTaskKey()).setValue(connection.project.getName());
root.getAttribute(GitlabAttribute.STATUS.getTaskKey()).setValue(issue.getState());
root.getAttribute(GitlabAttribute.TITLE.getTaskKey()).setValue(issue.getTitle());
root.getAttribute(GitlabAttribute.IID.getTaskKey()).setValue("" + issue.getIid());
root.getAttribute(GitlabAttribute.PRIORITY.getTaskKey()).setValue(getPriority(labels));
root.getAttribute(GitlabAttribute.TYPE.getTaskKey()).setValue(getType(labels));
if(issue.getMilestone() != null) {
root.getAttribute(GitlabAttribute.MILESTONE.getTaskKey()).setValue(issue.getMilestone().getTitle());
}
if(issue.getUpdatedAt() != null) {
root.getAttribute(GitlabAttribute.UPDATED.getTaskKey()).setValue("" + issue.getUpdatedAt().getTime());
}
if(issue.getState().equals(GitlabIssue.StateClosed)) {
root.getAttribute(GitlabAttribute.COMPLETED.getTaskKey()).setValue("" + issue.getUpdatedAt().getTime());
}
if(issue.getAssignee() != null) {
root.getAttribute(GitlabAttribute.ASSIGNEE.getTaskKey()).setValue(issue.getAssignee().getName());
}
Collections.sort(notes, new Comparator<GitlabNote>() {
@Override
public int compare(GitlabNote o1, GitlabNote o2) {
return o1.getCreatedAt().compareTo(o2.getCreatedAt());
}
});
for (int i = 0; i < notes.size(); i++) {
TaskCommentMapper cmapper = new TaskCommentMapper();
cmapper.setAuthor(repository.createPerson(notes.get(i).getAuthor().getName()));
cmapper.setCreationDate(notes.get(i).getCreatedAt());
cmapper.setText(notes.get(i).getBody());
cmapper.setNumber(i + 1);
TaskAttribute attribute = data.getRoot().createAttribute(TaskAttribute.PREFIX_COMMENT + (i + 1));
cmapper.applyTo(attribute);
}
GitlabAction[] actions = GitlabAction.getActions(issue);
for(int i = 0; i < actions.length; ++i) {
GitlabAction action = actions[i];
TaskAttribute attribute = data.getRoot().createAttribute(TaskAttribute.PREFIX_OPERATION + action.label);
TaskOperation.applyTo(attribute, action.label, action.label);
}
return data;
}
private void createDefaultAttributes(TaskData data, boolean existingTask) {
createAttribute(data, GitlabAttribute.BODY);
createAttribute(data, GitlabAttribute.TITLE);
createAttribute(data, GitlabAttribute.LABELS);
createAttribute(data, GitlabAttribute.STATUS);
createAttribute(data, GitlabAttribute.PROJECT);
createAttribute(data, GitlabAttribute.CREATED);
createAttribute(data, GitlabAttribute.COMPLETED);
createAttribute(data, GitlabAttribute.UPDATED);
createAttribute(data, GitlabAttribute.ASSIGNEE);
createAttribute(data, GitlabAttribute.MILESTONE);
createAttribute(data, GitlabAttribute.IID);
createAttribute(data, GitlabAttribute.PRIORITY);
createAttribute(data, GitlabAttribute.TYPE);
data.getRoot().getAttribute(GitlabAttribute.CREATED.getTaskKey()).setValue("" + (new Date().getTime()));
if(existingTask) {
data.getRoot().createAttribute(TaskAttribute.COMMENT_NEW)
.getMetaData().setType(TaskAttribute.TYPE_LONG_RICH_TEXT)
.setReadOnly(false);
createAttribute(data, GitlabAttribute.AUTHOR);
}
TaskAttribute operation = data.getRoot().createAttribute(TaskAttribute.OPERATION);
operation.getMetaData().setType(TaskAttribute.TYPE_OPERATION);
}
private void createAttribute(TaskData data, GitlabAttribute attribute) {
TaskAttribute attr = data.getRoot().createAttribute(attribute.getTaskKey());
TaskAttributeMetaData metaData = attr.getMetaData();
metaData.setType(attribute.getType());
metaData.setKind(attribute.getKind());
metaData.setLabel(attribute.toString());
metaData.setReadOnly(attribute.isReadOnly());
}
/**
* Returns the Priority String for Mylyn. Uses a regular expression to check
* for priorities in the given label.
* @param labels
* @return
*/
private String getPriority(String labels) {
Matcher m = priorityPattern.matcher(labels);
if(m.find()) {
String p = m.group(1);
if(p.equals("high")) {
return PriorityLevel.P1.toString();
} else if(p.equals("low")) {
return PriorityLevel.P5.toString();
}
}
return PriorityLevel.P3.toString();
}
/**
* Returns the type string for Mylyn. Uses a regular expression to check
* for types in the given label.
* @param labels
* @return
*/
private String getType(String labels) {
Matcher m = typePattern.matcher(labels);
if(m.find()) {
return m.group(1);
}
return "";
}
}