]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5111 Refactored some ws to add response examples
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 30 Apr 2014 10:24:17 +0000 (12:24 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 30 Apr 2014 10:24:17 +0000 (12:24 +0200)
88 files changed:
sonar-server/src/main/java/org/sonar/server/authentication/ws/AuthenticationWs.java
sonar-server/src/main/java/org/sonar/server/issue/ActionPlanService.java [deleted file]
sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java
sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/filter/AppAction.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/filter/FavoritesAction.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWriter.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWs.java
sonar-server/src/main/java/org/sonar/server/issue/filter/ShowAction.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/ws/ActionPlanWs.java [deleted file]
sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java [deleted file]
sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java
sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
sonar-server/src/main/resources/org/sonar/server/authentication/ws/example-validate.json [new file with mode: 0644]
sonar-server/src/main/resources/org/sonar/server/issue/actionplan/example-search.json [new file with mode: 0644]
sonar-server/src/main/resources/org/sonar/server/issue/filter/example-show.json [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
sonar-server/src/test/java/org/sonar/server/authentication/ws/AuthenticationWsTest.java
sonar-server/src/test/java/org/sonar/server/issue/ActionPlanServiceTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java
sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java
sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanWsTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/filter/AppActionTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/filter/FavoritesActionTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWriterTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWsTest.java
sonar-server/src/test/java/org/sonar/server/issue/filter/ShowActionTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/ws/ActionPlanWsTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java
sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java
sonar-server/src/test/java/org/sonar/server/ws/ListingWsTest.java
sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/anonymous_page.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page_with_favorites.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page_with_selected_filter.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/selected_filter_can_not_be_modified.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/selected_filter_can_not_be_modified.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/show_filter.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/filter/ShowActionTest/show_filter.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_on_removed_component.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_on_removed_project_and_component.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_action_plan.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_changelog.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_characteristics.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_comments.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_dates.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_sub_project.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_technical_debt.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_transitions.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_users.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_on_removed_component.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_on_removed_project_and_component.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_action_plan.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_changelog.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_characteristics.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_dates.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_set_severity_action.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_sub_project.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_technical_debt.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_transitions.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_users.json [deleted file]
sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json [deleted file]

index b39bcb398b3eadde565bf2a2357d6197ceaf338e..6de8f94300505aaeac819b21eccbf0884a4c8492 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.sonar.server.authentication.ws;
 
+import com.google.common.io.Resources;
 import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
 
@@ -33,7 +34,8 @@ public class AuthenticationWs implements WebService {
     controller.createAction("validate")
       .setDescription("Check credentials")
       .setSince("3.3")
-      .setHandler(RailsHandler.INSTANCE);
+      .setHandler(RailsHandler.INSTANCE)
+      .setResponseExample(Resources.getResource(this.getClass(), "example-validate.json"));
 
     controller.done();
   }
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ActionPlanService.java b/sonar-server/src/main/java/org/sonar/server/issue/ActionPlanService.java
deleted file mode 100644 (file)
index 4ddaec8..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.server.issue;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.issue.IssueQuery;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.issue.ActionPlanDeadlineComparator;
-import org.sonar.core.issue.ActionPlanStats;
-import org.sonar.core.issue.DefaultActionPlan;
-import org.sonar.core.issue.IssueUpdater;
-import org.sonar.core.issue.db.*;
-import org.sonar.core.resource.ResourceDao;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceQuery;
-import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.user.UserSession;
-
-import javax.annotation.CheckForNull;
-
-import java.util.*;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * @since 3.6
- */
-public class ActionPlanService implements ServerComponent {
-
-  private final ActionPlanDao actionPlanDao;
-  private final ActionPlanStatsDao actionPlanStatsDao;
-  private final ResourceDao resourceDao;
-  private final IssueDao issueDao;
-  private final IssueUpdater issueUpdater;
-  private final IssueStorage issueStorage;
-
-  public ActionPlanService(ActionPlanDao actionPlanDao, ActionPlanStatsDao actionPlanStatsDao, ResourceDao resourceDao,
-                           IssueDao issueDao, IssueUpdater issueUpdater, IssueStorage issueStorage) {
-    this.actionPlanDao = actionPlanDao;
-    this.actionPlanStatsDao = actionPlanStatsDao;
-    this.resourceDao = resourceDao;
-    this.issueDao = issueDao;
-    this.issueUpdater = issueUpdater;
-    this.issueStorage = issueStorage;
-  }
-
-  public ActionPlan create(ActionPlan actionPlan, UserSession userSession) {
-    ResourceDto project = findProject(actionPlan.projectKey());
-    checkUserIsProjectAdministrator(project.getKey(), userSession);
-    actionPlanDao.save(ActionPlanDto.toActionDto(actionPlan, project.getId()));
-    return actionPlan;
-  }
-
-  public ActionPlan update(ActionPlan actionPlan, UserSession userSession) {
-    ResourceDto project = findProject(actionPlan.projectKey());
-    checkUserIsProjectAdministrator(project.getKey(), userSession);
-    actionPlanDao.update(ActionPlanDto.toActionDto(actionPlan, project.getId()));
-    return actionPlan;
-  }
-
-  public void delete(String actionPlanKey, UserSession userSession) {
-    ActionPlanDto dto = findActionPlanDto(actionPlanKey);
-    checkUserIsProjectAdministrator(dto.getProjectKey(), userSession);
-    unplanIssues(dto.toActionPlan(), userSession);
-    actionPlanDao.delete(actionPlanKey);
-  }
-
-  /**
-   * Unplan all issues linked to an action plan
-   */
-  private void unplanIssues(DefaultActionPlan actionPlan, UserSession userSession) {
-    // Get all issues linked to this plan (need to disable pagination and authorization check)
-    IssueQuery query = IssueQuery.builder().actionPlans(Arrays.asList(actionPlan.key())).requiredRole(null).build();
-    List<IssueDto> dtos = issueDao.selectIssues(query);
-    IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login());
-    List<DefaultIssue> issues = newArrayList();
-    for (IssueDto issueDto : dtos) {
-      DefaultIssue issue = issueDto.toDefaultIssue();
-      // Unplan issue
-      if (issueUpdater.plan(issue, null, context)) {
-        issues.add(issue);
-      }
-    }
-    // Save all issues
-    issueStorage.save(issues);
-  }
-
-  public ActionPlan setStatus(String actionPlanKey, String status, UserSession userSession) {
-    ActionPlanDto actionPlanDto = findActionPlanDto(actionPlanKey);
-    checkUserIsProjectAdministrator(actionPlanDto.getProjectKey(), userSession);
-
-    actionPlanDto.setStatus(status);
-    actionPlanDto.setCreatedAt(new Date());
-    actionPlanDao.update(actionPlanDto);
-    return actionPlanDto.toActionPlan();
-  }
-
-  @CheckForNull
-  public ActionPlan findByKey(String key, UserSession userSession) {
-    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(key);
-    if (actionPlanDto == null) {
-      return null;
-    }
-    checkUserCanAccessProject(actionPlanDto.getProjectKey(), userSession);
-    return actionPlanDto.toActionPlan();
-  }
-
-  public List<ActionPlan> findByKeys(Collection<String> keys) {
-    List<ActionPlanDto> actionPlanDtos = actionPlanDao.findByKeys(keys);
-    return toActionPlans(actionPlanDtos);
-  }
-
-  public Collection<ActionPlan> findOpenByProjectKey(String projectKey, UserSession userSession) {
-    ResourceDto project = findProject(projectKey);
-    checkUserCanAccessProject(project.getKey(), userSession);
-
-    List<ActionPlanDto> dtos = actionPlanDao.findOpenByProjectId(project.getId());
-    List<ActionPlan> plans = toActionPlans(dtos);
-    Collections.sort(plans, new ActionPlanDeadlineComparator());
-    return plans;
-  }
-
-  public List<ActionPlanStats> findActionPlanStats(String projectKey, UserSession userSession) {
-    ResourceDto project = findProject(projectKey);
-    checkUserCanAccessProject(project.getKey(), userSession);
-
-    List<ActionPlanStatsDto> actionPlanStatsDtos = actionPlanStatsDao.findByProjectId(project.getId());
-    List<ActionPlanStats> actionPlanStats = newArrayList(Iterables.transform(actionPlanStatsDtos, new Function<ActionPlanStatsDto, ActionPlanStats>() {
-      @Override
-      public ActionPlanStats apply(ActionPlanStatsDto actionPlanStatsDto) {
-        return actionPlanStatsDto.toActionPlanStat();
-      }
-    }));
-    Collections.sort(actionPlanStats, new ActionPlanDeadlineComparator());
-    return actionPlanStats;
-  }
-
-  public boolean isNameAlreadyUsedForProject(String name, String projectKey) {
-    return !actionPlanDao.findByNameAndProjectId(name, findProject(projectKey).getId()).isEmpty();
-  }
-
-  private List<ActionPlan> toActionPlans(List<ActionPlanDto> actionPlanDtos) {
-    return newArrayList(Iterables.transform(actionPlanDtos, new Function<ActionPlanDto, ActionPlan>() {
-      @Override
-      public ActionPlan apply(ActionPlanDto actionPlanDto) {
-        return actionPlanDto.toActionPlan();
-      }
-    }));
-  }
-
-  private ActionPlanDto findActionPlanDto(String actionPlanKey) {
-    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(actionPlanKey);
-    if (actionPlanDto == null) {
-      throw new NotFoundException("Action plan " + actionPlanKey + " has not been found.");
-    }
-    return actionPlanDto;
-  }
-
-  private ResourceDto findProject(String projectKey) {
-    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
-    if (resourceDto == null) {
-      throw new NotFoundException("Project " + projectKey + " does not exists.");
-    }
-    return resourceDto;
-  }
-
-  private void checkUserCanAccessProject(String projectKey, UserSession userSession) {
-    userSession.checkProjectPermission(UserRole.USER, projectKey);
-  }
-
-  private void checkUserIsProjectAdministrator(String projectKey, UserSession userSession) {
-    userSession.checkProjectPermission(UserRole.ADMIN, projectKey);
-  }
-
-}
index 4e8d3f6d78f92032f8dc4e89addd681b8bb9b98f..9882b276348ebd5592d556ecf3c3c47d433339d8 100644 (file)
@@ -42,6 +42,7 @@ import org.sonar.core.issue.db.IssueDto;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.core.resource.ResourceDao;
 import org.sonar.core.rule.DefaultRuleFinder;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
index d0362da17d0746f85517b555c516ee62875567db..0ccfca938c28a30605b364419172084907d0b064 100644 (file)
@@ -44,6 +44,7 @@ import org.sonar.core.resource.ResourceDao;
 import org.sonar.core.resource.ResourceDto;
 import org.sonar.core.resource.ResourceQuery;
 import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.filter.IssueFilterParameters;
 import org.sonar.server.issue.filter.IssueFilterResult;
 import org.sonar.server.issue.filter.IssueFilterService;
index 7db9d49daca97da01dfc3ee4922b9088ab0abb4a..5e026586f1adec0e44913e46582c4882f00130ec 100644 (file)
@@ -47,6 +47,7 @@ import org.sonar.core.resource.ResourceDao;
 import org.sonar.core.resource.ResourceDto;
 import org.sonar.core.resource.ResourceQuery;
 import org.sonar.core.user.AuthorizationDao;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.Nullable;
index d7974d2b57517d1e6f4f1f0832f49b5eb30e5003..49aa95a09ac9dfd5d2db3a9ac0a46babc8c591c7 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.condition.IsUnResolved;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
 import java.util.List;
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java b/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java
new file mode 100644 (file)
index 0000000..f3320db
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.actionplan;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.ActionPlanDeadlineComparator;
+import org.sonar.core.issue.ActionPlanStats;
+import org.sonar.core.issue.DefaultActionPlan;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.db.*;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.resource.ResourceQuery;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.CheckForNull;
+
+import java.util.*;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 3.6
+ */
+public class ActionPlanService implements ServerComponent {
+
+  private final ActionPlanDao actionPlanDao;
+  private final ActionPlanStatsDao actionPlanStatsDao;
+  private final ResourceDao resourceDao;
+  private final IssueDao issueDao;
+  private final IssueUpdater issueUpdater;
+  private final IssueStorage issueStorage;
+
+  public ActionPlanService(ActionPlanDao actionPlanDao, ActionPlanStatsDao actionPlanStatsDao, ResourceDao resourceDao,
+                           IssueDao issueDao, IssueUpdater issueUpdater, IssueStorage issueStorage) {
+    this.actionPlanDao = actionPlanDao;
+    this.actionPlanStatsDao = actionPlanStatsDao;
+    this.resourceDao = resourceDao;
+    this.issueDao = issueDao;
+    this.issueUpdater = issueUpdater;
+    this.issueStorage = issueStorage;
+  }
+
+  public ActionPlan create(ActionPlan actionPlan, UserSession userSession) {
+    ResourceDto project = findProject(actionPlan.projectKey());
+    checkUserIsProjectAdministrator(project.getKey(), userSession);
+    actionPlanDao.save(ActionPlanDto.toActionDto(actionPlan, project.getId()));
+    return actionPlan;
+  }
+
+  public ActionPlan update(ActionPlan actionPlan, UserSession userSession) {
+    ResourceDto project = findProject(actionPlan.projectKey());
+    checkUserIsProjectAdministrator(project.getKey(), userSession);
+    actionPlanDao.update(ActionPlanDto.toActionDto(actionPlan, project.getId()));
+    return actionPlan;
+  }
+
+  public void delete(String actionPlanKey, UserSession userSession) {
+    ActionPlanDto dto = findActionPlanDto(actionPlanKey);
+    checkUserIsProjectAdministrator(dto.getProjectKey(), userSession);
+    unplanIssues(dto.toActionPlan(), userSession);
+    actionPlanDao.delete(actionPlanKey);
+  }
+
+  /**
+   * Unplan all issues linked to an action plan
+   */
+  private void unplanIssues(DefaultActionPlan actionPlan, UserSession userSession) {
+    // Get all issues linked to this plan (need to disable pagination and authorization check)
+    IssueQuery query = IssueQuery.builder().actionPlans(Arrays.asList(actionPlan.key())).requiredRole(null).build();
+    List<IssueDto> dtos = issueDao.selectIssues(query);
+    IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login());
+    List<DefaultIssue> issues = newArrayList();
+    for (IssueDto issueDto : dtos) {
+      DefaultIssue issue = issueDto.toDefaultIssue();
+      // Unplan issue
+      if (issueUpdater.plan(issue, null, context)) {
+        issues.add(issue);
+      }
+    }
+    // Save all issues
+    issueStorage.save(issues);
+  }
+
+  public ActionPlan setStatus(String actionPlanKey, String status, UserSession userSession) {
+    ActionPlanDto actionPlanDto = findActionPlanDto(actionPlanKey);
+    checkUserIsProjectAdministrator(actionPlanDto.getProjectKey(), userSession);
+
+    actionPlanDto.setStatus(status);
+    actionPlanDto.setCreatedAt(new Date());
+    actionPlanDao.update(actionPlanDto);
+    return actionPlanDto.toActionPlan();
+  }
+
+  @CheckForNull
+  public ActionPlan findByKey(String key, UserSession userSession) {
+    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(key);
+    if (actionPlanDto == null) {
+      return null;
+    }
+    checkUserCanAccessProject(actionPlanDto.getProjectKey(), userSession);
+    return actionPlanDto.toActionPlan();
+  }
+
+  public List<ActionPlan> findByKeys(Collection<String> keys) {
+    List<ActionPlanDto> actionPlanDtos = actionPlanDao.findByKeys(keys);
+    return toActionPlans(actionPlanDtos);
+  }
+
+  public Collection<ActionPlan> findOpenByProjectKey(String projectKey, UserSession userSession) {
+    ResourceDto project = findProject(projectKey);
+    checkUserCanAccessProject(project.getKey(), userSession);
+
+    List<ActionPlanDto> dtos = actionPlanDao.findOpenByProjectId(project.getId());
+    List<ActionPlan> plans = toActionPlans(dtos);
+    Collections.sort(plans, new ActionPlanDeadlineComparator());
+    return plans;
+  }
+
+  public List<ActionPlanStats> findActionPlanStats(String projectKey, UserSession userSession) {
+    ResourceDto project = findProject(projectKey);
+    checkUserCanAccessProject(project.getKey(), userSession);
+
+    List<ActionPlanStatsDto> actionPlanStatsDtos = actionPlanStatsDao.findByProjectId(project.getId());
+    List<ActionPlanStats> actionPlanStats = newArrayList(Iterables.transform(actionPlanStatsDtos, new Function<ActionPlanStatsDto, ActionPlanStats>() {
+      @Override
+      public ActionPlanStats apply(ActionPlanStatsDto actionPlanStatsDto) {
+        return actionPlanStatsDto.toActionPlanStat();
+      }
+    }));
+    Collections.sort(actionPlanStats, new ActionPlanDeadlineComparator());
+    return actionPlanStats;
+  }
+
+  public boolean isNameAlreadyUsedForProject(String name, String projectKey) {
+    return !actionPlanDao.findByNameAndProjectId(name, findProject(projectKey).getId()).isEmpty();
+  }
+
+  private List<ActionPlan> toActionPlans(List<ActionPlanDto> actionPlanDtos) {
+    return newArrayList(Iterables.transform(actionPlanDtos, new Function<ActionPlanDto, ActionPlan>() {
+      @Override
+      public ActionPlan apply(ActionPlanDto actionPlanDto) {
+        return actionPlanDto.toActionPlan();
+      }
+    }));
+  }
+
+  private ActionPlanDto findActionPlanDto(String actionPlanKey) {
+    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(actionPlanKey);
+    if (actionPlanDto == null) {
+      throw new NotFoundException("Action plan " + actionPlanKey + " has not been found.");
+    }
+    return actionPlanDto;
+  }
+
+  private ResourceDto findProject(String projectKey) {
+    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
+    if (resourceDto == null) {
+      throw new NotFoundException("Project " + projectKey + " does not exists.");
+    }
+    return resourceDto;
+  }
+
+  private void checkUserCanAccessProject(String projectKey, UserSession userSession) {
+    userSession.checkProjectPermission(UserRole.USER, projectKey);
+  }
+
+  private void checkUserIsProjectAdministrator(String projectKey, UserSession userSession) {
+    userSession.checkProjectPermission(UserRole.ADMIN, projectKey);
+  }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java b/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java
new file mode 100644 (file)
index 0000000..8842cbc
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.actionplan;
+
+import com.google.common.io.Resources;
+import org.sonar.api.server.ws.RailsHandler;
+import org.sonar.api.server.ws.WebService;
+
+public class ActionPlanWs implements WebService {
+
+  @Override
+  public void define(Context context) {
+    NewController controller = context.createController("api/action_plans");
+    controller.setDescription("Action plans");
+
+    WebService.NewAction search = controller.createAction("search")
+      .setDescription("Get a list of action plans. Requires Browse permission on project")
+      .setSince("3.6")
+      .setHandler(RailsHandler.INSTANCE)
+      .setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
+    addProjectParam(search);
+
+    WebService.NewAction create = controller.createAction("create")
+      .setDescription("Create an action plan. Requires Administer permission on project")
+      .setSince("3.6")
+      .setPost(true)
+      .setHandler(RailsHandler.INSTANCE);
+    addNameParam(create);
+    addDescriptionParam(create);
+    addDeadLineParam(create);
+    addProjectParam(create);
+
+    WebService.NewAction update = controller.createAction("update")
+      .setDescription("Update an action plan. Requires Administer permission on project")
+      .setSince("3.6")
+      .setPost(true)
+      .setHandler(RailsHandler.INSTANCE);
+    addKeyParam(update);
+    addNameParam(update);
+    addDescriptionParam(update);
+    addDeadLineParam(update);
+
+    WebService.NewAction delete = controller.createAction("delete")
+      .setDescription("Delete an action plan. Requires Administer permission on project")
+      .setSince("3.6")
+      .setPost(true)
+      .setHandler(RailsHandler.INSTANCE);
+    addKeyParam(delete);
+
+    WebService.NewAction open = controller.createAction("open")
+      .setDescription("Open an action plan. Requires Administer permission on project")
+      .setSince("3.6")
+      .setPost(true)
+      .setHandler(RailsHandler.INSTANCE);
+    addKeyParam(open);
+
+    WebService.NewAction close = controller.createAction("close")
+      .setDescription("Close an action plan. Requires Administer permission on project")
+      .setSince("3.6")
+      .setPost(true)
+      .setHandler(RailsHandler.INSTANCE);
+    addKeyParam(close);
+
+    controller.done();
+  }
+
+  private static NewParam addKeyParam(WebService.NewAction action) {
+    return action.createParam("key")
+      .setDescription("Key of the action plan")
+      .setExampleValue("3f19de90-1521-4482-a737-a311758ff513")
+      .setRequired(true);
+  }
+
+  private static NewParam addNameParam(WebService.NewAction action) {
+    return action.createParam("name")
+      .setDescription("Name of the action plan")
+      .setExampleValue("Version 3.6")
+      .setRequired(true);
+  }
+
+  private static NewParam addDescriptionParam(WebService.NewAction action) {
+    return action.createParam("description")
+      .setDescription("Description of the action plan")
+      .setExampleValue("Version 3.6");
+  }
+
+  private static NewParam addDeadLineParam(WebService.NewAction action) {
+    return action.createParam("deadLine")
+      .setDescription("Due date of the action plan. Format: YYYY-MM-DD")
+      .setExampleValue("2013-12-31");
+  }
+
+  private static NewParam addProjectParam(WebService.NewAction action) {
+    return action.createParam("project")
+      .setDescription("Project key")
+      .setExampleValue("org.codehaus.sonar:sonar")
+      .setRequired(true);
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/filter/AppAction.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/AppAction.java
new file mode 100644 (file)
index 0000000..24168ba
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.UserSession;
+
+import java.util.List;
+
+public class AppAction implements RequestHandler {
+
+  private final IssueFilterService service;
+  private final IssueFilterWriter issueFilterWriter;
+
+  public AppAction(IssueFilterService service, IssueFilterWriter issueFilterWriter) {
+    this.service = service;
+    this.issueFilterWriter = issueFilterWriter;
+  }
+
+  void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("app");
+    action
+      .setDescription("Data required for rendering the page 'Issues'")
+      .setInternal(true)
+      .setHandler(this);
+    action.createParam("id").setDescription("Optionally, the ID of the current filter");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    UserSession session = UserSession.get();
+
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject();
+
+    // Current filter (optional)
+    int filterId = request.paramAsInt("id", -1);
+    DefaultIssueFilter filter = null;
+    if (filterId >= 0) {
+      filter = service.find((long) filterId, session);
+    }
+
+    // Permissions
+    json.prop("canManageFilters", session.isLoggedIn());
+    json.prop("canBulkChange", session.isLoggedIn());
+
+    // Selected filter
+    if (filter != null) {
+      issueFilterWriter.write(session, filter, json);
+    }
+
+    // Favorite filters, if logged in
+    if (session.isLoggedIn()) {
+      List<DefaultIssueFilter> favorites = service.findFavoriteFilters(session);
+      json.name("favorites").beginArray();
+      for (DefaultIssueFilter favorite : favorites) {
+        json
+          .beginObject()
+          .prop("id", favorite.id())
+          .prop("name", favorite.name())
+          .endObject();
+      }
+      json.endArray();
+    }
+
+    json.endObject();
+    json.close();
+  }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/filter/FavoritesAction.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/FavoritesAction.java
new file mode 100644 (file)
index 0000000..1aeda5a
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.UserSession;
+
+public class FavoritesAction implements RequestHandler {
+
+  private final IssueFilterService service;
+
+  public FavoritesAction(IssueFilterService service) {
+    this.service = service;
+  }
+
+  void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("favorites");
+    action
+      .setDescription("The issue filters marked as favorite by request user")
+      .setSince("4.2")
+      .setHandler(this);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    UserSession session = UserSession.get();
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject().name("favoriteFilters").beginArray();
+    if (session.isLoggedIn()) {
+      for (DefaultIssueFilter favorite : service.findFavoriteFilters(session)) {
+        json.beginObject();
+        json.prop("id", favorite.id());
+        json.prop("name", favorite.name());
+        json.prop("user", favorite.user());
+        json.prop("shared", favorite.shared());
+        // no need to export description and query fields
+        json.endObject();
+      }
+    }
+    json.endArray().endObject().close();
+  }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWriter.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterWriter.java
new file mode 100644 (file)
index 0000000..940c67d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.user.UserSession;
+
+public class IssueFilterWriter implements ServerComponent {
+
+  void write(UserSession session, DefaultIssueFilter filter, JsonWriter json) {
+    json.name("filter").beginObject()
+      .prop("id", filter.id())
+      .prop("name", filter.name())
+      .prop("description", filter.description())
+      .prop("user", filter.user())
+      .prop("shared", filter.shared())
+      .prop("query", filter.data())
+      .prop("canModify", canModifyFilter(session, filter))
+      .endObject();
+  }
+
+  private boolean canModifyFilter(UserSession session, DefaultIssueFilter filter) {
+    return session.isLoggedIn() &&
+      (StringUtils.equals(filter.user(), session.login()) || session.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN));
+  }
+
+}
index 04dc4068583af57f67a74d309b37e2a53f860953..aba50b9c285311381d27853b9ff151e210ee79b2 100644 (file)
  */
 package org.sonar.server.issue.filter;
 
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
-import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.core.issue.DefaultIssueFilter;
-import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.server.user.UserSession;
-
-import java.util.List;
 
 public class IssueFilterWs implements WebService {
 
-  private static final String PARAM_ID = "id";
-  private final IssueFilterService service;
+  private final AppAction appAction;
+  private final ShowAction showAction;
+  private final FavoritesAction favoritesAction;
 
-  public IssueFilterWs(IssueFilterService service) {
-    this.service = service;
+  public IssueFilterWs(AppAction appAction, ShowAction showAction, FavoritesAction favoritesAction) {
+    this.appAction = appAction;
+    this.showAction = showAction;
+    this.favoritesAction = favoritesAction;
   }
 
   @Override
@@ -45,132 +38,10 @@ public class IssueFilterWs implements WebService {
     NewController controller = context.createController("api/issue_filters")
       .setSince("4.2")
       .setDescription("Issue Filters");
-
-    NewAction app = controller.createAction("app");
-    app
-      .setDescription("Data required for rendering the page 'Issues'")
-      .setInternal(true)
-      .setHandler(new RequestHandler() {
-        @Override
-        public void handle(Request request, Response response) {
-          app(request, response);
-        }
-      });
-    app.createParam(PARAM_ID).setDescription("Optionally, the ID of the current filter");
-
-    NewAction show = controller.createAction("show");
-    show
-      .setDescription("Get detail of an issue filter. Requires to be authenticated")
-      .setSince("4.2")
-      .setHandler(new RequestHandler() {
-        @Override
-        public void handle(Request request, Response response) {
-          show(request, response);
-        }
-      });
-    show.createParam(PARAM_ID)
-      .setDescription("ID of the issue filter")
-      .setRequired(true);
-
-    NewAction fav = controller.createAction("favorites");
-    fav
-      .setDescription("The issue filters marked as favorite by request user")
-      .setSince("4.2")
-      .setHandler(new RequestHandler() {
-        @Override
-        public void handle(Request request, Response response) {
-          favorites(request, response);
-        }
-      });
-
+    appAction.define(controller);
+    showAction.define(controller);
+    favoritesAction.define(controller);
     controller.done();
   }
 
-  private void app(Request request, Response response) {
-    UserSession session = UserSession.get();
-
-    JsonWriter json = response.newJsonWriter();
-    json.beginObject();
-
-    // Current filter (optional)
-    int filterId = request.paramAsInt(PARAM_ID, -1);
-    DefaultIssueFilter filter = null;
-    if (filterId >= 0) {
-      filter = service.find((long) filterId, session);
-    }
-
-    // Permissions
-    json.prop("canManageFilters", session.isLoggedIn());
-    json.prop("canBulkChange", session.isLoggedIn());
-
-    // Selected filter
-    if (filter != null) {
-      json.name("filter");
-      writeFilterJson(session, filter, json);
-    }
-
-    // Favorite filters, if logged in
-    if (session.isLoggedIn()) {
-      List<DefaultIssueFilter> favorites = service.findFavoriteFilters(session);
-      json.name("favorites").beginArray();
-      for (DefaultIssueFilter favorite : favorites) {
-        json
-          .beginObject()
-          .prop(PARAM_ID, favorite.id())
-          .prop("name", favorite.name())
-          .endObject();
-      }
-      json.endArray();
-    }
-
-    json.endObject();
-    json.close();
-  }
-
-  private void show(Request request, Response response) {
-    UserSession session = UserSession.get();
-    DefaultIssueFilter filter = service.find(Long.parseLong(request.mandatoryParam(PARAM_ID)), session);
-
-    JsonWriter json = response.newJsonWriter();
-    json.beginObject();
-    json.name("filter");
-    writeFilterJson(session, filter, json);
-    json.endObject();
-    json.close();
-  }
-
-  private void favorites(Request request, Response response) {
-    UserSession session = UserSession.get();
-    JsonWriter json = response.newJsonWriter();
-    json.beginObject().name("favoriteFilters").beginArray();
-    if (session.isLoggedIn()) {
-      for (DefaultIssueFilter favorite : service.findFavoriteFilters(session)) {
-        json.beginObject();
-        json.prop(PARAM_ID, favorite.id());
-        json.prop("name", favorite.name());
-        json.prop("user", favorite.user());
-        json.prop("shared", favorite.shared());
-        // no need to export description and query fields
-        json.endObject();
-      }
-    }
-    json.endArray().endObject().close();
-  }
-
-  private JsonWriter writeFilterJson(UserSession session, DefaultIssueFilter filter, JsonWriter json) {
-    return json.beginObject()
-      .prop(PARAM_ID, filter.id())
-      .prop("name", filter.name())
-      .prop("description", filter.description())
-      .prop("user", filter.user())
-      .prop("shared", filter.shared())
-      .prop("query", filter.data())
-      .prop("canModify", canModifyFilter(session, filter))
-      .endObject();
-  }
-
-  private boolean canModifyFilter(UserSession session, DefaultIssueFilter filter) {
-    return session.isLoggedIn() &&
-      (StringUtils.equals(filter.user(), session.login()) || session.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN));
-  }
 }
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/filter/ShowAction.java b/sonar-server/src/main/java/org/sonar/server/issue/filter/ShowAction.java
new file mode 100644 (file)
index 0000000..4d1faa3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import com.google.common.io.Resources;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.UserSession;
+
+public class ShowAction implements RequestHandler {
+
+  private final IssueFilterService service;
+  private final IssueFilterWriter issueFilterWriter;
+
+  public ShowAction(IssueFilterService service, IssueFilterWriter issueFilterWriter) {
+    this.service = service;
+    this.issueFilterWriter = issueFilterWriter;
+  }
+
+  void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("show");
+    action
+      .setDescription("Get detail of an issue filter. Requires to be authenticated")
+      .setSince("4.2")
+      .setHandler(this)
+      .setResponseExample(Resources.getResource(this.getClass(), "example-show.json"));
+    action.createParam("id")
+      .setDescription("ID of the issue filter")
+      .setRequired(true);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    UserSession session = UserSession.get();
+    DefaultIssueFilter filter = service.find(Long.parseLong(request.mandatoryParam("id")), session);
+
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject();
+    issueFilterWriter.write(session, filter, json);
+    json.endObject();
+    json.close();
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/ActionPlanWs.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/ActionPlanWs.java
deleted file mode 100644 (file)
index 8945188..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.server.issue.ws;
-
-import org.sonar.api.server.ws.RailsHandler;
-import org.sonar.api.server.ws.WebService;
-
-public class ActionPlanWs implements WebService {
-
-  @Override
-  public void define(Context context) {
-    NewController controller = context.createController("api/action_plans");
-    controller.setDescription("Action plans");
-
-    WebService.NewAction search = controller.createAction("search")
-      .setDescription("Get a list of action plans. Requires Browse permission on project")
-      .setSince("3.6")
-      .setHandler(RailsHandler.INSTANCE);
-    addProjectParam(search);
-
-    WebService.NewAction create = controller.createAction("create")
-      .setDescription("Create an action plan. Requires Administer permission on project")
-      .setSince("3.6")
-      .setPost(true)
-      .setHandler(RailsHandler.INSTANCE);
-    addNameParam(create);
-    addDescriptionParam(create);
-    addDeadLineParam(create);
-    addProjectParam(create);
-
-    WebService.NewAction update = controller.createAction("update")
-      .setDescription("Update an action plan. Requires Administer permission on project")
-      .setSince("3.6")
-      .setPost(true)
-      .setHandler(RailsHandler.INSTANCE);
-    addKeyParam(update);
-    addNameParam(update);
-    addDescriptionParam(update);
-    addDeadLineParam(update);
-
-    WebService.NewAction delete = controller.createAction("delete")
-      .setDescription("Delete an action plan. Requires Administer permission on project")
-      .setSince("3.6")
-      .setPost(true)
-      .setHandler(RailsHandler.INSTANCE);
-    addKeyParam(delete);
-
-    WebService.NewAction open = controller.createAction("open")
-      .setDescription("Open an action plan. Requires Administer permission on project")
-      .setSince("3.6")
-      .setPost(true)
-      .setHandler(RailsHandler.INSTANCE);
-    addKeyParam(open);
-
-    WebService.NewAction close = controller.createAction("close")
-      .setDescription("Close an action plan. Requires Administer permission on project")
-      .setSince("3.6")
-      .setPost(true)
-      .setHandler(RailsHandler.INSTANCE);
-    addKeyParam(close);
-
-    controller.done();
-  }
-
-  private static NewParam addKeyParam(WebService.NewAction action) {
-    return action.createParam("key")
-      .setDescription("Key of the action plan")
-      .setExampleValue("3f19de90-1521-4482-a737-a311758ff513")
-      .setRequired(true);
-  }
-
-  private static NewParam addNameParam(WebService.NewAction action) {
-    return action.createParam("name")
-      .setDescription("Name of the action plan")
-      .setExampleValue("Version 3.6")
-      .setRequired(true);
-  }
-
-  private static NewParam addDescriptionParam(WebService.NewAction action) {
-    return action.createParam("description")
-      .setDescription("Description of the action plan")
-      .setExampleValue("Version 3.6");
-  }
-
-  private static NewParam addDeadLineParam(WebService.NewAction action) {
-    return action.createParam("deadLine")
-      .setDescription("Due date of the action plan. Format: YYYY-MM-DD")
-      .setExampleValue("2013-12-31");
-  }
-
-  private static NewParam addProjectParam(WebService.NewAction action) {
-    return action.createParam("project")
-      .setDescription("Project key")
-      .setExampleValue("org.codehaus.sonar:sonar")
-      .setRequired(true);
-  }
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java
new file mode 100644 (file)
index 0000000..b244b68
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.issue.ws;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.sonar.api.component.Component;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.issue.*;
+import org.sonar.api.issue.action.Action;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.api.server.debt.DebtCharacteristic;
+import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.user.User;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.workflow.Transition;
+import org.sonar.markdown.Markdown;
+import org.sonar.server.debt.DebtModelService;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.IssueChangelog;
+import org.sonar.server.issue.IssueChangelogService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class IssueShowAction implements RequestHandler {
+
+  private final IssueFinder issueFinder;
+  private final IssueService issueService;
+  private final IssueChangelogService issueChangelogService;
+  private final ActionService actionService;
+  private final DebtModelService debtModel;
+  private final I18n i18n;
+  private final Durations durations;
+
+  public IssueShowAction(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService,
+                         DebtModelService debtModel, I18n i18n, Durations durations) {
+    this.issueFinder = issueFinder;
+    this.issueService = issueService;
+    this.issueChangelogService = issueChangelogService;
+    this.actionService = actionService;
+    this.debtModel = debtModel;
+    this.i18n = i18n;
+    this.durations = durations;
+  }
+
+  void define (WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("show")
+      .setDescription("Detail of issue")
+      .setSince("4.2")
+      .setInternal(true)
+      .setHandler(this);
+    action.createParam("key")
+      .setDescription("Issue key");
+  }
+
+  @Override
+  public void handle(Request request, Response response) {
+    String issueKey = request.mandatoryParam("key");
+    IssueQueryResult queryResult = issueFinder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).build());
+    if (queryResult.issues().size() != 1) {
+      throw new NotFoundException("Issue not found: " + issueKey);
+    }
+    DefaultIssue issue = (DefaultIssue) queryResult.first();
+
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject().name("issue").beginObject();
+
+    writeIssue(queryResult, issue, json);
+    writeTransitions(issue, json);
+    writeActions(issue, json);
+    writeComments(queryResult, issue, json);
+    writeChangelog(issue, json);
+
+    json.endObject().endObject().close();
+  }
+
+  private void writeIssue(IssueQueryResult result, DefaultIssue issue, JsonWriter json) {
+    String actionPlanKey = issue.actionPlanKey();
+    ActionPlan actionPlan = result.actionPlan(issue);
+    Duration debt = issue.debt();
+    Date updateDate = issue.updateDate();
+    Date closeDate = issue.closeDate();
+
+    json
+      .prop("key", issue.key())
+      .prop("rule", issue.ruleKey().toString())
+      .prop("ruleName", result.rule(issue).getName())
+      .prop("line", issue.line())
+      .prop("message", issue.message())
+      .prop("resolution", issue.resolution())
+      .prop("status", issue.status())
+      .prop("severity", issue.severity())
+      .prop("author", issue.authorLogin())
+      .prop("actionPlan", actionPlanKey)
+      .prop("actionPlanName", actionPlan != null ? actionPlan.name() : null)
+      .prop("debt", debt != null ? durations.format(UserSession.get().locale(), debt, Durations.DurationFormat.SHORT) : null)
+      .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
+      .prop("fCreationDate", formatDate(issue.creationDate()))
+      .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null)
+      .prop("fUpdateDate", formatDate(updateDate))
+      .prop("fUpdateAge", formatAgeDate(updateDate))
+      .prop("closeDate", closeDate != null ? DateUtils.formatDateTime(closeDate) : null)
+      .prop("fCloseDate", formatDate(issue.closeDate()));
+
+    addComponents(result, issue, json);
+    addUserWithLabel(result, issue.assignee(), "assignee", json);
+    addUserWithLabel(result, issue.reporter(), "reporter", json);
+    addCharacteristics(result, issue, json);
+  }
+
+  private void addComponents(IssueQueryResult result, DefaultIssue issue, JsonWriter json) {
+    // component, module and project can be null if they were removed
+    ComponentDto component = (ComponentDto) result.component(issue);
+    ComponentDto subProject = (ComponentDto) getSubProject(result, component);
+    ComponentDto project = (ComponentDto) geProject(result, component);
+
+    String projectName = project != null ? project.longName() != null ? project.longName() : project.name() : null;
+    // Do not display sub project long name if sub project and project are the same
+    boolean shoudDisplaySubProjectLongName = subProject != null && project != null && !subProject.getId().equals(project.getId());
+    String subProjectName = shoudDisplaySubProjectLongName ? subProject.longName() != null ? subProject.longName() : subProject.name() : null;
+
+    json
+      .prop("component", issue.componentKey())
+      .prop("componentLongName", component != null ? component.longName() : null)
+      .prop("componentQualifier", component != null ? component.qualifier() : null)
+      .prop("project", issue.projectKey())
+      .prop("projectName", projectName)
+        // Do not display sub project long name if sub project and project are the same
+      .prop("subProjectName", subProjectName)
+    ;
+  }
+
+  /**
+   * Can be null on project or on removed component
+   */
+  @CheckForNull
+  private Component getSubProject(IssueQueryResult result, @Nullable final ComponentDto component) {
+    if (component != null) {
+      return Iterables.find(result.components(), new Predicate<Component>() {
+        @Override
+        public boolean apply(Component input) {
+          Long groupId = component.subProjectId();
+          return groupId != null && groupId.equals(((ComponentDto) input).getId());
+        }
+      }, null);
+    }
+    return null;
+  }
+
+  /**
+   * Can be null on removed component
+   */
+  @CheckForNull
+  private Component geProject(IssueQueryResult result, @Nullable final ComponentDto component) {
+    if (component != null) {
+      return Iterables.find(result.components(), new Predicate<Component>() {
+        @Override
+        public boolean apply(Component input) {
+          return component.projectId().equals(((ComponentDto) input).getId());
+        }
+      }, null);
+    }
+    return null;
+  }
+
+  private void addCharacteristics(IssueQueryResult result, DefaultIssue issue, JsonWriter json) {
+    Integer subCharacteristicId = result.rule(issue).getCharacteristicId() != null ? result.rule(issue).getCharacteristicId() : result.rule(issue).getDefaultCharacteristicId();
+    DebtCharacteristic subCharacteristic = characteristicById(subCharacteristicId);
+    if (subCharacteristic != null) {
+      json.prop("subCharacteristic", subCharacteristic.name());
+      DebtCharacteristic characteristic = characteristicById(((DefaultDebtCharacteristic) subCharacteristic).parentId());
+      json.prop("characteristic", characteristic != null ? characteristic.name() : null);
+    }
+  }
+
+  @CheckForNull
+  private DebtCharacteristic characteristicById(@Nullable Integer id) {
+    if (id != null) {
+      return debtModel.characteristicById(id);
+    }
+    return null;
+  }
+
+  private void writeTransitions(Issue issue, JsonWriter json) {
+    json.name("transitions").beginArray();
+    if (UserSession.get().isLoggedIn()) {
+      List<Transition> transitions = issueService.listTransitions(issue, UserSession.get());
+      for (Transition transition : transitions) {
+        json.value(transition.key());
+      }
+    }
+    json.endArray();
+  }
+
+  private void writeActions(DefaultIssue issue, JsonWriter json) {
+    json.name("actions").beginArray();
+    for (String action : actions(issue)) {
+      json.value(action);
+    }
+    json.endArray();
+  }
+
+  // TODO all available actions should be returned by ActionService or another service
+  private List<String> actions(DefaultIssue issue) {
+    List<String> actions = newArrayList();
+    String login = UserSession.get().login();
+    if (login != null) {
+      actions.add("comment");
+      if (issue.resolution() == null) {
+        actions.add("assign");
+        if (!login.equals(issue.assignee())) {
+          actions.add("assign_to_me");
+        }
+        actions.add("plan");
+        String projectKey = issue.projectKey();
+        if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) {
+          actions.add("set_severity");
+        }
+        for (Action action : actionService.listAvailableActions(issue)) {
+          actions.add(action.key());
+        }
+      }
+    }
+    return actions;
+  }
+
+  private void writeComments(IssueQueryResult queryResult, Issue issue, JsonWriter json) {
+    json.name("comments").beginArray();
+    String login = UserSession.get().login();
+    for (IssueComment comment : issue.comments()) {
+      String userLogin = comment.userLogin();
+      User user = userLogin != null ? queryResult.user(userLogin) : null;
+      json
+        .beginObject()
+        .prop("key", comment.key())
+        .prop("userName", user != null ? user.name() : null)
+        .prop("raw", comment.markdownText())
+        .prop("html", Markdown.convertToHtml(comment.markdownText()))
+        .prop("createdAt", DateUtils.formatDateTime(comment.createdAt()))
+        .prop("fCreatedAge", formatAgeDate(comment.createdAt()))
+        .prop("updatable", login != null && login.equals(userLogin))
+        .endObject();
+    }
+    json.endArray();
+  }
+
+  private void writeChangelog(Issue issue, JsonWriter json) {
+    json.name("changelog").beginArray()
+      .beginObject()
+      .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
+      .prop("fCreationDate", formatDate(issue.creationDate()))
+      .name("diffs").beginArray()
+      .value(i18n.message(UserSession.get().locale(), "created", null))
+      .endArray()
+      .endObject();
+
+    IssueChangelog changelog = issueChangelogService.changelog(issue);
+    for (FieldDiffs diffs : changelog.changes()) {
+      User user = changelog.user(diffs);
+      json
+        .beginObject()
+        .prop("userName", user != null ? user.name() : null)
+        .prop("creationDate", DateUtils.formatDateTime(diffs.creationDate()))
+        .prop("fCreationDate", formatDate(diffs.creationDate()));
+      json.name("diffs").beginArray();
+      List<String> diffsFormatted = issueChangelogService.formatDiffs(diffs);
+      for (String diff : diffsFormatted) {
+        json.value(diff);
+      }
+      json.endArray();
+      json.endObject();
+    }
+    json.endArray();
+  }
+
+  private void addUserWithLabel(IssueQueryResult result, @Nullable String value, String field, JsonWriter json) {
+    if (value != null) {
+      User user = result.user(value);
+      json
+        .prop(field, value)
+        .prop(field + "Name", user != null ? user.name() : null);
+    }
+  }
+
+  @CheckForNull
+  private String formatDate(@Nullable Date date) {
+    if (date != null) {
+      return i18n.formatDateTime(UserSession.get().locale(), date);
+    }
+    return null;
+  }
+
+  @CheckForNull
+  private String formatAgeDate(@Nullable Date date) {
+    if (date != null) {
+      return i18n.ageFromNow(UserSession.get().locale(), date);
+    }
+    return null;
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java
deleted file mode 100644 (file)
index d32cf02..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.issue.ws;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import org.sonar.api.component.Component;
-import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.*;
-import org.sonar.api.issue.action.Action;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.server.debt.DebtCharacteristic;
-import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
-import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
-import org.sonar.api.server.ws.Response;
-import org.sonar.api.user.User;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.Duration;
-import org.sonar.api.utils.Durations;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.component.ComponentDto;
-import org.sonar.core.issue.workflow.Transition;
-import org.sonar.markdown.Markdown;
-import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.IssueChangelog;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.user.UserSession;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-public class IssueShowWsHandler implements RequestHandler {
-
-  private final IssueFinder issueFinder;
-  private final IssueService issueService;
-  private final IssueChangelogService issueChangelogService;
-  private final ActionService actionService;
-  private final DebtModelService debtModel;
-  private final I18n i18n;
-  private final Durations durations;
-
-  public IssueShowWsHandler(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService,
-                            DebtModelService debtModel, I18n i18n, Durations durations) {
-    this.issueFinder = issueFinder;
-    this.issueService = issueService;
-    this.issueChangelogService = issueChangelogService;
-    this.actionService = actionService;
-    this.debtModel = debtModel;
-    this.i18n = i18n;
-    this.durations = durations;
-  }
-
-  @Override
-  public void handle(Request request, Response response) {
-    String issueKey = request.mandatoryParam("key");
-    IssueQueryResult queryResult = issueFinder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).build());
-    if (queryResult.issues().size() != 1) {
-      throw new NotFoundException("Issue not found: " + issueKey);
-    }
-    DefaultIssue issue = (DefaultIssue) queryResult.first();
-
-    JsonWriter json = response.newJsonWriter();
-    json.beginObject().name("issue").beginObject();
-
-    writeIssue(queryResult, issue, json);
-    writeTransitions(issue, json);
-    writeActions(issue, json);
-    writeComments(queryResult, issue, json);
-    writeChangelog(issue, json);
-
-    json.endObject().endObject().close();
-  }
-
-  private void writeIssue(IssueQueryResult result, DefaultIssue issue, JsonWriter json) {
-    String actionPlanKey = issue.actionPlanKey();
-    ActionPlan actionPlan = result.actionPlan(issue);
-    Duration debt = issue.debt();
-    Date updateDate = issue.updateDate();
-    Date closeDate = issue.closeDate();
-
-    json
-      .prop("key", issue.key())
-      .prop("rule", issue.ruleKey().toString())
-      .prop("ruleName", result.rule(issue).getName())
-      .prop("line", issue.line())
-      .prop("message", issue.message())
-      .prop("resolution", issue.resolution())
-      .prop("status", issue.status())
-      .prop("severity", issue.severity())
-      .prop("author", issue.authorLogin())
-      .prop("actionPlan", actionPlanKey)
-      .prop("actionPlanName", actionPlan != null ? actionPlan.name() : null)
-      .prop("debt", debt != null ? durations.format(UserSession.get().locale(), debt, Durations.DurationFormat.SHORT) : null)
-      .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
-      .prop("fCreationDate", formatDate(issue.creationDate()))
-      .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null)
-      .prop("fUpdateDate", formatDate(updateDate))
-      .prop("fUpdateAge", formatAgeDate(updateDate))
-      .prop("closeDate", closeDate != null ? DateUtils.formatDateTime(closeDate) : null)
-      .prop("fCloseDate", formatDate(issue.closeDate()));
-
-    addComponents(result, issue, json);
-    addUserWithLabel(result, issue.assignee(), "assignee", json);
-    addUserWithLabel(result, issue.reporter(), "reporter", json);
-    addCharacteristics(result, issue, json);
-  }
-
-  private void addComponents(IssueQueryResult result, DefaultIssue issue, JsonWriter json) {
-    // component, module and project can be null if they were removed
-    ComponentDto component = (ComponentDto) result.component(issue);
-    ComponentDto subProject = (ComponentDto) getSubProject(result, component);
-    ComponentDto project = (ComponentDto) geProject(result, component);
-
-    String projectName = project != null ? project.longName() != null ? project.longName() : project.name() : null;
-    // Do not display sub project long name if sub project and project are the same
-    boolean shoudDisplaySubProjectLongName = subProject != null && project != null && !subProject.getId().equals(project.getId());
-    String subProjectName = shoudDisplaySubProjectLongName ? subProject.longName() != null ? subProject.longName() : subProject.name() : null;
-
-    json
-      .prop("component", issue.componentKey())
-      .prop("componentLongName", component != null ? component.longName() : null)
-      .prop("componentQualifier", component != null ? component.qualifier() : null)
-      .prop("project", issue.projectKey())
-      .prop("projectName", projectName)
-        // Do not display sub project long name if sub project and project are the same
-      .prop("subProjectName", subProjectName)
-    ;
-  }
-
-  /**
-   * Can be null on project or on removed component
-   */
-  @CheckForNull
-  private Component getSubProject(IssueQueryResult result, @Nullable final ComponentDto component) {
-    if (component != null) {
-      return Iterables.find(result.components(), new Predicate<Component>() {
-        @Override
-        public boolean apply(Component input) {
-          Long groupId = component.subProjectId();
-          return groupId != null && groupId.equals(((ComponentDto) input).getId());
-        }
-      }, null);
-    }
-    return null;
-  }
-
-  /**
-   * Can be null on removed component
-   */
-  @CheckForNull
-  private Component geProject(IssueQueryResult result, @Nullable final ComponentDto component) {
-    if (component != null) {
-      return Iterables.find(result.components(), new Predicate<Component>() {
-        @Override
-        public boolean apply(Component input) {
-          return component.projectId().equals(((ComponentDto) input).getId());
-        }
-      }, null);
-    }
-    return null;
-  }
-
-  private void addCharacteristics(IssueQueryResult result, DefaultIssue issue, JsonWriter json) {
-    Integer subCharacteristicId = result.rule(issue).getCharacteristicId() != null ? result.rule(issue).getCharacteristicId() : result.rule(issue).getDefaultCharacteristicId();
-    DebtCharacteristic subCharacteristic = characteristicById(subCharacteristicId);
-    if (subCharacteristic != null) {
-      json.prop("subCharacteristic", subCharacteristic.name());
-      DebtCharacteristic characteristic = characteristicById(((DefaultDebtCharacteristic) subCharacteristic).parentId());
-      json.prop("characteristic", characteristic != null ? characteristic.name() : null);
-    }
-  }
-
-  @CheckForNull
-  private DebtCharacteristic characteristicById(@Nullable Integer id) {
-    if (id != null) {
-      return debtModel.characteristicById(id);
-    }
-    return null;
-  }
-
-  private void writeTransitions(Issue issue, JsonWriter json) {
-    json.name("transitions").beginArray();
-    if (UserSession.get().isLoggedIn()) {
-      List<Transition> transitions = issueService.listTransitions(issue, UserSession.get());
-      for (Transition transition : transitions) {
-        json.value(transition.key());
-      }
-    }
-    json.endArray();
-  }
-
-  private void writeActions(DefaultIssue issue, JsonWriter json) {
-    json.name("actions").beginArray();
-    for (String action : actions(issue)) {
-      json.value(action);
-    }
-    json.endArray();
-  }
-
-  // TODO all available actions should be returned by ActionService or another service
-  private List<String> actions(DefaultIssue issue) {
-    List<String> actions = newArrayList();
-    String login = UserSession.get().login();
-    if (login != null) {
-      actions.add("comment");
-      if (issue.resolution() == null) {
-        actions.add("assign");
-        if (!login.equals(issue.assignee())) {
-          actions.add("assign_to_me");
-        }
-        actions.add("plan");
-        String projectKey = issue.projectKey();
-        if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) {
-          actions.add("set_severity");
-        }
-        for (Action action : actionService.listAvailableActions(issue)) {
-          actions.add(action.key());
-        }
-      }
-    }
-    return actions;
-  }
-
-  private void writeComments(IssueQueryResult queryResult, Issue issue, JsonWriter json) {
-    json.name("comments").beginArray();
-    String login = UserSession.get().login();
-    for (IssueComment comment : issue.comments()) {
-      String userLogin = comment.userLogin();
-      User user = userLogin != null ? queryResult.user(userLogin) : null;
-      json
-        .beginObject()
-        .prop("key", comment.key())
-        .prop("userName", user != null ? user.name() : null)
-        .prop("raw", comment.markdownText())
-        .prop("html", Markdown.convertToHtml(comment.markdownText()))
-        .prop("createdAt", DateUtils.formatDateTime(comment.createdAt()))
-        .prop("fCreatedAge", formatAgeDate(comment.createdAt()))
-        .prop("updatable", login != null && login.equals(userLogin))
-        .endObject();
-    }
-    json.endArray();
-  }
-
-  private void writeChangelog(Issue issue, JsonWriter json) {
-    json.name("changelog").beginArray()
-      .beginObject()
-      .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
-      .prop("fCreationDate", formatDate(issue.creationDate()))
-      .name("diffs").beginArray()
-      .value(i18n.message(UserSession.get().locale(), "created", null))
-      .endArray()
-      .endObject();
-
-    IssueChangelog changelog = issueChangelogService.changelog(issue);
-    for (FieldDiffs diffs : changelog.changes()) {
-      User user = changelog.user(diffs);
-      json
-        .beginObject()
-        .prop("userName", user != null ? user.name() : null)
-        .prop("creationDate", DateUtils.formatDateTime(diffs.creationDate()))
-        .prop("fCreationDate", formatDate(diffs.creationDate()));
-      json.name("diffs").beginArray();
-      List<String> diffsFormatted = issueChangelogService.formatDiffs(diffs);
-      for (String diff : diffsFormatted) {
-        json.value(diff);
-      }
-      json.endArray();
-      json.endObject();
-    }
-    json.endArray();
-  }
-
-  private void addUserWithLabel(IssueQueryResult result, @Nullable String value, String field, JsonWriter json) {
-    if (value != null) {
-      User user = result.user(value);
-      json
-        .prop(field, value)
-        .prop(field + "Name", user != null ? user.name() : null);
-    }
-  }
-
-  @CheckForNull
-  private String formatDate(@Nullable Date date) {
-    if (date != null) {
-      return i18n.formatDateTime(UserSession.get().locale(), date);
-    }
-    return null;
-  }
-
-  @CheckForNull
-  private String formatAgeDate(@Nullable Date date) {
-    if (date != null) {
-      return i18n.ageFromNow(UserSession.get().locale(), date);
-    }
-    return null;
-  }
-}
index bd14da41b4b03cca67140dc73353737fef1e23e0..e371a0380ffde24ef6eb26dbb9b0c1607fd3ba5a 100644 (file)
  */
 package org.sonar.server.issue.ws;
 
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
 
 public class IssuesWs implements WebService {
 
-  private final IssueShowWsHandler showHandler;
+  private final IssueShowAction showHandler;
 
-  public IssuesWs(IssueShowWsHandler showHandler) {
+  public IssuesWs(IssueShowAction showHandler) {
     this.showHandler = showHandler;
   }
 
@@ -33,13 +35,20 @@ public class IssuesWs implements WebService {
   public void define(Context context) {
     NewController controller = context.createController("api/issues");
     controller.setDescription("Coding rule issues");
+    controller.setSince("3.6");
+    showHandler.define(controller);
 
-    controller.createAction("show")
-      .setDescription("Detail of issue")
-      .setSince("4.2")
-      .setInternal(true)
-      .setHandler(showHandler)
-      .createParam("key", "Issue key");
+    WebService.NewAction search = controller.createAction("search")
+      .setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. Requires Browse permission on project(s).")
+      .setSince("3.6")
+      .setHandler(RailsHandler.INSTANCE);
+    search.createParam("issues")
+      .setDescription("Comma-separated list of issue keys.")
+      .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
+    search.createParam("severities")
+      .setDescription("Comma-separated list of severities.")
+      .setExampleValue("BLOCKER,CRITICAL")
+      .setPossibleValues(Severity.ALL.toArray(new String[Severity.ALL.size()]));
 
     controller.done();
   }
index 28bda726b6bf1791abb4c91f262d3aed14392a9d..c76338c9e57908e159c1f2ed00d5940418dd66d2 100644 (file)
  */
 package org.sonar.server.platform;
 
-import org.sonar.server.cluster.LocalNonBlockingWorkQueue;
-
-import org.sonar.server.rule2.RuleDao;
-import org.sonar.server.rule2.RuleService;
 import com.google.common.collect.Lists;
 import org.apache.commons.configuration.BaseConfiguration;
 import org.sonar.api.config.EmailSettings;
@@ -82,6 +78,7 @@ import org.sonar.jpa.session.DefaultDatabaseConnector;
 import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory;
 import org.sonar.server.authentication.ws.AuthenticationWs;
 import org.sonar.server.charts.ChartFactory;
+import org.sonar.server.cluster.LocalNonBlockingWorkQueue;
 import org.sonar.server.component.DefaultComponentFinder;
 import org.sonar.server.component.DefaultRubyComponentService;
 import org.sonar.server.db.EmbeddedDatabaseFactory;
@@ -91,10 +88,12 @@ import org.sonar.server.debt.*;
 import org.sonar.server.es.ESIndex;
 import org.sonar.server.es.ESNode;
 import org.sonar.server.issue.*;
+import org.sonar.server.issue.actionplan.ActionPlanService;
+import org.sonar.server.issue.actionplan.ActionPlanWs;
 import org.sonar.server.issue.filter.IssueFilterService;
+import org.sonar.server.issue.filter.IssueFilterWriter;
 import org.sonar.server.issue.filter.IssueFilterWs;
-import org.sonar.server.issue.ws.ActionPlanWs;
-import org.sonar.server.issue.ws.IssueShowWsHandler;
+import org.sonar.server.issue.ws.IssueShowAction;
 import org.sonar.server.issue.ws.IssuesWs;
 import org.sonar.server.notifications.NotificationCenter;
 import org.sonar.server.notifications.NotificationService;
@@ -114,6 +113,7 @@ import org.sonar.server.qualityprofile.ws.QProfileBackupWsHandler;
 import org.sonar.server.qualityprofile.ws.QProfilesWs;
 import org.sonar.server.rule.*;
 import org.sonar.server.rule.ws.*;
+import org.sonar.server.rule2.RuleService;
 import org.sonar.server.search.IndexUtils;
 import org.sonar.server.source.CodeColorizers;
 import org.sonar.server.source.DeprecatedSourceDecorator;
@@ -356,19 +356,27 @@ class ServerComponents {
     pico.addSingleton(IssueStatsFinder.class);
     pico.addSingleton(PublicRubyIssueService.class);
     pico.addSingleton(InternalRubyIssueService.class);
-    pico.addSingleton(ActionPlanService.class);
     pico.addSingleton(IssueChangelogService.class);
     pico.addSingleton(IssueNotifications.class);
     pico.addSingleton(ActionService.class);
     pico.addSingleton(Actions.class);
-    pico.addSingleton(IssueFilterSerializer.class);
-    pico.addSingleton(IssueFilterService.class);
     pico.addSingleton(IssueBulkChangeService.class);
     pico.addSingleton(IssueChangelogFormatter.class);
-    pico.addSingleton(IssueFilterWs.class);
-    pico.addSingleton(IssueShowWsHandler.class);
+    pico.addSingleton(IssueShowAction.class);
     pico.addSingleton(IssuesWs.class);
+
+    // issue filters
+    pico.addSingleton(IssueFilterService.class);
+    pico.addSingleton(IssueFilterSerializer.class);
+    pico.addSingleton(IssueFilterWs.class);
+    pico.addSingleton(IssueFilterWriter.class);
+    pico.addSingleton(org.sonar.server.issue.filter.AppAction.class);
+    pico.addSingleton(org.sonar.server.issue.filter.ShowAction.class);
+    pico.addSingleton(org.sonar.server.issue.filter.FavoritesAction.class);
+
+    // action plan
     pico.addSingleton(ActionPlanWs.class);
+    pico.addSingleton(ActionPlanService.class);
 
     // issues actions
     pico.addSingleton(AssignAction.class);
diff --git a/sonar-server/src/main/resources/org/sonar/server/authentication/ws/example-validate.json b/sonar-server/src/main/resources/org/sonar/server/authentication/ws/example-validate.json
new file mode 100644 (file)
index 0000000..df47e91
--- /dev/null
@@ -0,0 +1 @@
+{"validate": true}
diff --git a/sonar-server/src/main/resources/org/sonar/server/issue/actionplan/example-search.json b/sonar-server/src/main/resources/org/sonar/server/issue/actionplan/example-search.json
new file mode 100644 (file)
index 0000000..b96ee24
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "actionPlans": [
+    {
+      "key": "3f19de90-1521-4482-a737-a311758ff513",
+      "name": "Version 3.6",
+      "status": "OPEN",
+      "project": "java-sonar-runner-simple",
+      "userLogin": "admin",
+      "deadLine": "2013-12-31T00:00:00+0100",
+      "totalIssues": 1,
+      "unresolvedIssues": 0,
+      "createdAt": "2013-05-31T22:40:50+0200",
+      "updatedAt": "2013-05-31T22:40:50+0200"
+    },
+    {
+      "key": "8ab022c0-f0dc-41b7-a762-82502bda749f",
+      "name": "Version 3.5",
+      "status": "CLOSED",
+      "project": "java-sonar-runner-simple4",
+      "userLogin": "admin",
+      "totalIssues": 0,
+      "unresolvedIssues": 0,
+      "createdAt": "2013-05-31T22:40:30+0200",
+      "updatedAt": "2013-05-31T22:42:13+0200"
+    }
+  ]
+}
diff --git a/sonar-server/src/main/resources/org/sonar/server/issue/filter/example-show.json b/sonar-server/src/main/resources/org/sonar/server/issue/filter/example-show.json
new file mode 100644 (file)
index 0000000..e7f6e7a
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "filter": {
+    "id": 1,
+    "name": "My Filter",
+    "user": "admin",
+    "shared": true,
+    "query": "statuses=OPEN,REOPENED|sort=UPDATE_DATE|asc=false",
+    "canModify": true
+  }
+}
index 3b8222d789bb1b6eed4f15e0105cc8cafd00020b..308fd2429b902ec2e6c16dd9a3de6b55fbca4783 100644 (file)
@@ -219,7 +219,7 @@ class Api::IssuesController < Api::ApiController
     require_parameters :issue
 
     plan = nil
-    plan = params[:plan] if params[:plan] && !params[:plan].blank?
+    plan = params[:actionplan] if params[:actionplan] && !params[:actionplan].blank?
     result = Internal.issues.plan(params[:issue], plan)
     render_result_issue(result)
   end
index 9d37e5ac83e059ea7c748e6081518cda68e2adca..0af0de79a32b9f9ccc5fdc2045f6cc47d8deca36 100644 (file)
@@ -83,7 +83,7 @@ class IssueController < ApplicationController
     elsif action_type=='severity'
       issue_result = Internal.issues.setSeverity(issue_key, params[:severity])
     elsif action_type=='plan'
-      issue_result = Internal.issues.plan(issue_key, params[:plan])
+      issue_result = Internal.issues.plan(issue_key, params[:actionplan])
     elsif action_type=='unplan'
       issue_result = Internal.issues.plan(issue_key, nil)
     else
index 21be810466e919a6ea4a5f56c75e7190c8c3b906..d131b88e706bdc3c1dfd5a13f78a412622770695 100644 (file)
@@ -41,9 +41,7 @@ public class AuthenticationWsTest {
     WebService.Action validate = controller.action("validate");
     assertThat(validate).isNotNull();
     assertThat(validate.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(validate.since()).isEqualTo("3.3");
-    assertThat(validate.isPost()).isFalse();
-    assertThat(validate.isInternal()).isFalse();
+    assertThat(validate.responseExampleAsString()).isNotEmpty();
     assertThat(validate.params()).isEmpty();
   }
 }
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ActionPlanServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ActionPlanServiceTest.java
deleted file mode 100644 (file)
index 4ceb7c0..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.server.issue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.IssueQuery;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.issue.ActionPlanStats;
-import org.sonar.core.issue.DefaultActionPlan;
-import org.sonar.core.issue.IssueUpdater;
-import org.sonar.core.issue.db.*;
-import org.sonar.core.resource.ResourceDao;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceQuery;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.user.MockUserSession;
-import org.sonar.server.user.UserSession;
-
-import java.util.Collection;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class ActionPlanServiceTest {
-
-  @Mock
-  ActionPlanDao actionPlanDao;
-
-  @Mock
-  ActionPlanStatsDao actionPlanStatsDao;
-
-  @Mock
-  ResourceDao resourceDao;
-
-  @Mock
-  IssueDao issueDao;
-
-  @Mock
-  IssueUpdater issueUpdater;
-
-  @Mock
-  IssueStorage issueStorage;
-
-  String projectKey = "org.sonar.Sample";
-
-  UserSession projectAdministratorUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").addProjectPermissions(UserRole.ADMIN, projectKey);
-  UserSession projectUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").addProjectPermissions(UserRole.USER, projectKey);
-  UserSession unauthorizedUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas");
-
-  private ActionPlanService actionPlanService;
-
-  @Before
-  public void before() {
-    actionPlanService = new ActionPlanService(actionPlanDao, actionPlanStatsDao, resourceDao, issueDao, issueUpdater, issueStorage);
-  }
-
-  @Test
-  public void create() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
-
-    actionPlanService.create(actionPlan, projectAdministratorUserSession);
-    verify(actionPlanDao).save(any(ActionPlanDto.class));
-  }
-
-  @Test
-  public void create_required_admin_role() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
-
-    try {
-      actionPlanService.create(actionPlan, unauthorizedUserSession);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(ForbiddenException.class);
-    }
-    verifyZeroInteractions(actionPlanDao);
-  }
-
-  @Test
-  public void set_status() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-
-    ActionPlan result = actionPlanService.setStatus("ABCD", "CLOSED", projectAdministratorUserSession);
-    verify(actionPlanDao).update(any(ActionPlanDto.class));
-
-    assertThat(result).isNotNull();
-    assertThat(result.status()).isEqualTo("CLOSED");
-  }
-
-  @Test
-  public void update() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
-
-    actionPlanService.update(actionPlan, projectAdministratorUserSession);
-    verify(actionPlanDao).update(any(ActionPlanDto.class));
-  }
-
-  @Test
-  public void delete() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-    actionPlanService.delete("ABCD", projectAdministratorUserSession);
-    verify(actionPlanDao).delete("ABCD");
-  }
-
-  @Test
-  public void unplan_all_linked_issues_when_deleting_an_action_plan() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-
-    IssueDto issueDto = new IssueDto().setId(100L).setStatus(Issue.STATUS_OPEN).setRuleKey_unit_test_only("squid", "s100");
-    when(issueDao.selectIssues(any(IssueQuery.class))).thenReturn(newArrayList(issueDto));
-    when(issueUpdater.plan(any(DefaultIssue.class), eq((ActionPlan) null), any(IssueChangeContext.class))).thenReturn(true);
-
-    ArgumentCaptor<DefaultIssue> captor = ArgumentCaptor.forClass(DefaultIssue.class);
-    actionPlanService.delete("ABCD", projectAdministratorUserSession);
-    verify(actionPlanDao).delete("ABCD");
-    verify(issueUpdater).plan(captor.capture(), eq((ActionPlan) null), any(IssueChangeContext.class));
-    verify(issueStorage).save(newArrayList(captor.getAllValues()));
-  }
-
-  @Test
-  public void find_by_key() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-
-    ActionPlan result = actionPlanService.findByKey("ABCD", projectUserSession);
-    assertThat(result).isNotNull();
-    assertThat(result.key()).isEqualTo("ABCD");
-  }
-
-  @Test
-  public void return_null_if_no_action_plan_when_find_by_key() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(null);
-    assertThat(actionPlanService.findByKey("ABCD", projectUserSession)).isNull();
-  }
-
-  @Test
-  public void find_by_keys() {
-    when(actionPlanDao.findByKeys(newArrayList("ABCD"))).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
-    Collection<ActionPlan> results = actionPlanService.findByKeys(newArrayList("ABCD"));
-    assertThat(results).hasSize(1);
-    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
-  }
-
-  @Test
-  public void find_open_by_project_key() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-    when(actionPlanDao.findOpenByProjectId(1l)).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
-    Collection<ActionPlan> results = actionPlanService.findOpenByProjectKey(projectKey, projectUserSession);
-    assertThat(results).hasSize(1);
-    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
-  }
-
-  @Test
-  public void find_open_by_project_key_required_user_role() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
-    when(actionPlanDao.findOpenByProjectId(1l)).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
-
-    try {
-      actionPlanService.findOpenByProjectKey(projectKey, unauthorizedUserSession);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(ForbiddenException.class);
-    }
-    verifyZeroInteractions(actionPlanDao);
-  }
-
-  @Test(expected = NotFoundException.class)
-  public void throw_exception_if_project_not_found_when_find_open_by_project_key() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
-    actionPlanService.findOpenByProjectKey("<Unkown>", projectUserSession);
-  }
-
-  @Test
-  public void find_action_plan_stats() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setId(1L).setKey(projectKey));
-    when(actionPlanStatsDao.findByProjectId(1L)).thenReturn(newArrayList(new ActionPlanStatsDto()));
-
-    Collection<ActionPlanStats> results = actionPlanService.findActionPlanStats(projectKey, projectUserSession);
-    assertThat(results).hasSize(1);
-  }
-
-  @Test(expected = NotFoundException.class)
-  public void throw_exception_if_project_not_found_when_find_open_action_plan_stats() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
-
-    actionPlanService.findActionPlanStats(projectKey, projectUserSession);
-  }
-
-}
index 75f50312e61f942c2fabe25398686728c300d573..2ec0c9bb18345e86ff7d4b67d92c450b0aacadac 100644 (file)
@@ -43,6 +43,7 @@ import org.sonar.core.persistence.MyBatis;
 import org.sonar.core.resource.ResourceDao;
 import org.sonar.core.rule.DefaultRuleFinder;
 import org.sonar.core.user.DefaultUser;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 
 import java.util.Collections;
 import java.util.List;
index 44b5cc3df88ba0c4c7a7f7cee8eaa119a7aa3ea9..f637730d0ecca5612cf7636cec6a4df2c3ce3e89 100644 (file)
@@ -41,6 +41,7 @@ import org.sonar.core.resource.ResourceDao;
 import org.sonar.core.resource.ResourceDto;
 import org.sonar.core.resource.ResourceQuery;
 import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.filter.IssueFilterService;
 import org.sonar.server.user.UserSession;
 
index 3f842e788f4788f5e82a0c9d4583f2109fc4612c..f62b7272acbd2242af6817b0f5b00ba1a1e102b5 100644 (file)
@@ -50,6 +50,7 @@ import org.sonar.core.resource.ResourceDto;
 import org.sonar.core.resource.ResourceQuery;
 import org.sonar.core.user.AuthorizationDao;
 import org.sonar.core.user.DefaultUser;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
 import java.util.Collections;
index b19ee382493baf6aba2a1a07eb4c11af1415451c..e7d1b7ef08e5aa332ed1763dca472d73415c4f14 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.IssueChangeContext;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
 import java.util.List;
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java
new file mode 100644 (file)
index 0000000..a7bb7ec
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.actionplan;
+
+import org.fest.assertions.Fail;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.ActionPlanStats;
+import org.sonar.core.issue.DefaultActionPlan;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.db.*;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.resource.ResourceQuery;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.user.UserSession;
+
+import java.util.Collection;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ActionPlanServiceTest {
+
+  @Mock
+  ActionPlanDao actionPlanDao;
+
+  @Mock
+  ActionPlanStatsDao actionPlanStatsDao;
+
+  @Mock
+  ResourceDao resourceDao;
+
+  @Mock
+  IssueDao issueDao;
+
+  @Mock
+  IssueUpdater issueUpdater;
+
+  @Mock
+  IssueStorage issueStorage;
+
+  String projectKey = "org.sonar.Sample";
+
+  UserSession projectAdministratorUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").addProjectPermissions(UserRole.ADMIN, projectKey);
+  UserSession projectUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").addProjectPermissions(UserRole.USER, projectKey);
+  UserSession unauthorizedUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas");
+
+  private ActionPlanService actionPlanService;
+
+  @Before
+  public void before() {
+    actionPlanService = new ActionPlanService(actionPlanDao, actionPlanStatsDao, resourceDao, issueDao, issueUpdater, issueStorage);
+  }
+
+  @Test
+  public void create() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
+
+    actionPlanService.create(actionPlan, projectAdministratorUserSession);
+    verify(actionPlanDao).save(any(ActionPlanDto.class));
+  }
+
+  @Test
+  public void create_required_admin_role() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
+
+    try {
+      actionPlanService.create(actionPlan, unauthorizedUserSession);
+      Fail.fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(ForbiddenException.class);
+    }
+    verifyZeroInteractions(actionPlanDao);
+  }
+
+  @Test
+  public void set_status() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+
+    ActionPlan result = actionPlanService.setStatus("ABCD", "CLOSED", projectAdministratorUserSession);
+    verify(actionPlanDao).update(any(ActionPlanDto.class));
+
+    assertThat(result).isNotNull();
+    assertThat(result.status()).isEqualTo("CLOSED");
+  }
+
+  @Test
+  public void update() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
+
+    actionPlanService.update(actionPlan, projectAdministratorUserSession);
+    verify(actionPlanDao).update(any(ActionPlanDto.class));
+  }
+
+  @Test
+  public void delete() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+    actionPlanService.delete("ABCD", projectAdministratorUserSession);
+    verify(actionPlanDao).delete("ABCD");
+  }
+
+  @Test
+  public void unplan_all_linked_issues_when_deleting_an_action_plan() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+
+    IssueDto issueDto = new IssueDto().setId(100L).setStatus(Issue.STATUS_OPEN).setRuleKey_unit_test_only("squid", "s100");
+    when(issueDao.selectIssues(any(IssueQuery.class))).thenReturn(newArrayList(issueDto));
+    when(issueUpdater.plan(any(DefaultIssue.class), eq((ActionPlan) null), any(IssueChangeContext.class))).thenReturn(true);
+
+    ArgumentCaptor<DefaultIssue> captor = ArgumentCaptor.forClass(DefaultIssue.class);
+    actionPlanService.delete("ABCD", projectAdministratorUserSession);
+    verify(actionPlanDao).delete("ABCD");
+    verify(issueUpdater).plan(captor.capture(), eq((ActionPlan) null), any(IssueChangeContext.class));
+    verify(issueStorage).save(newArrayList(captor.getAllValues()));
+  }
+
+  @Test
+  public void find_by_key() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD").setProjectKey_unit_test_only(projectKey));
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+
+    ActionPlan result = actionPlanService.findByKey("ABCD", projectUserSession);
+    assertThat(result).isNotNull();
+    assertThat(result.key()).isEqualTo("ABCD");
+  }
+
+  @Test
+  public void return_null_if_no_action_plan_when_find_by_key() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(null);
+    assertThat(actionPlanService.findByKey("ABCD", projectUserSession)).isNull();
+  }
+
+  @Test
+  public void find_by_keys() {
+    when(actionPlanDao.findByKeys(newArrayList("ABCD"))).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
+    Collection<ActionPlan> results = actionPlanService.findByKeys(newArrayList("ABCD"));
+    assertThat(results).hasSize(1);
+    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
+  }
+
+  @Test
+  public void find_open_by_project_key() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+    when(actionPlanDao.findOpenByProjectId(1l)).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
+    Collection<ActionPlan> results = actionPlanService.findOpenByProjectKey(projectKey, projectUserSession);
+    assertThat(results).hasSize(1);
+    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
+  }
+
+  @Test
+  public void find_open_by_project_key_required_user_role() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey(projectKey).setId(1l));
+    when(actionPlanDao.findOpenByProjectId(1l)).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
+
+    try {
+      actionPlanService.findOpenByProjectKey(projectKey, unauthorizedUserSession);
+      Fail.fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(ForbiddenException.class);
+    }
+    verifyZeroInteractions(actionPlanDao);
+  }
+
+  @Test(expected = NotFoundException.class)
+  public void throw_exception_if_project_not_found_when_find_open_by_project_key() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
+    actionPlanService.findOpenByProjectKey("<Unkown>", projectUserSession);
+  }
+
+  @Test
+  public void find_action_plan_stats() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setId(1L).setKey(projectKey));
+    when(actionPlanStatsDao.findByProjectId(1L)).thenReturn(newArrayList(new ActionPlanStatsDto()));
+
+    Collection<ActionPlanStats> results = actionPlanService.findActionPlanStats(projectKey, projectUserSession);
+    assertThat(results).hasSize(1);
+  }
+
+  @Test(expected = NotFoundException.class)
+  public void throw_exception_if_project_not_found_when_find_open_action_plan_stats() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
+
+    actionPlanService.findActionPlanStats(projectKey, projectUserSession);
+  }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanWsTest.java b/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanWsTest.java
new file mode 100644 (file)
index 0000000..7d0677e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.actionplan;
+
+import org.junit.Test;
+import org.sonar.api.server.ws.RailsHandler;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.ws.WsTester;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ActionPlanWsTest {
+
+  WsTester tester = new WsTester(new ActionPlanWs());
+
+  @Test
+  public void define_controller() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+    assertThat(controller).isNotNull();
+    assertThat(controller.description()).isNotEmpty();
+    assertThat(controller.actions()).hasSize(6);
+  }
+
+  @Test
+  public void define_search_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+
+    WebService.Action action = controller.action("search");
+    assertThat(action).isNotNull();
+    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.params()).hasSize(1);
+  }
+
+  @Test
+  public void define_create_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+
+    WebService.Action action = controller.action("create");
+    assertThat(action).isNotNull();
+    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(action.params()).hasSize(4);
+  }
+
+  @Test
+  public void define_delete_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+
+    WebService.Action action = controller.action("delete");
+    assertThat(action).isNotNull();
+    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(action.params()).hasSize(1);
+  }
+
+  @Test
+  public void define_update_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+
+    WebService.Action action = controller.action("update");
+    assertThat(action).isNotNull();
+    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(action.params()).hasSize(4);
+  }
+
+  @Test
+  public void define_open_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+
+    WebService.Action action = controller.action("open");
+    assertThat(action).isNotNull();
+    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(action.params()).hasSize(1);
+  }
+
+  @Test
+  public void define_close_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/action_plans");
+
+    WebService.Action action = controller.action("close");
+    assertThat(action).isNotNull();
+    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(action.params()).hasSize(1);
+  }
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/filter/AppActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/filter/AppActionTest.java
new file mode 100644 (file)
index 0000000..b9b9288
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Arrays;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AppActionTest {
+
+  @Mock
+  IssueFilterService service;
+
+  IssueFilterWriter writer = new IssueFilterWriter();
+
+  AppAction action;
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    action = new AppAction(service, writer);
+    tester = new WsTester(new IssueFilterWs(action, mock(ShowAction.class), mock(FavoritesAction.class)));
+  }
+
+  @Test
+  public void anonymous_app() throws Exception {
+    MockUserSession.set().setLogin(null);
+    tester.newRequest("app").execute().assertJson(getClass(), "anonymous_page.json");
+  }
+
+  @Test
+  public void logged_in_app() throws Exception {
+    MockUserSession.set().setLogin("eric").setUserId(123);
+    tester.newRequest("app").execute()
+      .assertJson(getClass(), "logged_in_page.json");
+  }
+
+  @Test
+  public void logged_in_app_with_favorites() throws Exception {
+    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
+    when(service.findFavoriteFilters(session)).thenReturn(Arrays.asList(
+      new DefaultIssueFilter().setId(6L).setName("My issues"),
+      new DefaultIssueFilter().setId(13L).setName("Blocker issues")
+    ));
+    tester.newRequest("app").execute()
+      .assertJson(getClass(), "logged_in_page_with_favorites.json");
+  }
+
+  @Test
+  public void logged_in_app_with_selected_filter() throws Exception {
+    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
+    when(service.find(13L, session)).thenReturn(
+      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER").setUser("eric")
+    );
+
+    tester.newRequest("app").setParam("id", "13").execute()
+      .assertJson(getClass(), "logged_in_page_with_selected_filter.json");
+  }
+
+  @Test
+  public void app_selected_filter_can_not_be_modified() throws Exception {
+    // logged-in user is 'eric' but filter is owned by 'simon'
+    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123).setGlobalPermissions("none");
+    when(service.find(13L, session)).thenReturn(
+      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER").setUser("simon").setShared(true)
+    );
+
+    tester.newRequest("app").setParam("id", "13").execute()
+      .assertJson(getClass(), "selected_filter_can_not_be_modified.json");
+  }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/filter/FavoritesActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/filter/FavoritesActionTest.java
new file mode 100644 (file)
index 0000000..cbde7f9
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Arrays;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FavoritesActionTest {
+
+  @Mock
+  IssueFilterService service;
+
+  @Mock
+  IssueFilterWriter writer;
+
+  FavoritesAction action;
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    action = new FavoritesAction(service);
+    tester = new WsTester(new IssueFilterWs(mock(AppAction.class), mock(ShowAction.class), action));
+  }
+
+  @Test
+  public void favorites_of_anonymous() throws Exception {
+    MockUserSession.set();
+
+    tester.newRequest("favorites").execute()
+      .assertJson("{'favoriteFilters': []}");
+  }
+
+  @Test
+  public void favorites_of_logged_in_user() throws Exception {
+    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
+    when(service.findFavoriteFilters(session)).thenReturn(Arrays.asList(
+      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER").setUser("simon").setShared(true)
+    ));
+
+    tester.newRequest("favorites").execute()
+      .assertJson("{'favoriteFilters': [{'id': 13, 'name': 'Blocker issues', 'user': 'simon', 'shared': true}]}");
+  }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWriterTest.java b/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterWriterTest.java
new file mode 100644 (file)
index 0000000..922b76f
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.json.JSONException;
+import org.junit.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.user.UserSession;
+
+import java.io.StringWriter;
+
+public class IssueFilterWriterTest {
+
+  IssueFilterWriter writer = new IssueFilterWriter();
+
+  @Test
+  public void write_filter() throws Exception {
+    UserSession userSession = MockUserSession.set();
+    test(userSession,
+      new DefaultIssueFilter()
+        .setId(13L)
+        .setName("Blocker issues")
+        .setDescription("All Blocker Issues")
+        .setShared(true)
+        .setUser("simon")
+        .setData("severity=BLOCKER"),
+      "{\"filter\":{\n" +
+        "      \"id\":13,\n" +
+        "        \"name\":\"Blocker issues\",\n" +
+        "        \"description\":\"All Blocker Issues\",\n" +
+        "        \"shared\":true,\n" +
+        "        \"query\":\"severity=BLOCKER\",\n" +
+        "        \"user\":\"simon\",\n" +
+        "        \"canModify\":false\n" +
+        "    }}"
+    );
+  }
+
+  @Test
+  public void can_modify_if_logged_user_own_filter() throws Exception {
+    UserSession userSession = MockUserSession.set().setLogin("simon");
+    test(userSession,
+      new DefaultIssueFilter()
+        .setId(13L)
+        .setName("Blocker issues")
+        .setDescription("All Blocker Issues")
+        .setShared(true)
+        .setUser("simon")
+        .setData("severity=BLOCKER"),
+      "{\"filter\":{\n" +
+        "      \"id\":13,\n" +
+        "        \"name\":\"Blocker issues\",\n" +
+        "        \"description\":\"All Blocker Issues\",\n" +
+        "        \"shared\":true,\n" +
+        "        \"query\":\"severity=BLOCKER\",\n" +
+        "        \"user\":\"simon\",\n" +
+        "        \"canModify\":true\n" +
+        "    }}"
+    );
+  }
+
+  @Test
+  public void can_modify_if_logged_user_has_permission() throws Exception {
+    UserSession userSession = MockUserSession.set().setLogin("simon").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+    test(userSession,
+      new DefaultIssueFilter()
+        .setId(13L)
+        .setName("Blocker issues")
+        .setDescription("All Blocker Issues")
+        .setShared(true)
+        .setUser("julien")
+        .setData("severity=BLOCKER"),
+      "{\"filter\":{\n" +
+        "      \"id\":13,\n" +
+        "        \"name\":\"Blocker issues\",\n" +
+        "        \"description\":\"All Blocker Issues\",\n" +
+        "        \"shared\":true,\n" +
+        "        \"query\":\"severity=BLOCKER\",\n" +
+        "        \"user\":\"julien\",\n" +
+        "        \"canModify\":true\n" +
+        "    }}"
+    );
+  }
+
+  private void test(UserSession userSession, DefaultIssueFilter filter, String expected) throws JSONException {
+    StringWriter output = new StringWriter();
+    JsonWriter jsonWriter = JsonWriter.of(output);
+    jsonWriter.beginObject();
+    writer.write(userSession, filter, jsonWriter);
+    jsonWriter.endObject();
+    JSONAssert.assertEquals(output.toString(), expected, true);
+  }
+}
index 3eb495bbb7fbb20f6d44cd5705a4cd14668b8c53..82125ccc627841039f2817ab85c45ec6af962e92 100644 (file)
  */
 package org.sonar.server.issue.filter;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.core.issue.DefaultIssueFilter;
-import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.user.MockUserSession;
 import org.sonar.server.ws.WsTester;
 
-import java.util.Arrays;
-
 import static org.fest.assertions.Assertions.assertThat;
-import static org.fest.assertions.Fail.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
+@RunWith(MockitoJUnitRunner.class)
 public class IssueFilterWsTest {
 
-  IssueFilterService service = mock(IssueFilterService.class);
-  IssueFilterWs ws = new IssueFilterWs(service);
-  WsTester tester = new WsTester(ws);
+  @Mock
+  IssueFilterService service;
+
+  @Mock
+  IssueFilterWriter issueFilterWriter;
+
+  IssueFilterWs ws;
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    ws = new IssueFilterWs(new AppAction(service, issueFilterWriter), new ShowAction(service, issueFilterWriter), new FavoritesAction(service));
+    tester = new WsTester(ws);
+  }
 
   @Test
   public void define_ws() throws Exception {
@@ -48,127 +57,16 @@ public class IssueFilterWsTest {
 
     WebService.Action app = controller.action("app");
     assertThat(app).isNotNull();
-    assertThat(app.handler()).isNotNull();
-    assertThat(app.isPost()).isFalse();
-    assertThat(app.isInternal()).isTrue();
-    assertThat(app.since()).isEqualTo("4.2");
     assertThat(app.params()).hasSize(1);
 
-    WebService.Param appId = app.param("id");
-    assertThat(appId).isNotNull();
-    assertThat(appId.description()).isNotNull();
-    assertThat(appId.isRequired()).isFalse();
-
     WebService.Action show = controller.action("show");
     assertThat(show).isNotNull();
-    assertThat(show.handler()).isNotNull();
-    assertThat(show.isPost()).isFalse();
-    assertThat(show.isInternal()).isFalse();
-    assertThat(show.since()).isEqualTo("4.2");
+    assertThat(show.responseExampleAsString()).isNotEmpty();
     assertThat(show.params()).hasSize(1);
 
-    WebService.Param showId = show.param("id");
-    assertThat(showId).isNotNull();
-    assertThat(showId.description()).isNotNull();
-    assertThat(showId.isRequired()).isTrue();
-
     WebService.Action favorites = controller.action("favorites");
     assertThat(favorites).isNotNull();
-    assertThat(favorites.handler()).isNotNull();
-    assertThat(favorites.isPost()).isFalse();
-    assertThat(favorites.isInternal()).isFalse();
-    assertThat(favorites.since()).isEqualTo("4.2");
     assertThat(favorites.params()).isEmpty();
   }
 
-  @Test
-  public void anonymous_app() throws Exception {
-    MockUserSession.set().setLogin(null);
-    tester.newRequest("app").execute().assertJson(getClass(), "anonymous_page.json");
-  }
-
-  @Test
-  public void logged_in_app() throws Exception {
-    MockUserSession.set().setLogin("eric").setUserId(123);
-    tester.newRequest("app").execute()
-      .assertJson(getClass(), "logged_in_page.json");
-  }
-
-  @Test
-  public void logged_in_app_with_favorites() throws Exception {
-    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
-    when(service.findFavoriteFilters(session)).thenReturn(Arrays.asList(
-      new DefaultIssueFilter().setId(6L).setName("My issues"),
-      new DefaultIssueFilter().setId(13L).setName("Blocker issues")
-    ));
-    tester.newRequest("app").execute()
-      .assertJson(getClass(), "logged_in_page_with_favorites.json");
-  }
-
-  @Test
-  public void logged_in_app_with_selected_filter() throws Exception {
-    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
-    when(service.find(13L, session)).thenReturn(
-      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER").setUser("eric")
-    );
-
-    tester.newRequest("app").setParam("id", "13").execute()
-      .assertJson(getClass(), "logged_in_page_with_selected_filter.json");
-  }
-
-  @Test
-  public void app_selected_filter_can_not_be_modified() throws Exception {
-    // logged-in user is 'eric' but filter is owned by 'simon'
-    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123).setGlobalPermissions("none");
-    when(service.find(13L, session)).thenReturn(
-      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER").setUser("simon").setShared(true)
-    );
-
-    tester.newRequest("app").setParam("id", "13").execute()
-      .assertJson(getClass(), "selected_filter_can_not_be_modified.json");
-  }
-
-  @Test
-  public void show_filter() throws Exception {
-    // logged-in user is 'eric' but filter is owned by 'simon'
-    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123).setGlobalPermissions("none");
-    when(service.find(13L, session)).thenReturn(
-      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setDescription("All Blocker Issues").setData("severity=BLOCKER").setUser("simon").setShared(true)
-    );
-
-    tester.newRequest("show").setParam("id", "13").execute()
-      .assertJson(getClass(), "show_filter.json");
-  }
-
-  @Test
-  public void show_unknown_filter() throws Exception {
-    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123).setGlobalPermissions("none");
-    when(service.find(42L, session)).thenThrow(new NotFoundException("Filter 42 does not exist"));
-
-    try {
-      tester.newRequest("show").setParam("id", "42").execute();
-      fail();
-    } catch (NotFoundException e) {
-      assertThat(e).hasMessage("Filter 42 does not exist");
-    }
-  }
-
-  @Test
-  public void favorites_of_anonymous() throws Exception {
-    MockUserSession.set();
-
-    tester.newRequest("favorites").execute()
-      .assertJson("{'favoriteFilters': []}");
-  }
-
-  @Test
-  public void favorites_of_logged_in_user() throws Exception {
-    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123);
-    when(service.findFavoriteFilters(session)).thenReturn(Arrays.asList(
-      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setData("severity=BLOCKER").setUser("simon").setShared(true)
-    ));
-
-    tester.newRequest("favorites").execute()
-      .assertJson("{'favoriteFilters': [{'id': 13, 'name': 'Blocker issues', 'user': 'simon', 'shared': true}]}");
-  }
 }
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/filter/ShowActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/filter/ShowActionTest.java
new file mode 100644 (file)
index 0000000..fde9db1
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.issue.filter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.core.issue.DefaultIssueFilter;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ShowActionTest {
+
+  @Mock
+  IssueFilterService service;
+
+  IssueFilterWriter writer = new IssueFilterWriter();
+
+  ShowAction action;
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    action = new ShowAction(service, writer);
+    tester = new WsTester(new IssueFilterWs(mock(AppAction.class), action, mock(FavoritesAction.class)));
+  }
+
+  @Test
+  public void show_filter() throws Exception {
+    // logged-in user is 'eric' but filter is owned by 'simon'
+    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123).setGlobalPermissions("none");
+    when(service.find(13L, session)).thenReturn(
+      new DefaultIssueFilter().setId(13L).setName("Blocker issues").setDescription("All Blocker Issues").setData("severity=BLOCKER").setUser("simon").setShared(true)
+    );
+
+    tester.newRequest("show").setParam("id", "13").execute()
+      .assertJson(getClass(), "show_filter.json");
+  }
+
+  @Test
+  public void show_unknown_filter() throws Exception {
+    MockUserSession session = MockUserSession.set().setLogin("eric").setUserId(123).setGlobalPermissions("none");
+    when(service.find(42L, session)).thenThrow(new NotFoundException("Filter 42 does not exist"));
+
+    try {
+      tester.newRequest("show").setParam("id", "42").execute();
+      fail();
+    } catch (NotFoundException e) {
+      assertThat(e).hasMessage("Filter 42 does not exist");
+    }
+  }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/ActionPlanWsTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/ActionPlanWsTest.java
deleted file mode 100644 (file)
index ec3223d..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.server.issue.ws;
-
-import org.junit.Test;
-import org.sonar.api.server.ws.RailsHandler;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.server.ws.WsTester;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class ActionPlanWsTest {
-
-  WsTester tester = new WsTester(new ActionPlanWs());
-
-  @Test
-  public void define_action_plan_controller() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-    assertThat(controller).isNotNull();
-    assertThat(controller.description()).isNotEmpty();
-    assertThat(controller.actions()).hasSize(6);
-  }
-
-  @Test
-  public void define_search_action() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-
-    WebService.Action action = controller.action("search");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.since()).isEqualTo("3.6");
-    assertThat(action.isPost()).isFalse();
-    assertThat(action.isInternal()).isFalse();
-    assertThat(action.params()).hasSize(1);
-
-    WebService.Param project = action.param("project");
-    assertThat(project).isNotNull();
-    assertThat(project.description()).isNotNull();
-    assertThat(project.exampleValue()).isNotNull();
-    assertThat(project.isRequired()).isTrue();
-  }
-
-  @Test
-  public void define_create_action() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-
-    WebService.Action action = controller.action("create");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.since()).isEqualTo("3.6");
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.isInternal()).isFalse();
-    assertThat(action.params()).hasSize(4);
-
-    WebService.Param name = action.param("name");
-    assertThat(name).isNotNull();
-    assertThat(name.description()).isNotNull();
-    assertThat(name.exampleValue()).isNotNull();
-    assertThat(name.isRequired()).isTrue();
-
-    WebService.Param description = action.param("description");
-    assertThat(description).isNotNull();
-    assertThat(description.description()).isNotNull();
-    assertThat(description.isRequired()).isFalse();
-
-    WebService.Param project = action.param("project");
-    assertThat(project).isNotNull();
-    assertThat(project.description()).isNotNull();
-    assertThat(project.exampleValue()).isNotNull();
-    assertThat(project.isRequired()).isTrue();
-
-    WebService.Param deadLine = action.param("deadLine");
-    assertThat(deadLine).isNotNull();
-    assertThat(deadLine.description()).isNotNull();
-    assertThat(deadLine.exampleValue()).isNotNull();
-    assertThat(deadLine.isRequired()).isFalse();
-  }
-
-  @Test
-  public void define_delete_action() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-
-    WebService.Action action = controller.action("delete");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.since()).isEqualTo("3.6");
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.isInternal()).isFalse();
-    assertThat(action.params()).hasSize(1);
-
-    WebService.Param key = action.param("key");
-    assertThat(key).isNotNull();
-    assertThat(key.description()).isNotNull();
-    assertThat(key.exampleValue()).isNotNull();
-    assertThat(key.isRequired()).isTrue();
-  }
-
-  @Test
-  public void define_update_action() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-
-    WebService.Action action = controller.action("update");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.since()).isEqualTo("3.6");
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.isInternal()).isFalse();
-    assertThat(action.params()).hasSize(4);
-
-    WebService.Param project = action.param("key");
-    assertThat(project).isNotNull();
-    assertThat(project.description()).isNotNull();
-    assertThat(project.exampleValue()).isNotNull();
-    assertThat(project.isRequired()).isTrue();
-
-    WebService.Param name = action.param("name");
-    assertThat(name).isNotNull();
-    assertThat(name.description()).isNotNull();
-    assertThat(name.exampleValue()).isNotNull();
-    assertThat(name.isRequired()).isTrue();
-
-    WebService.Param description = action.param("description");
-    assertThat(description).isNotNull();
-    assertThat(description.description()).isNotNull();
-    assertThat(description.isRequired()).isFalse();
-
-    WebService.Param deadLine = action.param("deadLine");
-    assertThat(deadLine).isNotNull();
-    assertThat(deadLine.description()).isNotNull();
-    assertThat(deadLine.exampleValue()).isNotNull();
-    assertThat(deadLine.isRequired()).isFalse();
-  }
-
-  @Test
-  public void define_open_action() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-
-    WebService.Action action = controller.action("open");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.since()).isEqualTo("3.6");
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.isInternal()).isFalse();
-    assertThat(action.params()).hasSize(1);
-
-    WebService.Param key = action.param("key");
-    assertThat(key).isNotNull();
-    assertThat(key.description()).isNotNull();
-    assertThat(key.exampleValue()).isNotNull();
-    assertThat(key.isRequired()).isTrue();
-  }
-
-  @Test
-  public void define_close_action() throws Exception {
-    WebService.Controller controller = tester.controller("api/action_plans");
-
-    WebService.Action action = controller.action("close");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.since()).isEqualTo("3.6");
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.isInternal()).isFalse();
-    assertThat(action.params()).hasSize(1);
-
-    WebService.Param key = action.param("key");
-    assertThat(key).isNotNull();
-    assertThat(key.description()).isNotNull();
-    assertThat(key.exampleValue()).isNotNull();
-    assertThat(key.isRequired()).isTrue();
-  }
-}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java
new file mode 100644 (file)
index 0000000..20d023e
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.issue.ws;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.component.Component;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueFinder;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.action.Action;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.DefaultIssueComment;
+import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.user.User;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.DefaultActionPlan;
+import org.sonar.core.issue.DefaultIssueQueryResult;
+import org.sonar.core.issue.workflow.Transition;
+import org.sonar.core.user.DefaultUser;
+import org.sonar.server.debt.DebtModelService;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.IssueChangelog;
+import org.sonar.server.issue.IssueChangelogService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class IssueShowActionTest {
+
+  @Mock
+  IssueFinder issueFinder;
+
+  @Mock
+  IssueService issueService;
+
+  @Mock
+  IssueChangelogService issueChangelogService;
+
+  @Mock
+  ActionService actionService;
+
+  @Mock
+  DebtModelService debtModel;
+
+  @Mock
+  I18n i18n;
+
+  @Mock
+  Durations durations;
+
+  List<Issue> issues;
+  DefaultIssueQueryResult result;
+
+  private Date issue_creation_date;
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    issues = new ArrayList<Issue>();
+    result = new DefaultIssueQueryResult(issues);
+    result.addRules(newArrayList(Rule.create("squid", "AvoidCycle").setName("Avoid cycle")));
+    when(issueFinder.find(any(IssueQuery.class))).thenReturn(result);
+
+    when(issueChangelogService.changelog(any(Issue.class))).thenReturn(mock(IssueChangelog.class));
+
+    issue_creation_date = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
+    when(i18n.formatDateTime(any(Locale.class), eq(issue_creation_date))).thenReturn("Jan 22, 2014 10:03 AM");
+
+    when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created");
+
+    tester = new WsTester(new IssuesWs(new IssueShowAction(issueFinder, issueService, issueChangelogService, actionService, debtModel, i18n, durations)));
+  }
+
+  @Test
+  public void show_issue() throws Exception {
+    String issueKey = "ABCD";
+    Issue issue = new DefaultIssue()
+      .setKey(issueKey)
+      .setComponentKey("org.sonar.server.issue.IssueClient")
+      .setProjectKey("org.sonar.Sonar")
+      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+      .setLine(12)
+      .setEffortToFix(2.0)
+      .setMessage("Fix it")
+      .setResolution("FIXED")
+      .setStatus("CLOSED")
+      .setSeverity("MAJOR")
+      .setCreationDate(issue_creation_date);
+    issues.add(issue);
+
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(10L)
+      .setKey("org.sonar.server.issue.IssueClient")
+      .setLongName("SonarQube :: Issue Client")
+      .setQualifier("FIL")
+      .setSubProjectId(1L)
+      .setProjectId(1L)
+    ));
+
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(1L)
+      .setKey("org.sonar.Sonar")
+      .setLongName("SonarQube")
+      .setProjectId(1L)
+    ));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
+    request.execute().assertJson(getClass(), "show_issue.json");
+  }
+
+  @Test
+  public void show_issue_with_sub_project() throws Exception {
+    String issueKey = "ABCD";
+    Issue issue = new DefaultIssue()
+      .setKey(issueKey)
+      .setComponentKey("org.sonar.server.issue.IssueClient")
+      .setProjectKey("org.sonar.Sonar")
+      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+      .setLine(12)
+      .setEffortToFix(2.0)
+      .setMessage("Fix it")
+      .setResolution("FIXED")
+      .setStatus("CLOSED")
+      .setSeverity("MAJOR")
+      .setCreationDate(issue_creation_date);
+    issues.add(issue);
+
+    // File
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(10L)
+      .setKey("org.sonar.server.issue.IssueClient")
+      .setLongName("SonarQube :: Issue Client")
+      .setQualifier("FIL")
+      .setSubProjectId(2L)
+      .setProjectId(1L)));
+
+    // Module
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(2L)
+      .setKey("org.sonar.server.Server")
+      .setLongName("SonarQube :: Server")
+      .setQualifier("BRC")
+      .setSubProjectId(1L)
+      .setProjectId(1L)));
+
+    // Project
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(1L)
+      .setKey("org.sonar.Sonar")
+      .setLongName("SonarQube")));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
+    request.execute().assertJson(getClass(), "show_issue_with_sub_project.json");
+  }
+
+  @Test
+  public void use_project_and_sub_project_names_if_no_long_name() throws Exception {
+    String issueKey = "ABCD";
+    Issue issue = new DefaultIssue()
+      .setKey(issueKey)
+      .setComponentKey("org.sonar.server.issue.IssueClient")
+      .setProjectKey("org.sonar.Sonar")
+      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+      .setLine(12)
+      .setEffortToFix(2.0)
+      .setMessage("Fix it")
+      .setResolution("FIXED")
+      .setStatus("CLOSED")
+      .setSeverity("MAJOR")
+      .setCreationDate(issue_creation_date);
+    issues.add(issue);
+
+    // File
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(10L)
+      .setKey("org.sonar.server.issue.IssueClient")
+      .setLongName("SonarQube :: Issue Client")
+      .setQualifier("FIL")
+      .setSubProjectId(2L)
+      .setProjectId(1L)));
+
+    // Module
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(2L)
+      .setKey("org.sonar.server.Server")
+      .setName("SonarQube :: Server")
+      .setQualifier("BRC")
+      .setSubProjectId(1L)
+      .setProjectId(1L)));
+
+    // Project
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(1L)
+      .setKey("org.sonar.Sonar")
+      .setName("SonarQube")));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
+    request.execute().assertJson(getClass(), "show_issue_with_sub_project.json");
+  }
+
+  @Test
+  public void show_issue_on_removed_component() throws Exception {
+    String issueKey = "ABCD";
+    Issue issue = createIssue();
+    issues.add(issue);
+
+    Component project = mock(Component.class);
+    when(project.key()).thenReturn("org.sonar.Sonar");
+    when(project.longName()).thenReturn("SonarQube");
+    result.addProjects(newArrayList(project));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
+    request.execute().assertJson(getClass(), "show_issue_on_removed_component.json");
+  }
+
+  @Test
+  public void show_issue_on_removed_project_and_component() throws Exception {
+    String issueKey = "ABCD";
+    Issue issue = createIssue();
+    issues.add(issue);
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
+    request.execute().assertJson(getClass(), "show_issue_on_removed_project_and_component.json");
+  }
+
+  @Test
+  public void show_issue_with_action_plan() throws Exception {
+    Issue issue = createStandardIssue()
+      .setActionPlanKey("AP-ABCD");
+    issues.add(issue);
+
+    result.addActionPlans(newArrayList((ActionPlan) new DefaultActionPlan().setKey("AP-ABCD").setName("Version 4.2")));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_action_plan.json");
+  }
+
+  @Test
+  public void show_issue_with_users() throws Exception {
+    Issue issue = createStandardIssue()
+      .setAssignee("john")
+      .setReporter("steven")
+      .setAuthorLogin("Henry");
+    issues.add(issue);
+
+    result.addUsers(Lists.<User>newArrayList(
+      new DefaultUser().setLogin("john").setName("John"),
+      new DefaultUser().setLogin("steven").setName("Steven")
+    ));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_users.json");
+  }
+
+  @Test
+  public void show_issue_with_technical_debt() throws Exception {
+    Duration debt = (Duration.create(7260L));
+    Issue issue = createStandardIssue().setDebt(debt);
+    issues.add(issue);
+
+    when(durations.format(any(Locale.class), eq(debt), eq(Durations.DurationFormat.SHORT))).thenReturn("2 hours 1 minutes");
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_technical_debt.json");
+  }
+
+  @Test
+  public void show_issue_with_user_characteristics() throws Exception {
+    Issue issue = createStandardIssue().setDebt(Duration.create(7260L));
+    issues.add(issue);
+
+    result.rule(issue).setCharacteristicId(2);
+    when(debtModel.characteristicById(1)).thenReturn(new DefaultDebtCharacteristic().setId(1).setName("Maintainability"));
+    when(debtModel.characteristicById(2)).thenReturn(new DefaultDebtCharacteristic().setId(2).setName("Readability").setParentId(1));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_characteristics.json");
+  }
+
+  @Test
+  public void show_issue_with_default_characteristics() throws Exception {
+    Issue issue = createStandardIssue().setDebt(Duration.create(7260L));
+    issues.add(issue);
+
+    result.rule(issue).setDefaultCharacteristicId(2);
+    when(debtModel.characteristicById(1)).thenReturn(new DefaultDebtCharacteristic().setId(1).setName("Maintainability"));
+    when(debtModel.characteristicById(2)).thenReturn(new DefaultDebtCharacteristic().setId(2).setName("Readability").setParentId(1));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_characteristics.json");
+  }
+
+  @Test
+  public void use_user_characteristic_if_both_characteristic_ids_are_defined() throws Exception {
+    Issue issue = createStandardIssue().setDebt(Duration.create(7260L));
+    issues.add(issue);
+
+    result.rule(issue).setCharacteristicId(2);
+    when(debtModel.characteristicById(1)).thenReturn(new DefaultDebtCharacteristic().setId(1).setName("Maintainability"));
+    when(debtModel.characteristicById(2)).thenReturn(new DefaultDebtCharacteristic().setId(2).setName("Readability").setParentId(1));
+
+    result.rule(issue).setDefaultCharacteristicId(20);
+    when(debtModel.characteristicById(10)).thenReturn(new DefaultDebtCharacteristic().setId(10).setName("Default Maintainability"));
+    when(debtModel.characteristicById(20)).thenReturn(new DefaultDebtCharacteristic().setId(20).setName("Default Readability").setParentId(10));
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_characteristics.json");
+  }
+
+  @Test
+  public void show_issue_with_dates() throws Exception {
+    Date creationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
+    Date updateDate = DateUtils.parseDateTime("2014-01-23T19:10:03+0100");
+    Date closedDate = DateUtils.parseDateTime("2014-01-24T19:10:03+0100");
+
+    Issue issue = createStandardIssue()
+      .setCreationDate(creationDate)
+      .setUpdateDate(updateDate)
+      .setCloseDate(closedDate);
+    issues.add(issue);
+
+    when(i18n.formatDateTime(any(Locale.class), eq(creationDate))).thenReturn("Jan 22, 2014 10:03 AM");
+    when(i18n.formatDateTime(any(Locale.class), eq(updateDate))).thenReturn("Jan 23, 2014 10:03 AM");
+    when(i18n.ageFromNow(any(Locale.class), eq(updateDate))).thenReturn("9 days");
+    when(i18n.formatDateTime(any(Locale.class), eq(closedDate))).thenReturn("Jan 24, 2014 10:03 AM");
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_dates.json");
+  }
+
+  @Test
+  public void show_issue_with_comments() throws Exception {
+    Date date1 = DateUtils.parseDateTime("2014-02-22T19:10:03+0100");
+    Date date2 = DateUtils.parseDateTime("2014-02-23T19:10:03+0100");
+
+    Issue issue = createStandardIssue()
+      .addComment(
+        new DefaultIssueComment()
+          .setKey("COMMENT-ABCD")
+          .setMarkdownText("*My comment*")
+          .setUserLogin("john")
+          .setCreatedAt(date1))
+      .addComment(
+        new DefaultIssueComment()
+          .setKey("COMMENT-ABCE")
+          .setMarkdownText("Another comment")
+          .setUserLogin("arthur")
+          .setCreatedAt(date2)
+      );
+    issues.add(issue);
+    result.addUsers(Lists.<User>newArrayList(
+      new DefaultUser().setLogin("john").setName("John"),
+      new DefaultUser().setLogin("arthur").setName("Arthur")
+    ));
+
+    when(i18n.ageFromNow(any(Locale.class), eq(date1))).thenReturn("9 days");
+    when(i18n.ageFromNow(any(Locale.class), eq(date2))).thenReturn("10 days");
+
+    MockUserSession.set().setLogin("arthur");
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_comments.json");
+  }
+
+  @Test
+  public void show_issue_with_transitions() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("RESOLVED")
+      .setResolution("FIXED");
+    issues.add(issue);
+
+    when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN")));
+
+    MockUserSession.set().setLogin("john");
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_transitions.json");
+  }
+
+  @Test
+  public void show_issue_with_actions() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN");
+    issues.add(issue);
+
+    MockUserSession.set().setLogin("john");
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_actions.json");
+  }
+
+  @Test
+  public void show_issue_with_set_severity_action() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN");
+    issues.add(issue);
+
+    MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, issue.projectKey());
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_set_severity_action.json");
+  }
+
+  @Test
+  public void show_issue_with_assign_to_me_action() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN");
+    issues.add(issue);
+
+    MockUserSession.set().setLogin("john");
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_assign_to_me_action.json");
+  }
+
+  @Test
+  public void show_issue_without_assign_to_me_action() throws Exception {
+    DefaultIssue issue = createStandardIssue()
+      .setStatus("OPEN")
+      .setAssignee("john");
+    issues.add(issue);
+
+    result.addUsers(Lists.<User>newArrayList(
+      new DefaultUser().setLogin("john").setName("John")
+    ));
+
+    MockUserSession.set().setLogin("john");
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_without_assign_to_me_action.json");
+  }
+
+  @Test
+  public void show_issue_with_actions_defined_by_plugins() throws Exception {
+    Issue issue = createStandardIssue()
+      .setStatus("OPEN");
+    issues.add(issue);
+
+    Action action = mock(Action.class);
+    when(action.key()).thenReturn("link-to-jira");
+    when(actionService.listAvailableActions(issue)).thenReturn(newArrayList(action));
+
+    MockUserSession.set().setLogin("john");
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_actions_defined_by_plugins.json");
+  }
+
+  @Test
+  public void show_issue_with_changelog() throws Exception {
+    Issue issue = createStandardIssue();
+    issues.add(issue);
+
+    Date date1 = DateUtils.parseDateTime("2014-02-22T19:10:03+0100");
+    Date date2 = DateUtils.parseDateTime("2014-02-23T19:10:03+0100");
+
+    List<User> users = Lists.<User>newArrayList(new DefaultUser().setLogin("john").setName("John"));
+    FieldDiffs userChange = new FieldDiffs()
+      .setUserLogin("john")
+      .setDiff("actionPlan", null, "1.0")
+      .setCreationDate(date1);
+    FieldDiffs scanChange = new FieldDiffs()
+      .setDiff("severity", "INFO", "BLOCKER")
+      .setDiff("status", "REOPEN", "RESOLVED")
+      .setCreationDate(date2);
+    when(issueChangelogService.changelog(issue)).thenReturn(new IssueChangelog(newArrayList(userChange, scanChange), users));
+    when(issueChangelogService.formatDiffs(userChange)).thenReturn(newArrayList("Action plan updated to 1.0"));
+    when(issueChangelogService.formatDiffs(scanChange)).thenReturn(newArrayList("Severity updated from Info to Blocker", "Status updated from Reopen to Resolved"));
+
+    when(i18n.formatDateTime(any(Locale.class), eq(date1))).thenReturn("Fev 22, 2014 10:03 AM");
+    when(i18n.formatDateTime(any(Locale.class), eq(date2))).thenReturn("Fev 23, 2014 10:03 AM");
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
+    request.execute().assertJson(getClass(), "show_issue_with_changelog.json");
+  }
+
+  private DefaultIssue createStandardIssue() {
+    DefaultIssue issue = createIssue();
+    addComponentAndProject();
+    return issue;
+  }
+
+  private DefaultIssue createIssue() {
+    return new DefaultIssue()
+      .setKey("ABCD")
+      .setComponentKey("org.sonar.server.issue.IssueClient")
+      .setProjectKey("org.sonar.Sonar")
+      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+      .setCreationDate(issue_creation_date);
+  }
+
+  private void addComponentAndProject() {
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(10L)
+      .setKey("org.sonar.server.issue.IssueClient")
+      .setLongName("SonarQube :: Issue Client")
+      .setQualifier("FIL")
+      .setSubProjectId(1L)
+      .setProjectId(1L)
+    ));
+
+    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
+      .setId(1L)
+      .setKey("org.sonar.Sonar")
+      .setLongName("SonarQube")
+      .setProjectId(1L)
+    ));
+  }
+
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java
deleted file mode 100644 (file)
index 01ddbfb..0000000
+++ /dev/null
@@ -1,561 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.issue.ws;
-
-import com.google.common.collect.Lists;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.component.Component;
-import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.IssueFinder;
-import org.sonar.api.issue.IssueQuery;
-import org.sonar.api.issue.action.Action;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
-import org.sonar.server.ws.WsTester;
-import org.sonar.api.user.User;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.Duration;
-import org.sonar.api.utils.Durations;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.component.ComponentDto;
-import org.sonar.core.issue.DefaultActionPlan;
-import org.sonar.core.issue.DefaultIssueQueryResult;
-import org.sonar.core.issue.workflow.Transition;
-import org.sonar.core.user.DefaultUser;
-import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.IssueChangelog;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.user.MockUserSession;
-import org.sonar.server.user.UserSession;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class IssueShowWsHandlerTest {
-
-  @Mock
-  IssueFinder issueFinder;
-
-  @Mock
-  IssueService issueService;
-
-  @Mock
-  IssueChangelogService issueChangelogService;
-
-  @Mock
-  ActionService actionService;
-
-  @Mock
-  DebtModelService debtModel;
-
-  @Mock
-  I18n i18n;
-
-  @Mock
-  Durations durations;
-
-  List<Issue> issues;
-  DefaultIssueQueryResult result;
-
-  private Date issue_creation_date;
-
-  WsTester tester;
-
-  @Before
-  public void setUp() throws Exception {
-    issues = new ArrayList<Issue>();
-    result = new DefaultIssueQueryResult(issues);
-    result.addRules(newArrayList(Rule.create("squid", "AvoidCycle").setName("Avoid cycle")));
-    when(issueFinder.find(any(IssueQuery.class))).thenReturn(result);
-
-    when(issueChangelogService.changelog(any(Issue.class))).thenReturn(mock(IssueChangelog.class));
-
-    issue_creation_date = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
-    when(i18n.formatDateTime(any(Locale.class), eq(issue_creation_date))).thenReturn("Jan 22, 2014 10:03 AM");
-
-    when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created");
-
-    tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, debtModel, i18n, durations)));
-  }
-
-  @Test
-  public void show_issue() throws Exception {
-    String issueKey = "ABCD";
-    Issue issue = new DefaultIssue()
-      .setKey(issueKey)
-      .setComponentKey("org.sonar.server.issue.IssueClient")
-      .setProjectKey("org.sonar.Sonar")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
-      .setLine(12)
-      .setEffortToFix(2.0)
-      .setMessage("Fix it")
-      .setResolution("FIXED")
-      .setStatus("CLOSED")
-      .setSeverity("MAJOR")
-      .setCreationDate(issue_creation_date);
-    issues.add(issue);
-
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(10L)
-      .setKey("org.sonar.server.issue.IssueClient")
-      .setLongName("SonarQube :: Issue Client")
-      .setQualifier("FIL")
-      .setSubProjectId(1L)
-      .setProjectId(1L)
-    ));
-
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(1L)
-      .setKey("org.sonar.Sonar")
-      .setLongName("SonarQube")
-      .setProjectId(1L)
-    ));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
-    request.execute().assertJson(getClass(), "show_issue.json");
-  }
-
-  @Test
-  public void show_issue_with_sub_project() throws Exception {
-    String issueKey = "ABCD";
-    Issue issue = new DefaultIssue()
-      .setKey(issueKey)
-      .setComponentKey("org.sonar.server.issue.IssueClient")
-      .setProjectKey("org.sonar.Sonar")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
-      .setLine(12)
-      .setEffortToFix(2.0)
-      .setMessage("Fix it")
-      .setResolution("FIXED")
-      .setStatus("CLOSED")
-      .setSeverity("MAJOR")
-      .setCreationDate(issue_creation_date);
-    issues.add(issue);
-
-    // File
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(10L)
-      .setKey("org.sonar.server.issue.IssueClient")
-      .setLongName("SonarQube :: Issue Client")
-      .setQualifier("FIL")
-      .setSubProjectId(2L)
-      .setProjectId(1L)));
-
-    // Module
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(2L)
-      .setKey("org.sonar.server.Server")
-      .setLongName("SonarQube :: Server")
-      .setQualifier("BRC")
-      .setSubProjectId(1L)
-      .setProjectId(1L)));
-
-    // Project
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(1L)
-      .setKey("org.sonar.Sonar")
-      .setLongName("SonarQube")));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
-    request.execute().assertJson(getClass(), "show_issue_with_sub_project.json");
-  }
-
-  @Test
-  public void use_project_and_sub_project_names_if_no_long_name() throws Exception {
-    String issueKey = "ABCD";
-    Issue issue = new DefaultIssue()
-      .setKey(issueKey)
-      .setComponentKey("org.sonar.server.issue.IssueClient")
-      .setProjectKey("org.sonar.Sonar")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
-      .setLine(12)
-      .setEffortToFix(2.0)
-      .setMessage("Fix it")
-      .setResolution("FIXED")
-      .setStatus("CLOSED")
-      .setSeverity("MAJOR")
-      .setCreationDate(issue_creation_date);
-    issues.add(issue);
-
-    // File
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(10L)
-      .setKey("org.sonar.server.issue.IssueClient")
-      .setLongName("SonarQube :: Issue Client")
-      .setQualifier("FIL")
-      .setSubProjectId(2L)
-      .setProjectId(1L)));
-
-    // Module
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(2L)
-      .setKey("org.sonar.server.Server")
-      .setName("SonarQube :: Server")
-      .setQualifier("BRC")
-      .setSubProjectId(1L)
-      .setProjectId(1L)));
-
-    // Project
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(1L)
-      .setKey("org.sonar.Sonar")
-      .setName("SonarQube")));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
-    request.execute().assertJson(getClass(), "show_issue_with_sub_project.json");
-  }
-
-  @Test
-  public void show_issue_on_removed_component() throws Exception {
-    String issueKey = "ABCD";
-    Issue issue = createIssue();
-    issues.add(issue);
-
-    Component project = mock(Component.class);
-    when(project.key()).thenReturn("org.sonar.Sonar");
-    when(project.longName()).thenReturn("SonarQube");
-    result.addProjects(newArrayList(project));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
-    request.execute().assertJson(getClass(), "show_issue_on_removed_component.json");
-  }
-
-  @Test
-  public void show_issue_on_removed_project_and_component() throws Exception {
-    String issueKey = "ABCD";
-    Issue issue = createIssue();
-    issues.add(issue);
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issueKey);
-    request.execute().assertJson(getClass(), "show_issue_on_removed_project_and_component.json");
-  }
-
-  @Test
-  public void show_issue_with_action_plan() throws Exception {
-    Issue issue = createStandardIssue()
-      .setActionPlanKey("AP-ABCD");
-    issues.add(issue);
-
-    result.addActionPlans(newArrayList((ActionPlan) new DefaultActionPlan().setKey("AP-ABCD").setName("Version 4.2")));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_action_plan.json");
-  }
-
-  @Test
-  public void show_issue_with_users() throws Exception {
-    Issue issue = createStandardIssue()
-      .setAssignee("john")
-      .setReporter("steven")
-      .setAuthorLogin("Henry");
-    issues.add(issue);
-
-    result.addUsers(Lists.<User>newArrayList(
-      new DefaultUser().setLogin("john").setName("John"),
-      new DefaultUser().setLogin("steven").setName("Steven")
-    ));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_users.json");
-  }
-
-  @Test
-  public void show_issue_with_technical_debt() throws Exception {
-    Duration debt = (Duration.create(7260L));
-    Issue issue = createStandardIssue().setDebt(debt);
-    issues.add(issue);
-
-    when(durations.format(any(Locale.class), eq(debt), eq(Durations.DurationFormat.SHORT))).thenReturn("2 hours 1 minutes");
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_technical_debt.json");
-  }
-
-  @Test
-  public void show_issue_with_user_characteristics() throws Exception {
-    Issue issue = createStandardIssue().setDebt(Duration.create(7260L));
-    issues.add(issue);
-
-    result.rule(issue).setCharacteristicId(2);
-    when(debtModel.characteristicById(1)).thenReturn(new DefaultDebtCharacteristic().setId(1).setName("Maintainability"));
-    when(debtModel.characteristicById(2)).thenReturn(new DefaultDebtCharacteristic().setId(2).setName("Readability").setParentId(1));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_characteristics.json");
-  }
-
-  @Test
-  public void show_issue_with_default_characteristics() throws Exception {
-    Issue issue = createStandardIssue().setDebt(Duration.create(7260L));
-    issues.add(issue);
-
-    result.rule(issue).setDefaultCharacteristicId(2);
-    when(debtModel.characteristicById(1)).thenReturn(new DefaultDebtCharacteristic().setId(1).setName("Maintainability"));
-    when(debtModel.characteristicById(2)).thenReturn(new DefaultDebtCharacteristic().setId(2).setName("Readability").setParentId(1));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_characteristics.json");
-  }
-
-  @Test
-  public void use_user_characteristic_if_both_characteristic_ids_are_defined() throws Exception {
-    Issue issue = createStandardIssue().setDebt(Duration.create(7260L));
-    issues.add(issue);
-
-    result.rule(issue).setCharacteristicId(2);
-    when(debtModel.characteristicById(1)).thenReturn(new DefaultDebtCharacteristic().setId(1).setName("Maintainability"));
-    when(debtModel.characteristicById(2)).thenReturn(new DefaultDebtCharacteristic().setId(2).setName("Readability").setParentId(1));
-
-    result.rule(issue).setDefaultCharacteristicId(20);
-    when(debtModel.characteristicById(10)).thenReturn(new DefaultDebtCharacteristic().setId(10).setName("Default Maintainability"));
-    when(debtModel.characteristicById(20)).thenReturn(new DefaultDebtCharacteristic().setId(20).setName("Default Readability").setParentId(10));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_characteristics.json");
-  }
-
-  @Test
-  public void show_issue_with_dates() throws Exception {
-    Date creationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100");
-    Date updateDate = DateUtils.parseDateTime("2014-01-23T19:10:03+0100");
-    Date closedDate = DateUtils.parseDateTime("2014-01-24T19:10:03+0100");
-
-    Issue issue = createStandardIssue()
-      .setCreationDate(creationDate)
-      .setUpdateDate(updateDate)
-      .setCloseDate(closedDate);
-    issues.add(issue);
-
-    when(i18n.formatDateTime(any(Locale.class), eq(creationDate))).thenReturn("Jan 22, 2014 10:03 AM");
-    when(i18n.formatDateTime(any(Locale.class), eq(updateDate))).thenReturn("Jan 23, 2014 10:03 AM");
-    when(i18n.ageFromNow(any(Locale.class), eq(updateDate))).thenReturn("9 days");
-    when(i18n.formatDateTime(any(Locale.class), eq(closedDate))).thenReturn("Jan 24, 2014 10:03 AM");
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_dates.json");
-  }
-
-  @Test
-  public void show_issue_with_comments() throws Exception {
-    Date date1 = DateUtils.parseDateTime("2014-02-22T19:10:03+0100");
-    Date date2 = DateUtils.parseDateTime("2014-02-23T19:10:03+0100");
-
-    Issue issue = createStandardIssue()
-      .addComment(
-        new DefaultIssueComment()
-          .setKey("COMMENT-ABCD")
-          .setMarkdownText("*My comment*")
-          .setUserLogin("john")
-          .setCreatedAt(date1))
-      .addComment(
-        new DefaultIssueComment()
-          .setKey("COMMENT-ABCE")
-          .setMarkdownText("Another comment")
-          .setUserLogin("arthur")
-          .setCreatedAt(date2)
-      );
-    issues.add(issue);
-    result.addUsers(Lists.<User>newArrayList(
-      new DefaultUser().setLogin("john").setName("John"),
-      new DefaultUser().setLogin("arthur").setName("Arthur")
-    ));
-
-    when(i18n.ageFromNow(any(Locale.class), eq(date1))).thenReturn("9 days");
-    when(i18n.ageFromNow(any(Locale.class), eq(date2))).thenReturn("10 days");
-
-    MockUserSession.set().setLogin("arthur");
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_comments.json");
-  }
-
-  @Test
-  public void show_issue_with_transitions() throws Exception {
-    DefaultIssue issue = createStandardIssue()
-      .setStatus("RESOLVED")
-      .setResolution("FIXED");
-    issues.add(issue);
-
-    when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN")));
-
-    MockUserSession.set().setLogin("john");
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_transitions.json");
-  }
-
-  @Test
-  public void show_issue_with_actions() throws Exception {
-    DefaultIssue issue = createStandardIssue()
-      .setStatus("OPEN");
-    issues.add(issue);
-
-    MockUserSession.set().setLogin("john");
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_actions.json");
-  }
-
-  @Test
-  public void show_issue_with_set_severity_action() throws Exception {
-    DefaultIssue issue = createStandardIssue()
-      .setStatus("OPEN");
-    issues.add(issue);
-
-    MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, issue.projectKey());
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_set_severity_action.json");
-  }
-
-  @Test
-  public void show_issue_with_assign_to_me_action() throws Exception {
-    DefaultIssue issue = createStandardIssue()
-      .setStatus("OPEN");
-    issues.add(issue);
-
-    MockUserSession.set().setLogin("john");
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_assign_to_me_action.json");
-  }
-
-  @Test
-  public void show_issue_without_assign_to_me_action() throws Exception {
-    DefaultIssue issue = createStandardIssue()
-      .setStatus("OPEN")
-      .setAssignee("john");
-    issues.add(issue);
-
-    result.addUsers(Lists.<User>newArrayList(
-      new DefaultUser().setLogin("john").setName("John")
-    ));
-
-    MockUserSession.set().setLogin("john");
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_without_assign_to_me_action.json");
-  }
-
-  @Test
-  public void show_issue_with_actions_defined_by_plugins() throws Exception {
-    Issue issue = createStandardIssue()
-      .setStatus("OPEN");
-    issues.add(issue);
-
-    Action action = mock(Action.class);
-    when(action.key()).thenReturn("link-to-jira");
-    when(actionService.listAvailableActions(issue)).thenReturn(newArrayList(action));
-
-    MockUserSession.set().setLogin("john");
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_actions_defined_by_plugins.json");
-  }
-
-  @Test
-  public void show_issue_with_changelog() throws Exception {
-    Issue issue = createStandardIssue();
-    issues.add(issue);
-
-    Date date1 = DateUtils.parseDateTime("2014-02-22T19:10:03+0100");
-    Date date2 = DateUtils.parseDateTime("2014-02-23T19:10:03+0100");
-
-    List<User> users = Lists.<User>newArrayList(new DefaultUser().setLogin("john").setName("John"));
-    FieldDiffs userChange = new FieldDiffs()
-      .setUserLogin("john")
-      .setDiff("actionPlan", null, "1.0")
-      .setCreationDate(date1);
-    FieldDiffs scanChange = new FieldDiffs()
-      .setDiff("severity", "INFO", "BLOCKER")
-      .setDiff("status", "REOPEN", "RESOLVED")
-      .setCreationDate(date2);
-    when(issueChangelogService.changelog(issue)).thenReturn(new IssueChangelog(newArrayList(userChange, scanChange), users));
-    when(issueChangelogService.formatDiffs(userChange)).thenReturn(newArrayList("Action plan updated to 1.0"));
-    when(issueChangelogService.formatDiffs(scanChange)).thenReturn(newArrayList("Severity updated from Info to Blocker", "Status updated from Reopen to Resolved"));
-
-    when(i18n.formatDateTime(any(Locale.class), eq(date1))).thenReturn("Fev 22, 2014 10:03 AM");
-    when(i18n.formatDateTime(any(Locale.class), eq(date2))).thenReturn("Fev 23, 2014 10:03 AM");
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
-    request.execute().assertJson(getClass(), "show_issue_with_changelog.json");
-  }
-
-  private DefaultIssue createStandardIssue() {
-    DefaultIssue issue = createIssue();
-    addComponentAndProject();
-    return issue;
-  }
-
-  private DefaultIssue createIssue() {
-    return new DefaultIssue()
-      .setKey("ABCD")
-      .setComponentKey("org.sonar.server.issue.IssueClient")
-      .setProjectKey("org.sonar.Sonar")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
-      .setCreationDate(issue_creation_date);
-  }
-
-  private void addComponentAndProject() {
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(10L)
-      .setKey("org.sonar.server.issue.IssueClient")
-      .setLongName("SonarQube :: Issue Client")
-      .setQualifier("FIL")
-      .setSubProjectId(1L)
-      .setProjectId(1L)
-    ));
-
-    result.addComponents(Lists.<Component>newArrayList(new ComponentDto()
-      .setId(1L)
-      .setKey("org.sonar.Sonar")
-      .setLongName("SonarQube")
-      .setProjectId(1L)
-    ));
-  }
-
-}
index 9fd33aa4b2deebc30cbbd1cead91019e90d49dc0..fe1401d646a199f4bf529acd154d9de3ba91ece4 100644 (file)
 package org.sonar.server.issue.ws;
 
 import org.junit.Test;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.issue.IssueFinder;
+import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.Durations;
+import org.sonar.server.debt.DebtModelService;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.IssueChangelogService;
+import org.sonar.server.issue.IssueService;
 import org.sonar.server.ws.WsTester;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
-
 public class IssuesWsTest {
 
-  IssueShowWsHandler showHandler = mock(IssueShowWsHandler.class);
-  WsTester tester = new WsTester(new IssuesWs(showHandler));
+  IssueShowAction showAction = new IssueShowAction(mock(IssueFinder.class), mock(IssueService.class), mock(IssueChangelogService.class), mock(ActionService.class),
+    mock(DebtModelService.class), mock(I18n.class), mock(Durations.class));
+  WsTester tester = new WsTester(new IssuesWs(showAction));
 
   @Test
-  public void define_ws() throws Exception {
+  public void define_controller() throws Exception {
     WebService.Controller controller = tester.controller("api/issues");
     assertThat(controller).isNotNull();
     assertThat(controller.description()).isNotEmpty();
+    assertThat(controller.since()).isEqualTo("3.6");
+    assertThat(controller.actions()).hasSize(2);
+  }
+
+  @Test
+  public void define_show_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/issues");
 
     WebService.Action show = controller.action("show");
     assertThat(show).isNotNull();
@@ -44,7 +59,26 @@ public class IssuesWsTest {
     assertThat(show.since()).isEqualTo("4.2");
     assertThat(show.isPost()).isFalse();
     assertThat(show.isInternal()).isTrue();
-    assertThat(show.handler()).isSameAs(showHandler);
+    assertThat(show.handler()).isSameAs(showAction);
+
+    WebService.Param key = show.param("key");
+    assertThat(key).isNotNull();
+    assertThat(key.description()).isNotNull();
+    assertThat(key.isRequired()).isFalse();
+  }
+
+  @Test
+  public void define_search_action() throws Exception {
+    WebService.Controller controller = tester.controller("api/issues");
+
+    WebService.Action show = controller.action("search");
+    assertThat(show).isNotNull();
+    assertThat(show.handler()).isNotNull();
+    assertThat(show.since()).isEqualTo("3.6");
+    assertThat(show.isPost()).isFalse();
+    assertThat(show.isInternal()).isFalse();
+    assertThat(show.handler()).isInstanceOf(RailsHandler.class);
+    assertThat(show.params()).hasSize(2);
   }
 
 }
index 6ddbd9f484545f965c0b89b289f16622eb2f0827..1a480af5e23625b7490d560976b802c016166808 100644 (file)
@@ -42,9 +42,12 @@ public class SourcesWsTest {
 
     WebService.Action show = controller.action("show");
     assertThat(show).isNotNull();
-    assertThat(show.handler()).isNotNull();
-    assertThat(show.since()).isEqualTo("4.2");
-    assertThat(show.isPost()).isFalse();
     assertThat(show.handler()).isSameAs(showAction);
+    assertThat(show.params()).hasSize(5);
+
+    WebService.Action scm = controller.action("scm");
+    assertThat(scm).isNotNull();
+    assertThat(scm.handler()).isSameAs(scmAction);
+    assertThat(scm.params()).hasSize(4);
   }
 }
index 0daf8447175cebdaa7db8cdbff7d04084109195b..5378eb8a8ef0630c2861baaec7294d5f57fbd488 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.ws;
 
+import com.google.common.io.Resources;
 import org.junit.Test;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.RequestHandler;
@@ -89,7 +90,7 @@ public class ListingWsTest {
         .setSince("4.1")
         .setPost(true)
         .setInternal(true)
-        .setResponseExample(getClass().getResource("/org/sonar/server/ws/ListingWsTest/metrics_example.json"))
+        .setResponseExample(Resources.getResource(getClass(), "ListingWsTest/metrics_example.json"))
         .setHandler(new RequestHandler() {
           @Override
           public void handle(Request request, Response response) {
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/anonymous_page.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/anonymous_page.json
new file mode 100644 (file)
index 0000000..6a97863
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "canManageFilters": false,
+    "canBulkChange": false
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page.json
new file mode 100644 (file)
index 0000000..3a9d984
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "canManageFilters": true,
+    "canBulkChange": true,
+    "favorites": []
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page_with_favorites.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page_with_favorites.json
new file mode 100644 (file)
index 0000000..62f43b2
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "canManageFilters": true,
+    "canBulkChange": true,
+    "favorites": [
+        {
+            "id": 6,
+            "name": "My issues"
+        },
+        {
+            "id": 13,
+            "name": "Blocker issues"
+        }
+    ]
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page_with_selected_filter.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/logged_in_page_with_selected_filter.json
new file mode 100644 (file)
index 0000000..e56c2b5
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "canManageFilters": true,
+  "canBulkChange": true,
+  "filter": {
+    "id": 13,
+    "name": "Blocker issues",
+    "shared": false,
+    "query": "severity=BLOCKER",
+    "user": "eric",
+    "canModify": true
+  },
+  "favorites": [
+  ]
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/selected_filter_can_not_be_modified.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/AppActionTest/selected_filter_can_not_be_modified.json
new file mode 100644 (file)
index 0000000..8b995de
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "canManageFilters": true,
+  "canBulkChange": true,
+  "filter": {
+    "id": 13,
+    "name": "Blocker issues",
+    "shared": true,
+    "query": "severity=BLOCKER",
+    "user": "simon",
+    "canModify": false
+  },
+  "favorites": [
+  ]
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/anonymous_page.json
deleted file mode 100644 (file)
index 6a97863..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-    "canManageFilters": false,
-    "canBulkChange": false
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page.json
deleted file mode 100644 (file)
index 3a9d984..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-    "canManageFilters": true,
-    "canBulkChange": true,
-    "favorites": []
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_favorites.json
deleted file mode 100644 (file)
index 62f43b2..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "canManageFilters": true,
-    "canBulkChange": true,
-    "favorites": [
-        {
-            "id": 6,
-            "name": "My issues"
-        },
-        {
-            "id": 13,
-            "name": "Blocker issues"
-        }
-    ]
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/logged_in_page_with_selected_filter.json
deleted file mode 100644 (file)
index e56c2b5..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "canManageFilters": true,
-  "canBulkChange": true,
-  "filter": {
-    "id": 13,
-    "name": "Blocker issues",
-    "shared": false,
-    "query": "severity=BLOCKER",
-    "user": "eric",
-    "canModify": true
-  },
-  "favorites": [
-  ]
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/selected_filter_can_not_be_modified.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/selected_filter_can_not_be_modified.json
deleted file mode 100644 (file)
index 8b995de..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "canManageFilters": true,
-  "canBulkChange": true,
-  "filter": {
-    "id": 13,
-    "name": "Blocker issues",
-    "shared": true,
-    "query": "severity=BLOCKER",
-    "user": "simon",
-    "canModify": false
-  },
-  "favorites": [
-  ]
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/show_filter.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/IssueFilterWsTest/show_filter.json
deleted file mode 100644 (file)
index 447e436..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "filter": {
-    "id": 13,
-    "name": "Blocker issues",
-    "description": "All Blocker Issues",
-    "shared": true,
-    "query": "severity=BLOCKER",
-    "user": "simon",
-    "canModify": false
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/filter/ShowActionTest/show_filter.json b/sonar-server/src/test/resources/org/sonar/server/issue/filter/ShowActionTest/show_filter.json
new file mode 100644 (file)
index 0000000..447e436
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "filter": {
+    "id": 13,
+    "name": "Blocker issues",
+    "description": "All Blocker Issues",
+    "shared": true,
+    "query": "severity=BLOCKER",
+    "user": "simon",
+    "canModify": false
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue.json
new file mode 100644 (file)
index 0000000..6a0eb80
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "line": 12,
+    "message": "Fix it",
+    "resolution": "FIXED",
+    "status": "CLOSED",
+    "severity": "MAJOR",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_on_removed_component.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_on_removed_component.json
new file mode 100644 (file)
index 0000000..74542d5
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "project": "org.sonar.Sonar",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_on_removed_project_and_component.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_on_removed_project_and_component.json
new file mode 100644 (file)
index 0000000..74542d5
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "project": "org.sonar.Sonar",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_action_plan.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_action_plan.json
new file mode 100644 (file)
index 0000000..71dff77
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "actionPlan" : "AP-ABCD",
+    "actionPlanName" : "Version 4.2",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions.json
new file mode 100644 (file)
index 0000000..eb7f97f
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "assign_to_me", "plan"
+    ],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json
new file mode 100644 (file)
index 0000000..9f83417
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "assign_to_me", "plan", "link-to-jira"
+    ],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json
new file mode 100644 (file)
index 0000000..eb7f97f
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "assign_to_me", "plan"
+    ],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_changelog.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_changelog.json
new file mode 100644 (file)
index 0000000..ceef684
--- /dev/null
@@ -0,0 +1,35 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      },
+      {
+        "userName": "John",
+        "creationDate": "2014-02-22T19:10:03+0100",
+        "fCreationDate": "Fev 22, 2014 10:03 AM",
+        "diffs": ["Action plan updated to 1.0"]
+      },
+      {
+        "creationDate": "2014-02-23T19:10:03+0100",
+        "fCreationDate": "Fev 23, 2014 10:03 AM",
+        "diffs": ["Severity updated from Info to Blocker", "Status updated from Reopen to Resolved"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_characteristics.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_characteristics.json
new file mode 100644 (file)
index 0000000..833d800
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "characteristic": "Maintainability",
+    "subCharacteristic": "Readability",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_comments.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_comments.json
new file mode 100644 (file)
index 0000000..8eba44a
--- /dev/null
@@ -0,0 +1,43 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": ["comment", "assign", "assign_to_me", "plan"],
+    "comments": [
+      {
+        "key": "COMMENT-ABCD",
+        "userName": "John",
+        "raw": "*My comment*",
+        "html": "<em>My comment</em>",
+        "createdAt": "2014-02-22T19:10:03+0100",
+        "fCreatedAge": "9 days",
+        "updatable": false
+      },
+      {
+        "key": "COMMENT-ABCE",
+        "userName": "Arthur",
+        "raw": "Another comment",
+        "html": "Another comment",
+        "createdAt": "2014-02-23T19:10:03+0100",
+        "fCreatedAge": "10 days",
+        "updatable": true
+      }
+    ],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_dates.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_dates.json
new file mode 100644 (file)
index 0000000..de7b32b
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "updateDate": "2014-01-23T19:10:03+0100",
+    "fUpdateDate": "Jan 23, 2014 10:03 AM",
+    "fUpdateAge": "9 days",
+    "closeDate": "2014-01-24T19:10:03+0100",
+    "fCloseDate": "Jan 24, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json
new file mode 100644 (file)
index 0000000..242d99c
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "assign_to_me", "plan", "set_severity"
+    ],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_sub_project.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_sub_project.json
new file mode 100644 (file)
index 0000000..aa7b9e3
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "subProjectName": "SonarQube :: Server",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "line": 12,
+    "message": "Fix it",
+    "resolution": "FIXED",
+    "status": "CLOSED",
+    "severity": "MAJOR",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_technical_debt.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_technical_debt.json
new file mode 100644 (file)
index 0000000..d7cbcc8
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "debt": "2 hours 1 minutes",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_transitions.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_transitions.json
new file mode 100644 (file)
index 0000000..ed14d16
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "status": "RESOLVED",
+    "resolution": "FIXED",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": ["reopen"],
+    "actions": ["comment"],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_users.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_users.json
new file mode 100644 (file)
index 0000000..5f69ff2
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "assignee": "john",
+    "assigneeName": "John",
+    "reporter": "steven",
+    "reporterName": "Steven",
+    "author": "Henry",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json
new file mode 100644 (file)
index 0000000..ffde399
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "issue": {
+    "key": "ABCD",
+    "component": "org.sonar.server.issue.IssueClient",
+    "componentLongName": "SonarQube :: Issue Client",
+    "componentQualifier": "FIL",
+    "project": "org.sonar.Sonar",
+    "projectName": "SonarQube",
+    "rule": "squid:AvoidCycle",
+    "ruleName": "Avoid cycle",
+    "assignee": "john",
+    "assigneeName": "John",
+    "status": "OPEN",
+    "creationDate": "2014-01-22T19:10:03+0100",
+    "fCreationDate": "Jan 22, 2014 10:03 AM",
+    "transitions": [],
+    "actions": [
+       "comment", "assign", "plan"
+    ],
+    "comments": [],
+    "changelog": [
+      {
+        "creationDate": "2014-01-22T19:10:03+0100",
+        "fCreationDate": "Jan 22, 2014 10:03 AM",
+        "diffs": ["Created"]
+      }
+    ]
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue.json
deleted file mode 100644 (file)
index 6a0eb80..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "line": 12,
-    "message": "Fix it",
-    "resolution": "FIXED",
-    "status": "CLOSED",
-    "severity": "MAJOR",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_on_removed_component.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_on_removed_component.json
deleted file mode 100644 (file)
index 74542d5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "project": "org.sonar.Sonar",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_on_removed_project_and_component.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_on_removed_project_and_component.json
deleted file mode 100644 (file)
index 74542d5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "project": "org.sonar.Sonar",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_action_plan.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_action_plan.json
deleted file mode 100644 (file)
index 71dff77..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "actionPlan" : "AP-ABCD",
-    "actionPlanName" : "Version 4.2",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions.json
deleted file mode 100644 (file)
index eb7f97f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "status": "OPEN",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [
-       "comment", "assign", "assign_to_me", "plan"
-    ],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_actions_defined_by_plugins.json
deleted file mode 100644 (file)
index 9f83417..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "status": "OPEN",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [
-       "comment", "assign", "assign_to_me", "plan", "link-to-jira"
-    ],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_assign_to_me_action.json
deleted file mode 100644 (file)
index eb7f97f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "status": "OPEN",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [
-       "comment", "assign", "assign_to_me", "plan"
-    ],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_changelog.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_changelog.json
deleted file mode 100644 (file)
index ceef684..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      },
-      {
-        "userName": "John",
-        "creationDate": "2014-02-22T19:10:03+0100",
-        "fCreationDate": "Fev 22, 2014 10:03 AM",
-        "diffs": ["Action plan updated to 1.0"]
-      },
-      {
-        "creationDate": "2014-02-23T19:10:03+0100",
-        "fCreationDate": "Fev 23, 2014 10:03 AM",
-        "diffs": ["Severity updated from Info to Blocker", "Status updated from Reopen to Resolved"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_characteristics.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_characteristics.json
deleted file mode 100644 (file)
index 833d800..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "characteristic": "Maintainability",
-    "subCharacteristic": "Readability",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_comments.json
deleted file mode 100644 (file)
index 8eba44a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": ["comment", "assign", "assign_to_me", "plan"],
-    "comments": [
-      {
-        "key": "COMMENT-ABCD",
-        "userName": "John",
-        "raw": "*My comment*",
-        "html": "<em>My comment</em>",
-        "createdAt": "2014-02-22T19:10:03+0100",
-        "fCreatedAge": "9 days",
-        "updatable": false
-      },
-      {
-        "key": "COMMENT-ABCE",
-        "userName": "Arthur",
-        "raw": "Another comment",
-        "html": "Another comment",
-        "createdAt": "2014-02-23T19:10:03+0100",
-        "fCreatedAge": "10 days",
-        "updatable": true
-      }
-    ],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_dates.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_dates.json
deleted file mode 100644 (file)
index de7b32b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "updateDate": "2014-01-23T19:10:03+0100",
-    "fUpdateDate": "Jan 23, 2014 10:03 AM",
-    "fUpdateAge": "9 days",
-    "closeDate": "2014-01-24T19:10:03+0100",
-    "fCloseDate": "Jan 24, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_set_severity_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_set_severity_action.json
deleted file mode 100644 (file)
index 242d99c..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "status": "OPEN",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [
-       "comment", "assign", "assign_to_me", "plan", "set_severity"
-    ],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_sub_project.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_sub_project.json
deleted file mode 100644 (file)
index aa7b9e3..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "subProjectName": "SonarQube :: Server",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "line": 12,
-    "message": "Fix it",
-    "resolution": "FIXED",
-    "status": "CLOSED",
-    "severity": "MAJOR",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_technical_debt.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_technical_debt.json
deleted file mode 100644 (file)
index d7cbcc8..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "debt": "2 hours 1 minutes",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_transitions.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_transitions.json
deleted file mode 100644 (file)
index ed14d16..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "status": "RESOLVED",
-    "resolution": "FIXED",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": ["reopen"],
-    "actions": ["comment"],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_users.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_with_users.json
deleted file mode 100644 (file)
index 5f69ff2..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "assignee": "john",
-    "assigneeName": "John",
-    "reporter": "steven",
-    "reporterName": "Steven",
-    "author": "Henry",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowWsHandlerTest/show_issue_without_assign_to_me_action.json
deleted file mode 100644 (file)
index ffde399..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "issue": {
-    "key": "ABCD",
-    "component": "org.sonar.server.issue.IssueClient",
-    "componentLongName": "SonarQube :: Issue Client",
-    "componentQualifier": "FIL",
-    "project": "org.sonar.Sonar",
-    "projectName": "SonarQube",
-    "rule": "squid:AvoidCycle",
-    "ruleName": "Avoid cycle",
-    "assignee": "john",
-    "assigneeName": "John",
-    "status": "OPEN",
-    "creationDate": "2014-01-22T19:10:03+0100",
-    "fCreationDate": "Jan 22, 2014 10:03 AM",
-    "transitions": [],
-    "actions": [
-       "comment", "assign", "plan"
-    ],
-    "comments": [],
-    "changelog": [
-      {
-        "creationDate": "2014-01-22T19:10:03+0100",
-        "fCreationDate": "Jan 22, 2014 10:03 AM",
-        "diffs": ["Created"]
-      }
-    ]
-  }
-}