Merge branch 'mikhail.golubev/configurable-issues-states'
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Tue, 24 Feb 2015 11:57:47 +0000 (14:57 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Tue, 24 Feb 2015 12:28:27 +0000 (15:28 +0300)
Conflicts:
plugins/tasks/tasks-tests/test/com/intellij/tasks/integration/JiraIntegrationTest.java

1  2 
plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/rest/api20alpha1/JiraRestApi20Alpha1.java
plugins/tasks/tasks-tests/test/com/intellij/tasks/integration/JiraIntegrationTest.java

index 7de17945ac318da643b832be0dcfbcd2abcabe4a,77c42205f3f968f4e0b3706882cc9c8de04db545..bc70d239ce733b6c8daa5de19678336e89ccbb10
@@@ -2,9 -2,7 +2,10 @@@ package com.intellij.tasks.jira.rest.ap
  
  import com.google.gson.reflect.TypeToken;
  import com.intellij.openapi.diagnostic.Logger;
 -import com.intellij.tasks.*;
++import com.intellij.tasks.CustomTaskState;
 +import com.intellij.tasks.LocalTask;
++import com.intellij.tasks.Task;
 +import com.intellij.tasks.TaskBundle;
- import com.intellij.tasks.TaskState;
  import com.intellij.tasks.jira.JiraRepository;
  import com.intellij.tasks.jira.rest.JiraRestApi;
  import com.intellij.tasks.jira.rest.JiraRestTask;
@@@ -24,12 -25,23 +28,24 @@@ import java.util.Set
   */
  public class JiraRestApi20Alpha1 extends JiraRestApi {
    private static final Logger LOG = Logger.getInstance(JiraRestApi20Alpha1.class);
-   private static final Type ISSUES_WRAPPER_TYPE = new TypeToken<JiraResponseWrapper.Issues<JiraIssueApi20Alpha1>>() { /* empty */ }.getType();
 -  private static final Type ISSUES_WRAPPER_TYPE = new TypeToken<JiraResponseWrapper.Issues<JiraIssueApi20Alpha1>>() { /* empty */
 -  }.getType();
++  private static final Type ISSUES_WRAPPER_TYPE = new TypeToken<JiraResponseWrapper.Issues<JiraIssueApi20Alpha1>>() {/* empty */}.getType();
  
    public JiraRestApi20Alpha1(JiraRepository repository) {
      super(repository);
    }
  
 -    result.add(CustomTaskState.fromPredefined(TaskState.IN_PROGRESS));
 -    result.add(CustomTaskState.fromPredefined(TaskState.RESOLVED));
 -    result.add(CustomTaskState.fromPredefined(TaskState.REOPENED));
+   @NotNull
+   @Override
+   public Set<CustomTaskState> getAvailableTaskStates(@NotNull Task task) throws Exception {
++    // REST API of JIRA 4.x for retrieving possible transitions is very limited: we can't fetch possible resolutions and
++    // names of transition destinations. So we have no other options than to hardcode them.
+     final HashSet<CustomTaskState> result = new HashSet<CustomTaskState>();
++    result.add(new CustomTaskState("4", "In Progress"));
++    result.add(new CustomTaskState("5", "Resolved (Fixed)"));
++    result.add(new CustomTaskState("3", "Reopened"));
+     return result;
+   }
    @Override
    protected JiraIssue parseIssue(String response) {
      return JiraRepository.GSON.fromJson(response, JiraIssueApi20Alpha1.class);
  
    @Nullable
    @Override
-   protected String getRequestForStateTransition(@NotNull TaskState state) {
-     switch (state) {
-       case IN_PROGRESS:
-         return  "{\"transition\": \"4\"}";
-       case RESOLVED:
-         // 5 for "Resolved", 2 for "Closed"
-         return  "{\"transition\": \"5\", \"resolution\": \"Fixed\"}";
-       case REOPENED:
-         return  "{\"transition\": \"3\"}";
-       default:
-         return null;
+   protected String getRequestForStateTransition(@NotNull CustomTaskState state) {
 -    // REST API of JIRA 4.x for retrieving possible transitions is very limited: we can't fetch possible resolutions and
 -    // names of transition destinations. So we have no other options than to hardcode them.
 -    final TaskState taskState = state.asPredefined();
 -    if (taskState != null) {
 -      switch (taskState) {
 -        case IN_PROGRESS:
 -          return  "{\"transition\": \"4\"}";
 -        case RESOLVED:
 -          // 5 for "Resolved", 2 for "Closed"
 -          return  "{\"transition\": \"5\", \"resolution\": \"Fixed\"}";
 -        case REOPENED:
 -          return  "{\"transition\": \"3\"}";
 -        default:
 -          return null;
++    try {
++      switch (Integer.parseInt(state.getId())) {
++        case 4: // In Progress
++          return "{\"transition\": \"4\"}";
++        case 5: // Resolved (2 for "Closed")
++          return "{\"transition\": \"5\", \"resolution\": \"Fixed\"}";
++        case 3: // Reopened
++          return "{\"transition\": \"3\"}";
+       }
+     }
++    catch (NumberFormatException ignored) {
 +    }
++    LOG.error("Unknown ID of predefined issue state: " + state.getId());
+     return null;
    }
  
++
    @Override
    public void updateTimeSpend(@NotNull LocalTask task, @NotNull String timeSpent, String comment) throws Exception {
      throw new Exception(TaskBundle.message("jira.failure.no.time.spent"));
index a4199be63b4077ee11b2f40d7993b986b0c05c75,24a3be38c265eecb60cd460e8aca9424acd9daee..67bbb0138f36d0f1396cf0784adc2485fa98f809
@@@ -133,74 -116,19 +130,74 @@@ public class JiraIntegrationTest extend
      }
    }
  
 -  // TODO move to on-Demand-specific tests
 -  //public void testBasicAuthenticationDisabling() throws Exception {
 -  //  assertTrue("Basic authentication should be enabled at first", myRepository.isUseHttpAuthentication());
 -  //  myRepository.findTask("PRJONE-1");
 -  //  assertFalse("Basic authentication should be disabled once JSESSIONID cookie was received", myRepository.isUseHttpAuthentication());
 -  //  HttpClient client = myRepository.getHttpClient();
 -  //  assertFalse(client.getParams().isAuthenticationPreemptive());
 -  //  assertNull(client.getState().getCredentials(AuthScope.ANY));
 -  //}
 +  // Our test servers poorly handles frequent state updates of single dedicated issue. As a workaround
 +  // we create new issue for every test run (via XML-RPC API in JIRA 4.x and REST API in JIRA 5+)
  
 -  public void testSetTaskState() throws Exception {
 -    changeStateAndCheck(JIRA_4_TEST_SERVER_URL, "PRJONE-8");
 -    changeStateAndCheck(JIRA_5_TEST_SERVER_URL, "UT-8");
 +  public void testSetTaskStateInJira5() throws Exception {
 +    myRepository.setUrl(JIRA_5_TEST_SERVER_URL);
 +    final String id = createIssueViaRestApi("BTSU", "Test issue for state updates (" + SHORT_TIMESTAMP_FORMAT.format(new Date()) + ")");
 +    changeTaskStateAndCheck(id);
 +  }
 +
 +  // We can use XML-RPC in JIRA 5+ too, but nonetheless it's useful to have REST-based implementation as well
 +  private String createIssueViaRestApi(@NotNull String project, @NotNull String summary) throws Exception {
 +    final HttpClient client = myRepository.getHttpClient();
 +    final PostMethod method = new PostMethod(myRepository.getUrl() + "/rest/api/latest/issue");
 +    try {
 +      // For simplicity assume that project, summary and username don't contain illegal characters
 +      @Language("JSON")
 +      final String json = "{\"fields\": {\n" +
 +                          "  \"project\": {\n" +
 +                          "    \"key\": \"" + project + "\"\n" +
 +                          "  },\n" +
 +                          "  \"issuetype\": {\n" +
 +                          "    \"name\": \"Bug\"\n" +
 +                          "  },\n" +
 +                          "  \"assignee\": {\n" +
 +                          "    \"name\": \"" + myRepository.getUsername() + "\"\n" +
 +                          "  },\n" +
 +                          "  \"summary\": \"" + summary + "\"\n" +
 +                          "}}";
 +      method.setRequestEntity(new StringRequestEntity(json, "application/json", "utf-8"));
 +      client.executeMethod(method);
 +      return new Gson().fromJson(method.getResponseBodyAsString(), JsonObject.class).get("id").getAsString();
 +    }
 +    finally {
 +      method.releaseConnection();
 +    }
 +  }
 +
 +  public void testSetTaskStateInJira4() throws Exception {
 +    myRepository.setUrl(JIRA_4_TEST_SERVER_URL);
 +    final String id = createIssueViaXmlRpc("BTSU", "Test issue for state updates (" + SHORT_TIMESTAMP_FORMAT.format(new Date()) + ")");
 +    changeTaskStateAndCheck(id);
 +  }
 +
 +  @SuppressWarnings("UseOfObsoleteCollectionType")
 +  @NotNull
 +  private String createIssueViaXmlRpc(@NotNull String project, @NotNull String summary) throws Exception {
 +    final URL url = new URL(myRepository.getUrl() + "/rpc/xmlrpc");
 +    final XmlRpcClient xmlRpcClient = new XmlRpcClient(url);
 +    final Map<String, Object> issue = new Hashtable<String, Object>();
 +    issue.put("summary", summary);
 +    issue.put("project", project);
 +    issue.put("assignee", myRepository.getUsername());
 +    issue.put("type", 1); // Bug
 +    issue.put("state", 1); // Open
 +
 +    final Vector<Object> params = new Vector<Object>(Arrays.asList("", issue)); // empty token because of HTTP basic auth
 +    final Hashtable result = (Hashtable)xmlRpcClient.execute(new XmlRpcRequest("jira1.createIssue", params),
 +                                                             new CommonsXmlRpcTransport(url, myRepository.getHttpClient()));
 +    return (String)result.get("key");
 +  }
 +
 +  private void changeTaskStateAndCheck(@NotNull String issueKey) throws Exception {
 +    final Task original = myRepository.findTask(issueKey);
 +    assertNotNull(original);
-     myRepository.setTaskState(original, TaskState.IN_PROGRESS);
++    myRepository.setTaskState(original, new CustomTaskState("4", "In Progress"));
 +    final Task updated = myRepository.findTask(issueKey);
 +    assertNotNull(updated);
 +    assertEquals(TaskState.IN_PROGRESS, updated.getState());
    }
  
    public void testSetTimeSpend() throws Exception {