Merge branch 'east825/IDEA-136499'
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Thu, 18 Feb 2016 16:11:32 +0000 (19:11 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Thu, 18 Feb 2016 16:11:32 +0000 (19:11 +0300)
12 files changed:
plugins/tasks/tasks-api/src/com/intellij/tasks/Task.java
plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java
plugins/tasks/tasks-core/src/com/intellij/tasks/actions/EditTaskDialog.java
plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskAutoCompletionListProvider.java
plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java
plugins/tasks/tasks-core/src/com/intellij/tasks/doc/TaskDocumentationProvider.java
plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabRepository.java
plugins/tasks/tasks-core/src/com/intellij/tasks/gitlab/GitlabTask.java
plugins/tasks/tasks-core/src/com/intellij/tasks/impl/LocalTaskImpl.java
plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java
plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java
plugins/tasks/tasks-tests/test/com/intellij/tasks/integration/GitlabIntegrationTest.java

index 6b7717fd73f1f2f67810dd94d55df1048222feb3..5ce47198299c1712f605b1c5374b907b1cb2dcf4 100644 (file)
@@ -33,15 +33,29 @@ public abstract class Task {
   /**
    * Global unique task identifier, e.g. IDEA-00001. It's important that its format is consistent with
    * {@link TaskRepository#extractId(String)}, because otherwise task won't be updated on its activation.
+   * Note that this ID is used to find issues and to compare them, so (ideally) it has to be unique.
+   * 
+   * In some cases task server doesn't offer such global ID (but, for instance, pair (project-name, per-project-id) instead) or it's not
+   * what users want to see in UI (e.g. notorious <tt>id</tt> and <tt>iid</tt> in Gitlab). In this case you should generate artificial ID 
+   * for internal usage and implement {@link #getPresentableId()}.
    *
    * @return unique global ID as described
    *
-   * @see com.intellij.tasks.TaskRepository#extractId(String)
-   * @see com.intellij.tasks.TaskManager#activateTask(Task, boolean)
+   * @see #getPresentableId()
+   * @see TaskRepository#extractId(String)
+   * @see TaskManager#activateTask(Task, boolean)
    */
   @NotNull
   public abstract String getId();
 
+
+  /**
+   * @return ID in the form that is suitable for commit messages, dialogs, completion items, etc.
+   */
+  @NotNull
+  public String getPresentableId() {
+    return getId();
+  }
   /**
    * Short task description.
    * @return description
@@ -100,7 +114,7 @@ public abstract class Task {
   public final String toString() {
     String text;
     if (isIssue()) {
-      text = getId() + ": " + getSummary();
+      text = getPresentableId() + ": " + getSummary();
     } else {
       text = getSummary();
     }
index 8051403ded0f0b055e10e99cd20af581a011c72a..8c4384b574dfbe28df4c925315dcbb0d199a687e 100644 (file)
@@ -352,7 +352,7 @@ public abstract class TaskRepository {
   @Nullable
   public String getTaskComment(@NotNull Task task) {
     return isShouldFormatCommitMessage()
-           ? myCommitMessageFormat.replace("{id}", task.getId()).replace("{summary}", task.getSummary())
+           ? myCommitMessageFormat.replace("{id}", task.getPresentableId()).replace("{summary}", task.getSummary())
            : null;
   }
 
index a25adf3907a702e30621b294a1856df9aac80a80..4d5daa9bc9afc484f2ebf2ad4fa867882de86efc 100644 (file)
@@ -59,7 +59,7 @@ public class EditTaskDialog extends DialogWrapper {
   protected EditTaskDialog(Project project, LocalTaskImpl task) {
     super(project);
     myTask = task;
-    setTitle("Edit Task " + (task.isIssue() ? task.getId() : ""));
+    setTitle("Edit Task " + (task.isIssue() ? task.getPresentableId() : ""));
 
 //    mySummary.putClientProperty(DialogWrapperPeer.HAVE_INITIAL_SELECTION, "");
     mySummary.setText(task.getSummary());
index 6d2598d2a0239859a81657b4bee9855c6c2a8d0a..9fa080f1cc45184c731176c99b5a0712f5226dd6 100644 (file)
@@ -103,7 +103,7 @@ public class TaskAutoCompletionListProvider extends TextFieldWithAutoCompletionL
   @NotNull
   @Override
   protected String getLookupString(@NotNull final Task task) {
-    return task.getId();
+    return task.getPresentableId();
   }
 
   @Override
index 6fb815e0edfc61af2fb98727e13abc15cc327125..ab3f60a045d2723c14bb82736824b2a6b196d4e8 100644 (file)
@@ -56,7 +56,7 @@ public class TaskSearchSupport {
     final Matcher matcher = getMatcher(pattern);
     return ContainerUtil.mapNotNull(tasks, new NullableFunction<Task, Task>() {
       public Task fun(Task task) {
-        return matcher.matches(task.getId()) || matcher.matches(task.getSummary()) ? task : null;
+        return matcher.matches(task.getPresentableId()) || matcher.matches(task.getSummary()) ? task : null;
       }
     });
   }
index 843a6fc7f7e20b2c58eb9193a9d2771a9b8da3bb..87d980bc21e169da770c8f57a3f086b394146a88 100644 (file)
@@ -51,7 +51,7 @@ public class TaskDocumentationProvider extends AbstractDocumentationProvider imp
     final Task task = ((TaskPsiElement)element).getTask();
     final StringBuilder builder = new StringBuilder();
     builder.append("<b>Summary:</b> ").append(task.getSummary()).append("<br>");
-    builder.append("<b>Id:</b> ").append(task.getId()).append("<br>");
+    builder.append("<b>Id:</b> ").append(task.getPresentableId()).append("<br>");
     if (task.getCreated() != null) {
       builder.append("<b>Created at:</b> ").append(task.getCreated()).append("<br>");
     }
index 40c42a4df2a32912f8423fd544bf118efa062022..8b850c2f76a68b014471fc64800763e552a8e924 100644 (file)
@@ -42,14 +42,17 @@ import static com.intellij.tasks.impl.httpclient.TaskResponseUtil.GsonSingleObje
 public class GitlabRepository extends NewBaseRepositoryImpl {
 
   @NonNls public static final String REST_API_PATH_PREFIX = "/api/v3/";
+  @NonNls private static final String TOKEN_HEADER = "PRIVATE-TOKEN";
+
   private static final Pattern ID_PATTERN = Pattern.compile("\\d+");
+  private static final Gson GSON = TaskGsonUtil.createDefaultBuilder().create();
 
-  public static final Gson GSON = TaskGsonUtil.createDefaultBuilder().create();
-  public static final TypeToken<List<GitlabProject>> LIST_OF_PROJECTS_TYPE = new TypeToken<List<GitlabProject>>() {
-  };
-  public static final TypeToken<List<GitlabIssue>> LIST_OF_ISSUES_TYPE = new TypeToken<List<GitlabIssue>>() {
-  };
-  public static final GitlabProject UNSPECIFIED_PROJECT = new GitlabProject() {
+  // @formatter:off
+  private static final TypeToken<List<GitlabProject>> LIST_OF_PROJECTS_TYPE = new TypeToken<List<GitlabProject>>() {};
+  private static final TypeToken<List<GitlabIssue>> LIST_OF_ISSUES_TYPE = new TypeToken<List<GitlabIssue>>() {};
+  // @formatter:on
+
+  static final GitlabProject UNSPECIFIED_PROJECT = new GitlabProject() {
     @Override
     public String getName() {
       return "-- all issues created by you --";
@@ -88,7 +91,7 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
   @Override
   public boolean equals(Object o) {
     if (!super.equals(o)) return false;
-    GitlabRepository repository = (GitlabRepository)o;
+    final GitlabRepository repository = (GitlabRepository)o;
     if (!Comparing.equal(myCurrentProject, repository.myCurrentProject)) return false;
     return true;
   }
@@ -114,9 +117,7 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
   @Override
   public Task findTask(@NotNull String id) throws Exception {
     // doesn't work now, because Gitlab's REST API doesn't provide endpoint to find task
-    // by its global ID, only by project ID and task's local ID (iid).
-    //GitlabIssue issue = fetchIssue(Integer.parseInt(id));
-    //return issue == null ? null : new GitlabTask(this, issue);
+    // using only its global ID, it requires both task's global ID AND task's project ID
     return null;
   }
 
@@ -155,7 +156,7 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
   @SuppressWarnings("UnusedDeclaration")
   @NotNull
   public GitlabProject fetchProject(int id) throws Exception {
-    HttpGet request = new HttpGet(getRestApiUrl("project", id));
+    final HttpGet request = new HttpGet(getRestApiUrl("project", id));
     return getHttpClient().execute(request, new GsonSingleObjectDeserializer<GitlabProject>(GSON, GitlabProject.class));
   }
 
@@ -182,12 +183,14 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
     return getRestApiUrl("issues");
   }
 
-  @SuppressWarnings("UnusedDeclaration")
+  /**
+   * @param issueId global issue's ID (<tt>id</tt> field, not <tt>iid</tt>)
+   */
   @Nullable
-  public GitlabIssue fetchIssue(int id) throws Exception {
+  public GitlabIssue fetchIssue(int projectId, int issueId) throws Exception {
     ensureProjectsDiscovered();
-    HttpGet request = new HttpGet(getRestApiUrl("issues", id));
-    ResponseHandler<GitlabIssue> handler = new GsonSingleObjectDeserializer<GitlabIssue>(GSON, GitlabIssue.class, true);
+    final HttpGet request = new HttpGet(getRestApiUrl("projects", projectId, "issues", issueId));
+    final ResponseHandler<GitlabIssue> handler = new GsonSingleObjectDeserializer<GitlabIssue>(GSON, GitlabIssue.class, true);
     return getHttpClient().execute(request, handler);
   }
 
@@ -223,7 +226,7 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
     return new HttpRequestInterceptor() {
       @Override
       public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
-        request.addHeader("PRIVATE-TOKEN", myPassword);
+        request.addHeader(TOKEN_HEADER, myPassword);
         //request.addHeader("Accept", "application/json");
       }
     };
index 335cd9514eb83c3f797b63d8125e693357cf3eb6..a6b04356df59c24cf5c32f66b20da386fcc6c1fa 100644 (file)
@@ -37,9 +37,17 @@ public class GitlabTask extends Task {
   @NotNull
   @Override
   public String getId() {
+    // Will be in form <projectId>:<issueId>
+    //return myIssue.getProjectId() + ":" + myIssue.getId();
     return String.valueOf(myIssue.getId());
   }
 
+  @NotNull
+  @Override
+  public String getPresentableId() {
+    return "#" + myIssue.getLocalId();
+  }
+
   @NotNull
   @Override
   public String getSummary() {
index 50018ce09deaf8d70a5383f02d2ff3c2839432e7..0f96aed5c2f8c72cdc2173c781117278d75ca63e 100644 (file)
@@ -57,6 +57,7 @@ public class LocalTaskImpl extends LocalTask {
 
   private String myProject = null;
   private String myNumber = "";
+  private String myPresentableId = "";
 
   private boolean myIssue = false;
   private TaskRepository myRepository = null;
@@ -161,6 +162,7 @@ public class LocalTaskImpl extends LocalTask {
 
     myProject = issue.getProject();
     myNumber = issue.getNumber();
+    myPresentableId = issue.getPresentableId();
   }
 
   public void setId(String id) {
@@ -394,4 +396,15 @@ public class LocalTaskImpl extends LocalTask {
   public void setProject(@Nullable String project) {
     myProject = project;
   }
+
+  public void setPresentableId(@NotNull String presentableId) {
+    myPresentableId = presentableId;
+  }
+
+  @NotNull
+  @Override
+  public String getPresentableId() {
+    // Use global ID for compatibility
+    return StringUtil.isEmpty(myPresentableId) ? getId() : myPresentableId;
+  }
 }
index bc0ccb8e886cd5f0a817ff8b0f219caf79e700c5..0e4dcbb397616ed9b5ce32018fe664662a8f48ad 100644 (file)
@@ -497,7 +497,7 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe
     if (task.isIssue()) {
       StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
         public void run() {
-          ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Backgroundable(myProject, "Updating " + task.getId()) {
+          ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Backgroundable(myProject, "Updating " + task.getPresentableId()) {
 
             public void run(@NotNull ProgressIndicator indicator) {
               updateIssue(task.getId());
index b7db9b9a9f684f0510d57a57dd5abc7d9831af68..74bc704393ed7c6f38b3b403281d9f2390d7ac00 100644 (file)
@@ -72,7 +72,7 @@ public class TaskUtil {
 
   public static String formatTask(@NotNull Task task, String format) {
     return format
-      .replace("{id}", task.getId())
+      .replace("{id}", task.getPresentableId())
       .replace("{number}", task.getNumber())
       .replace("{project}", StringUtil.notNullize(task.getProject()))
       .replace("{summary}", task.getSummary());
@@ -90,7 +90,7 @@ public class TaskUtil {
   public static String getTrimmedSummary(Task task) {
     String text;
     if (task.isIssue()) {
-      text = task.getId() + ": " + task.getSummary();
+      text = task.getPresentableId() + ": " + task.getSummary();
     }
     else {
       text = task.getSummary();
index 3e5af94f2f53109c381c73859dacf97132528823..a593989060f61a8042d39da40090cba666eda057 100644 (file)
@@ -15,7 +15,6 @@ import com.intellij.util.containers.ContainerUtil;
 import java.util.Collections;
 
 /**
- * TODO: install Gitlab on server and add more functional tests
  * @author Mikhail Golubev
  */
 public class GitlabIntegrationTest extends TaskManagerTestCase {
@@ -48,14 +47,13 @@ public class GitlabIntegrationTest extends TaskManagerTestCase {
 
     LocalTaskImpl localTask = new LocalTaskImpl(new GitlabTask(myRepository, issue));
     String changeListComment = TaskUtil.getChangeListComment(localTask);
-    assertEquals("project-1 2 1 Sample title", changeListComment);
+    assertEquals("project-1 2 #2 Sample title", changeListComment);
 
     myRepository.setProjects(Collections.<GitlabProject>emptyList());
     localTask = new LocalTaskImpl(new GitlabTask(myRepository, issue));
     changeListComment = TaskUtil.getChangeListComment(localTask);
     // Project is unknown, so "" is substituted instead
-    assertEquals(" 2 1 Sample title", changeListComment);
-
+    assertEquals(" 2 #2 Sample title", changeListComment);
   }
 
   public void testIssueFilteringByState() throws Exception {
@@ -74,6 +72,25 @@ public class GitlabIntegrationTest extends TaskManagerTestCase {
     assertEquals("Opened issue #1", openedIssues[0].getSummary());
   }
 
+  // IDEA-136499
+  public void testPresentableId() throws Exception {
+    final GitlabIssue issue = myRepository.fetchIssue(5 /* ID Formatting Tests */, 10);
+    assertNotNull(issue);
+    assertEquals(10, issue.getId());
+    assertEquals(1, issue.getLocalId());
+    assertEquals(5, issue.getProjectId());
+
+    final GitlabTask task = new GitlabTask(myRepository, issue);
+    assertEquals("#1", task.getPresentableId());
+    assertEquals("1", task.getNumber());
+    assertEquals("ID Formatting Tests", task.getProject());
+    assertEquals("10", task.getId());
+    assertEquals("#1: First issue with iid = 1", task.toString());
+    myRepository.setShouldFormatCommitMessage(true);
+    assertEquals("#1 First issue with iid = 1", myRepository.getTaskComment(task));
+  }
+
+
   @Override
   public void setUp() throws Exception {
     super.setUp();