]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5531 Return comments and take into account extra fields param
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 25 Sep 2014 13:07:29 +0000 (15:07 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 25 Sep 2014 13:07:41 +0000 (15:07 +0200)
16 files changed:
server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_extra_fields.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/single_result.json [deleted file]
sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/empty.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml [new file with mode: 0644]

index 41364c818af5f9ee4243cbb231033671d161959d..0c72c7c8befdcc9685bd7507055cf7b0c2e40054 100644 (file)
@@ -23,7 +23,6 @@ import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import org.apache.ibatis.session.SqlSession;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.component.Component;
@@ -39,10 +38,11 @@ import org.sonar.core.issue.DefaultIssueQueryResult;
 import org.sonar.core.issue.db.IssueChangeDao;
 import org.sonar.core.issue.db.IssueDao;
 import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.core.resource.ResourceDao;
-import org.sonar.server.rule.DefaultRuleFinder;
 import org.sonar.server.issue.actionplan.ActionPlanService;
+import org.sonar.server.rule.DefaultRuleFinder;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
@@ -99,7 +99,7 @@ public class DefaultIssueFinder implements IssueFinder {
   public IssueQueryResult find(IssueQuery query) {
     LOG.debug("IssueQuery : {}", query);
     long start = System.currentTimeMillis();
-    SqlSession sqlSession = myBatis.openSession(false);
+    DbSession sqlSession = myBatis.openSession(false);
     try {
       // 1. Select the authorized ids of all the issues that match the query
       List<IssueDto> authorizedIssues = issueDao.selectIssueIds(query, UserSession.get().userId(), sqlSession);
index fd24180b2de2ed1a4ec62f348af76b69066b3a8d..9eb6f17acb3c80a16e6cd956dc7e64ee49345442 100644 (file)
@@ -137,8 +137,7 @@ public class DefaultIssueService implements IssueService {
     List<Transition> outTransitions = workflow.outTransitions(issue);
     List<Transition> allowedTransitions = new ArrayList<Transition>();
     for (Transition transition : outTransitions) {
-      DefaultIssue defaultIssue = (DefaultIssue) issue;
-      String projectKey = defaultIssue.projectKey();
+      String projectKey = issue.projectKey();
       if (StringUtils.isBlank(transition.requiredProjectPermission()) ||
         (projectKey != null && UserSession.get().hasProjectPermission(transition.requiredProjectPermission(), projectKey))) {
         allowedTransitions.add(transition);
index f8c6cb2048abe3496ed520bf3ac3af37b1b17d87..42f9a0b79a8e3e12702811404c3536acb23b460c 100644 (file)
@@ -118,9 +118,6 @@ public class IssueNormalizer extends BaseNormalizer<IssueDto, String> {
     update.put(IssueField.RULE_KEY.field(), dto.getRuleKey().toString());
 
     // TODO Not yet normalized
-    // IssueDoc issueDoc = new IssueDoc(null);
-    // issueDoc.isNew();
-    // issueDoc.comments();
     // issueDoc.attributes();
 
     /** Upsert elements */
index 4057524f0f86698727ca6516e308eb1d0f611edc..23a2c1093d873389ac9523d6b5c3d116106039b2 100644 (file)
@@ -23,7 +23,6 @@ package org.sonar.server.issue.ws;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.action.Action;
-import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.workflow.Transition;
@@ -48,8 +47,7 @@ public class IssueActionsWriter implements ServerComponent {
   public void writeTransitions(Issue issue, JsonWriter json) {
     json.name("transitions").beginArray();
     if (UserSession.get().isLoggedIn()) {
-      List<Transition> transitions = issueService.listTransitions(issue);
-      for (Transition transition : transitions) {
+      for (Transition transition : issueService.listTransitions(issue)) {
         json.value(transition.key());
       }
     }
@@ -58,13 +56,13 @@ public class IssueActionsWriter implements ServerComponent {
 
   public void writeActions(Issue issue, JsonWriter json) {
     json.name("actions").beginArray();
-    for (String action : actions((DefaultIssue) issue)) {
+    for (String action : actions(issue)) {
       json.value(action);
     }
     json.endArray();
   }
 
-  private List<String> actions(DefaultIssue issue) {
+  private List<String> actions(Issue issue) {
     List<String> actions = newArrayList();
     String login = UserSession.get().login();
     if (login != null) {
index 14f5dc61fa38f5bb461695ccb1473a7b6ec05145..9de0b8b32fbbf0f93870a55a4558dc40227fc3d6 100644 (file)
@@ -21,10 +21,16 @@ package org.sonar.server.issue.ws;
 
 import com.google.common.base.Function;
 import com.google.common.base.Strings;
+import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 import com.google.common.io.Resources;
 import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.*;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueComment;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.internal.DefaultIssueComment;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.server.ws.Request;
@@ -37,6 +43,7 @@ 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.db.IssueChangeDao;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.db.DbClient;
@@ -57,6 +64,8 @@ import javax.annotation.Nullable;
 import java.util.*;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+import static com.google.common.collect.Sets.newHashSet;
 
 public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
 
@@ -70,6 +79,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
 
   private static final String EXTRA_FIELDS_PARAM = "extra_fields";
 
+  private final IssueChangeDao issueChangeDao;
   private final IssueService service;
   private final IssueActionsWriter actionsWriter;
 
@@ -80,13 +90,14 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
   private final I18n i18n;
   private final Durations durations;
 
-  public SearchAction(IssueService service, IssueActionsWriter actionsWriter, RuleService ruleService, DbClient dbClient,
+  public SearchAction(DbClient dbClient, IssueChangeDao issueChangeDao, IssueService service, IssueActionsWriter actionsWriter, RuleService ruleService,
     ActionPlanService actionPlanService, UserFinder userFinder, I18n i18n, Durations durations) {
     super(SEARCH_ACTION);
+    this.dbClient = dbClient;
+    this.issueChangeDao = issueChangeDao;
     this.service = service;
     this.actionsWriter = actionsWriter;
     this.ruleService = ruleService;
-    this.dbClient = dbClient;
     this.actionPlanService = actionPlanService;
     this.userFinder = userFinder;
     this.i18n = i18n;
@@ -208,12 +219,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
 
   @Override
   protected Result<Issue> doSearch(IssueQuery query, QueryContext context) {
-    return ((DefaultIssueService)service).search(query, context);
-  }
-
-  @Override
-  protected void doResultResponse(Request request, QueryContext context, Result<Issue> result, JsonWriter json) {
-    writeIssues(result, request.paramAsStrings(EXTRA_FIELDS_PARAM), json);
+    return ((DefaultIssueService) service).search(query, context);
   }
 
   @Override
@@ -224,29 +230,41 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
 
   @Override
   protected void doContextResponse(Request request, QueryContext context, Result<Issue> result, JsonWriter json) {
-    // Insert the projects and component name;
-    Set<RuleKey> ruleKeys = new HashSet<RuleKey>();
-    Set<String> projectKeys = new HashSet<String>();
-    Set<String> componentKeys = new HashSet<String>();
-    Set<String> actionPlanKeys = new HashSet<String>();
-    List<String> userLogins = new ArrayList<String>();
+    List<String> issueKeys = newArrayList();
+    Set<RuleKey> ruleKeys = newHashSet();
+    Set<String> projectKeys = newHashSet();
+    Set<String> componentKeys = newHashSet();
+    Set<String> actionPlanKeys = newHashSet();
+    List<String> userLogins = newArrayList();
+    Map<String, User> usersByLogin = newHashMap();
+    Multimap<String, DefaultIssueComment> commentsByIssues = ArrayListMultimap.create();
 
     for (Issue issue : result.getHits()) {
+      issueKeys.add(issue.key());
       ruleKeys.add(issue.ruleKey());
       projectKeys.add(issue.projectKey());
       componentKeys.add(issue.componentKey());
       actionPlanKeys.add(issue.actionPlanKey());
-      userLogins.add(issue.authorLogin());
+      if (issue.reporter() != null) {
+        userLogins.add(issue.reporter());
+      }
+      if (issue.assignee() != null) {
+        userLogins.add(issue.assignee());
+      }
     }
 
-    writeRules(json, !request.mandatoryParamAsBoolean(IssueFilterParameters.HIDE_RULES) ? ruleService.getByKeys(ruleKeys) : Collections.<Rule>emptyList());
-    writeUsers(json, userFinder.findByLogins(userLogins));
-    writeActionPlans(json, actionPlanService.findByKeys(actionPlanKeys));
-
     DbSession session = dbClient.openSession(false);
     try {
+      List<DefaultIssueComment> comments = issueChangeDao.selectCommentsByIssues(session, issueKeys);
+      for (DefaultIssueComment issueComment : comments) {
+        userLogins.add(issueComment.userLogin());
+        commentsByIssues.put(issueComment.issueKey(), issueComment);
+      }
+      usersByLogin = getUsersByLogin(session, userLogins);
+
       List<ComponentDto> componentDtos = dbClient.componentDao().getByKeys(session, componentKeys);
       List<ComponentDto> projectDtos = dbClient.componentDao().getByKeys(session, projectKeys);
+
       componentDtos.addAll(projectDtos);
       writeProjects(json, projectDtos);
       writeComponents(json, componentDtos);
@@ -254,6 +272,13 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
       session.close();
     }
 
+    Map<String, ActionPlan> actionPlanByKeys = getActionPlanByKeys(actionPlanKeys);
+
+    writeIssues(result, commentsByIssues, usersByLogin, actionPlanByKeys, request.paramAsStrings(EXTRA_FIELDS_PARAM), json);
+    writeRules(json, !request.mandatoryParamAsBoolean(IssueFilterParameters.HIDE_RULES) ? ruleService.getByKeys(ruleKeys) : Collections.<Rule>emptyList());
+    writeUsers(json, usersByLogin);
+    writeActionPlans(json, actionPlanByKeys.values());
+
     // TODO remove legacy paging. Handled by the SearchRequestHandler
     writeLegacyPaging(context, json, result);
   }
@@ -294,7 +319,8 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
     json.endArray();
   }
 
-  private void writeIssues(Result<Issue> result, @Nullable List<String> extraFields, JsonWriter json) {
+  private void writeIssues(Result<Issue> result, Multimap<String, DefaultIssueComment> commentsByIssues, Map<String, User> usersByLogin, Map<String, ActionPlan> actionPlanByKeys,
+                           @Nullable List<String> extraFields, JsonWriter json) {
     json.name("issues").beginArray();
 
     for (Issue issue : result.getHits()) {
@@ -325,24 +351,22 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
         .prop("fUpdateAge", formatAgeDate(updateDate))
         .prop("closeDate", isoDate(issue.closeDate()));
 
-      // TODO add comments
-      // writeIssueComments(result, issue, json);
+      writeIssueComments(commentsByIssues.get(issue.key()), usersByLogin, json);
       writeIssueAttributes(issue, json);
-      // TODO Add fields
-      // writeIssueExtraFields(result, issue, extraFields, json);
+      writeIssueExtraFields(issue, usersByLogin, actionPlanByKeys, extraFields, json);
       json.endObject();
     }
 
     json.endArray();
   }
 
-  private void writeIssueComments(IssueQueryResult queryResult, Issue issue, JsonWriter json) {
-    if (!issue.comments().isEmpty()) {
+  private void writeIssueComments(Collection<DefaultIssueComment> issueComments, Map<String, User> usersByLogin, JsonWriter json) {
+    if (!issueComments.isEmpty()) {
       json.name("comments").beginArray();
       String login = UserSession.get().login();
-      for (IssueComment comment : issue.comments()) {
+      for (IssueComment comment : issueComments) {
         String userLogin = comment.userLogin();
-        User user = userLogin != null ? queryResult.user(userLogin) : null;
+        User user = userLogin != null ? usersByLogin.get(userLogin) : null;
         json.beginObject()
           .prop("key", comment.key())
           .prop("login", comment.userLogin())
@@ -367,7 +391,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
     }
   }
 
-  private void writeIssueExtraFields(IssueQueryResult result, Issue issue, @Nullable List<String> extraFields, JsonWriter json) {
+  private void writeIssueExtraFields(Issue issue, Map<String, User> usersByLogin, Map<String, ActionPlan> actionPlanByKeys, @Nullable List<String> extraFields, JsonWriter json) {
     if (extraFields != null && UserSession.get().isLoggedIn()) {
       if (extraFields.contains(ACTIONS_EXTRA_FIELD)) {
         actionsWriter.writeActions(issue, json);
@@ -379,19 +403,19 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
 
       String assignee = issue.assignee();
       if (extraFields.contains(ASSIGNEE_NAME_EXTRA_FIELD) && assignee != null) {
-        User user = result.user(assignee);
+        User user = usersByLogin.get(assignee);
         json.prop(ASSIGNEE_NAME_EXTRA_FIELD, user != null ? user.name() : null);
       }
 
       String reporter = issue.reporter();
       if (extraFields.contains(REPORTER_NAME_EXTRA_FIELD) && reporter != null) {
-        User user = result.user(reporter);
+        User user = usersByLogin.get(reporter);
         json.prop(REPORTER_NAME_EXTRA_FIELD, user != null ? user.name() : null);
       }
 
       String actionPlanKey = issue.actionPlanKey();
       if (extraFields.contains(ACTION_PLAN_NAME_EXTRA_FIELD) && actionPlanKey != null) {
-        ActionPlan actionPlan = result.actionPlan(issue);
+        ActionPlan actionPlan = actionPlanByKeys.get(actionPlanKey);
         json.prop(ACTION_PLAN_NAME_EXTRA_FIELD, actionPlan != null ? actionPlan.name() : null);
       }
     }
@@ -429,9 +453,9 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
     json.endArray();
   }
 
-  private void writeUsers(JsonWriter json, List<User> users) {
+  private void writeUsers(JsonWriter json, Map<String, User> usersByLogin) {
     json.name("users").beginArray();
-    for (User user : users) {
+    for (User user : usersByLogin.values()) {
       json.beginObject()
         .prop("login", user.login())
         .prop("name", user.name())
@@ -442,7 +466,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
     json.endArray();
   }
 
-  private void writeActionPlans(JsonWriter json, List<ActionPlan> plans) {
+  private void writeActionPlans(JsonWriter json, Collection<ActionPlan> plans) {
     if (!plans.isEmpty()) {
       json.name("actionPlans").beginArray();
       for (ActionPlan actionPlan : plans) {
@@ -467,6 +491,22 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
     }
   }
 
+  private Map<String, User> getUsersByLogin(DbSession session, List<String> userLogins) {
+    Map<String, User> usersByLogin = newHashMap();
+    for (User user : userFinder.findByLogins(userLogins)) {
+      usersByLogin.put(user.login(), user);
+    }
+    return usersByLogin;
+  }
+
+  private Map<String, ActionPlan> getActionPlanByKeys(Collection<String> actionPlanKeys) {
+    Map<String, ActionPlan> actionPlans = newHashMap();
+    for (ActionPlan actionPlan : actionPlanService.findByKeys(actionPlanKeys)) {
+      actionPlans.put(actionPlan.key(), actionPlan);
+    }
+    return actionPlans;
+  }
+
   @CheckForNull
   private String isoDate(@Nullable Date date) {
     if (date != null) {
index 7ede3e3a093553e92a81d52f43b6402c1bc2db99..80600912b15f04577772029dcac2ec7972876f91 100644 (file)
@@ -54,8 +54,6 @@ public abstract class SearchRequestHandler<QUERY, DOMAIN> implements RequestHand
 
   protected abstract QUERY doQuery(Request request);
 
-  protected abstract void doResultResponse(Request request, QueryContext context, Result<DOMAIN> result, JsonWriter json);
-
   protected abstract void doContextResponse(Request request, QueryContext context, Result<DOMAIN> result, JsonWriter json);
 
   protected abstract void doDefinition(WebService.NewAction action);
@@ -106,7 +104,6 @@ public abstract class SearchRequestHandler<QUERY, DOMAIN> implements RequestHand
 
     JsonWriter json = response.newJsonWriter().beginObject();
     this.writeStatistics(json, result, context);
-    doResultResponse(request, context, result, json);
     doContextResponse(request, context, result, json);
     if (context.isFacet()) {
       writeFacets(result, json);
index 1557bfe81d02c0d53a5f5cc78bb11bda71de5ed9..3e515920e2ee174e0049ea5b6e08118ae2af703e 100644 (file)
@@ -29,7 +29,7 @@ import org.sonar.api.security.DefaultGroups;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.component.ComponentDto;
-import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.issue.db.*;
 import org.sonar.core.permission.PermissionFacade;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.rule.RuleDto;
@@ -79,9 +79,7 @@ public class SearchActionMediumTest {
     tester.get(RuleDao.class).insert(session, rule);
 
     project = new ComponentDto()
-      .setId(1L)
-      .setKey("MyProject")
-      .setProjectId(1L);
+      .setKey("MyProject");
     db.componentDao().insert(session, project);
     db.snapshotDao().insert(session, SnapshotTesting.createForComponent(project));
 
@@ -90,18 +88,17 @@ public class SearchActionMediumTest {
     db.issueAuthorizationDao().synchronizeAfter(session, new Date(0));
 
     file = new ComponentDto()
-      .setProjectId(1L)
       .setKey("MyComponent")
-      .setId(2L);
+      .setProjectId(project.getId());
     db.componentDao().insert(session, file);
     db.snapshotDao().insert(session, SnapshotTesting.createForComponent(file));
 
-    UserDto john = new UserDto().setLogin("john").setName("John").setEmail("john@email.com").setActive(true);
+    UserDto john = new UserDto().setLogin("john").setName("John").setEmail("john@email.com");
     db.userDao().insert(session, john);
 
     session.commit();
 
-    MockUserSession.set().setLogin("gandalf");
+    MockUserSession.set().setLogin("john");
   }
 
   @After
@@ -119,10 +116,11 @@ public class SearchActionMediumTest {
   }
 
   @Test
-  public void find_single_result() throws Exception {
+  public void issue() throws Exception {
+    db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
+    db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
+
     IssueDto issue = IssueTesting.newDto(rule, file, project)
-      .setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
-      .setIssueUpdateDate(DateUtils.parseDate("2014-12-04"))
       .setRule(rule)
       .setDebt(10L)
       .setRootComponent(project)
@@ -130,15 +128,95 @@ public class SearchActionMediumTest {
       .setStatus("OPEN").setResolution("OPEN")
       .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
       .setSeverity("MAJOR")
-      .setAuthorLogin("john");
+      .setAuthorLogin("John")
+      .setAssignee("simon")
+      .setReporter("fabrice")
+      .setActionPlanKey("AP-ABCD")
+      .setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
+      .setIssueUpdateDate(DateUtils.parseDate("2014-12-04"));
     db.issueDao().insert(session, issue);
     session.commit();
 
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
+    // TODO date assertion is complex to test, and components id are not predictable, that's why strict boolean is set to false
+    result.assertJson(this.getClass(), "issue.json", false);
+  }
 
-    WsTester.Result result = request.execute();
-    // TODO Date assertion is complex to test
-    result.assertJson(this.getClass(), "single_result.json", false);
+  @Test
+  public void issue_with_comment() throws Exception {
+    db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
+
+    IssueDto issue = IssueTesting.newDto(rule, file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
+    db.issueDao().insert(session, issue);
+
+    tester.get(IssueChangeDao.class).insert(session,
+      new IssueChangeDto().setIssueKey(issue.getKey())
+        .setKey("COMMENT-ABCD")
+        .setChangeData("*My comment*")
+        .setChangeType(IssueChangeDto.TYPE_COMMENT)
+        .setUserLogin("john")
+        .setCreatedAt(DateUtils.parseDate("2014-09-09")));
+    tester.get(IssueChangeDao.class).insert(session,
+      new IssueChangeDto().setIssueKey(issue.getKey())
+        .setKey("COMMENT-ABCE")
+        .setChangeData("Another comment")
+        .setChangeType(IssueChangeDto.TYPE_COMMENT)
+        .setUserLogin("fabrice")
+        .setCreatedAt(DateUtils.parseDate("2014-09-10")));
+    session.commit();
+
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
+    result.assertJson(this.getClass(), "issue_with_comment.json", false);
+  }
+
+  @Test
+  public void issue_with_action_plan() throws Exception {
+    db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
+    db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
+
+    tester.get(ActionPlanDao.class).save(new ActionPlanDto()
+      .setKey("AP-ABCD")
+      .setName("1.0")
+      .setStatus("OPEN")
+      .setProjectId(project.getId())
+      .setUserLogin("simon")
+      .setDeadLine(DateUtils.parseDateTime("2014-01-24T19:10:03+0100"))
+      .setCreatedAt(DateUtils.parseDateTime("2014-01-22T19:10:03+0100"))
+      .setUpdatedAt(DateUtils.parseDateTime("2014-01-23T19:10:03+0100")));
+
+    IssueDto issue = IssueTesting.newDto(rule, file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+      .setActionPlanKey("AP-ABCD");
+    db.issueDao().insert(session, issue);
+    session.commit();
+
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
+    result.assertJson(this.getClass(), "issue_with_action_plan.json", false);
+  }
+
+  @Test
+  public void issue_with_extra_fields() throws Exception {
+    db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
+    db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
+
+    tester.get(ActionPlanDao.class).save(new ActionPlanDto()
+      .setKey("AP-ABCD")
+      .setName("1.0")
+      .setStatus("OPEN")
+      .setProjectId(project.getId())
+      .setUserLogin("simon"));
+
+    IssueDto issue = IssueTesting.newDto(rule, file, project)
+      .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+      .setAuthorLogin("John")
+      .setAssignee("simon")
+      .setReporter("fabrice")
+      .setActionPlanKey("AP-ABCD");
+    db.issueDao().insert(session, issue);
+    session.commit();
+
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
+      .setParam("extra_fields", "actions,transitions,assigneeName,reporterName,actionPlanName").execute();
+    result.assertJson(this.getClass(), "issue_with_extra_fields.json", false);
   }
 
   @Test
@@ -156,10 +234,7 @@ public class SearchActionMediumTest {
     db.issueDao().insert(session, issue);
     session.commit();
 
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
-    request.setParam(SearchAction.PARAM_FACETS, "true");
-
-    WsTester.Result result = request.execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(SearchAction.PARAM_FACETS, "true").execute();
     result.assertJson(this.getClass(), "display_facets.json", false);
   }
 
@@ -178,10 +253,7 @@ public class SearchActionMediumTest {
     db.issueDao().insert(session, issue);
     session.commit();
 
-    WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
-    request.setParam(IssueFilterParameters.HIDE_RULES, "true");
-
-    WsTester.Result result = request.execute();
+    WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(IssueFilterParameters.HIDE_RULES, "true").execute();
     result.assertJson(this.getClass(), "hide_rules.json", false);
   }
 
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json
new file mode 100644 (file)
index 0000000..4700d9a
--- /dev/null
@@ -0,0 +1,59 @@
+{
+  "maxResultsReached": false,
+  "total": 1,
+  "p": 1,
+  "ps": 100,
+  "issues": [
+    {
+      "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
+      "component": "MyComponent",
+      "project": "MyProject",
+      "rule": "xoo:x1",
+      "status": "OPEN",
+      "resolution": "OPEN",
+      "severity": "MAJOR",
+      "debt": "10min",
+      "author": "John",
+      "assignee": "simon",
+      "reporter": "fabrice",
+      "actionPlan": "AP-ABCD",
+      "updateDate": "2014-12-04T00:00:00+0100",
+      "fUpdateAge": "less than a minute"
+    }
+  ],
+  "rules": [
+    {
+      "key": "xoo:x1",
+      "name": "Rule name",
+      "desc": "Rule desc",
+      "status": "READY"
+    }
+  ],
+  "components": [
+    {
+      "key": "MyComponent"
+    },
+    {
+      "key": "MyProject"
+    }
+  ],
+  "projects": [
+    {
+      "key": "MyProject"
+    }
+  ],
+  "users": [
+    {
+      "login": "simon",
+      "name": "Simon",
+      "active": true,
+      "email": "simon@email.com"
+    },
+    {
+      "login": "fabrice",
+      "name": "Fabrice",
+      "active": true,
+      "email": "fabrice@email.com"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json
new file mode 100644 (file)
index 0000000..878af79
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "issues": [
+    {
+      "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
+      "actionPlan": "AP-ABCD"
+    }
+  ],
+  "actionPlans": [
+    {
+      "key": "AP-ABCD",
+      "name": "1.0",
+      "status": "OPEN",
+      "project": "MyProject",
+      "userLogin": "simon",
+      "deadLine": "2014-01-24T19:10:03+0100",
+      "fDeadLine": "Jan 24, 2014 7:10 PM",
+      "createdAt": "2014-01-22T19:10:03+0100",
+      "fCreatedAt": "Jan 22, 2014 7:10 PM",
+      "updatedAt": "2014-01-23T19:10:03+0100",
+      "fUpdatedAt": "Jan 23, 2014 7:10 PM"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json
new file mode 100644 (file)
index 0000000..8da9530
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "issues": [
+    {
+      "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
+      "comments": [
+        {
+          "key": "COMMENT-ABCD",
+          "login": "john",
+          "userName": "John",
+          "htmlText": "<em>My comment</em>",
+          "markdown": "*My comment*",
+          "updatable": true
+        },
+        {
+          "key": "COMMENT-ABCE",
+          "login": "fabrice",
+          "userName": "Fabrice",
+          "htmlText": "Another comment",
+          "markdown": "Another comment",
+          "updatable": false
+        }
+      ]
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_extra_fields.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_extra_fields.json
new file mode 100644 (file)
index 0000000..09d89a5
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "issues": [
+    {
+      "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
+      "assignee": "simon",
+      "assigneeName": "Simon",
+      "reporter": "fabrice",
+      "reporterName": "Fabrice",
+      "actionPlan": "AP-ABCD",
+      "actionPlanName": "1.0",
+      "actions": [
+        "comment", "assign", "assign_to_me", "plan"
+      ],
+      "transitions": [
+        "confirm", "resolve"
+      ]
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/single_result.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/single_result.json
deleted file mode 100644 (file)
index 46e7874..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-{
-  "maxResultsReached": false,
-  "total": 1,
-  "p": 1,
-  "ps": 100,
-  "issues": [
-    {
-      "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
-      "component": "MyComponent",
-      "project": "MyProject",
-      "rule": "xoo:x1",
-      "status": "OPEN",
-      "resolution": "OPEN",
-      "severity": "MAJOR",
-      "debt": "10min",
-      "author": "john",
-      "fUpdateAge": "less than a minute"
-    }
-  ],
-  "rules": [
-    {
-      "key": "xoo:x1",
-      "name": "Rule name",
-      "desc": "Rule desc",
-      "status": "READY"
-    }
-  ],
-  "components": [
-    {
-      "key": "MyComponent",
-      "id": 2
-    },
-    {
-      "key": "MyProject",
-      "id": 1
-    }
-  ],
-  "projects": [
-    {
-      "key": "MyProject",
-      "id": 1
-    }
-  ],
-  "users": [
-    {
-      "login": "john",
-      "name": "John",
-      "active": true,
-      "email": "john@email.com"
-    }
-  ]
-}
index 458781bae7523bb9f900ed9a270ab3bacef9b745..92e971323fc349a1038399b8f1075704e2c18a3b 100644 (file)
@@ -22,11 +22,11 @@ package org.sonar.core.issue.db;
 
 import com.google.common.collect.Lists;
 import org.apache.ibatis.session.ResultHandler;
-import org.apache.ibatis.session.SqlSession;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.internal.DefaultIssueComment;
 import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 
 import javax.annotation.CheckForNull;
@@ -47,7 +47,7 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
     this.mybatis = mybatis;
   }
 
-  public List<DefaultIssueComment> selectCommentsByIssues(SqlSession session, Collection<String> issueKeys) {
+  public List<DefaultIssueComment> selectCommentsByIssues(DbSession session, Collection<String> issueKeys) {
     List<DefaultIssueComment> comments = Lists.newArrayList();
     for (IssueChangeDto dto : selectByIssuesAndType(session, issueKeys, IssueChangeDto.TYPE_COMMENT)) {
       comments.add(dto.toComment());
@@ -56,7 +56,7 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
   }
 
   public List<FieldDiffs> selectChangelogByIssue(String issueKey) {
-    SqlSession session = mybatis.openSession(false);
+    DbSession session = mybatis.openSession(false);
     try {
       List<FieldDiffs> result = Lists.newArrayList();
       for (IssueChangeDto dto : selectByIssuesAndType(session, Arrays.asList(issueKey), IssueChangeDto.TYPE_FIELD_CHANGE)) {
@@ -69,7 +69,7 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
   }
 
   public void selectChangelogOnNonClosedIssuesByModuleAndType(Integer componentId, ResultHandler handler) {
-    SqlSession session = mybatis.openSession(false);
+    DbSession session = mybatis.openSession(false);
     try {
       Map<String, Object> params = newHashMap();
       params.put("componentId", componentId);
@@ -83,7 +83,7 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
 
   @CheckForNull
   public DefaultIssueComment selectCommentByKey(String commentKey) {
-    SqlSession session = mybatis.openSession(false);
+    DbSession session = mybatis.openSession(false);
     try {
       IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
       IssueChangeDto dto = mapper.selectByKeyAndType(commentKey, IssueChangeDto.TYPE_COMMENT);
@@ -94,7 +94,7 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
     }
   }
 
-  List<IssueChangeDto> selectByIssuesAndType(SqlSession session, Collection<String> issueKeys, String changeType) {
+  List<IssueChangeDto> selectByIssuesAndType(DbSession session, Collection<String> issueKeys, String changeType) {
     if (issueKeys.isEmpty()) {
       return Collections.emptyList();
     }
@@ -108,8 +108,12 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
     return dtosList;
   }
 
+  public void insert(DbSession session, IssueChangeDto change) {
+      session.getMapper(IssueChangeMapper.class).insert(change);
+  }
+
   public boolean delete(String key) {
-    SqlSession session = mybatis.openSession(false);
+    DbSession session = mybatis.openSession(false);
     try {
       IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
       int count = mapper.delete(key);
@@ -122,7 +126,7 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
   }
 
   public boolean update(IssueChangeDto change) {
-    SqlSession session = mybatis.openSession(false);
+    DbSession session = mybatis.openSession(false);
     try {
       IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
       int count = mapper.update(change);
index 4d4de63457d4602b8deaf9a77e3238a68b5bc0b3..29dba1d2455fb1ca0c068581e82438fbd34170da 100644 (file)
 package org.sonar.core.issue.db;
 
 import org.apache.ibatis.executor.result.DefaultResultHandler;
-import org.apache.ibatis.session.SqlSession;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.issue.internal.DefaultIssueComment;
 import org.sonar.api.issue.internal.FieldDiffs;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 
 import java.util.Arrays;
@@ -39,18 +40,26 @@ import static org.mockito.Mockito.mock;
 
 public class IssueChangeDaoTest extends AbstractDaoTestCase {
 
+  DbSession session;
+
   IssueChangeDao dao;
 
   @Before
-  public void setUp() {
+  public void createDao() {
+    session = getMyBatis().openSession(false);
     dao = new IssueChangeDao(getMyBatis());
   }
 
+  @After
+  public void tearDown() throws Exception {
+    session.close();
+  }
+
   @Test
   public void select_comments_by_issues() {
     setupData("shared");
 
-    SqlSession session = getMyBatis().openSession();
+    DbSession session = getMyBatis().openSession(false);
     List<DefaultIssueComment> comments = dao.selectCommentsByIssues(session, Arrays.asList("1000"));
     MyBatis.closeQuietly(session);
     assertThat(comments).hasSize(2);
@@ -70,7 +79,7 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase {
   public void select_comments_by_issues_on_huge_number_of_issues() {
     setupData("shared");
 
-    SqlSession session = getMyBatis().openSession();
+    DbSession session = getMyBatis().openSession(false);
     List<String> hugeNbOfIssues = newArrayList();
     for (int i=0; i<4500; i++) {
       hugeNbOfIssues.add("ABCD"+i);
@@ -151,7 +160,7 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase {
   @Test
   public void select_comments_by_issues_empty_input() {
     // no need to connect to db
-    SqlSession session = mock(SqlSession.class);
+    DbSession session = mock(DbSession.class);
     List<DefaultIssueComment> comments = dao.selectCommentsByIssues(session, Collections.<String>emptyList());
 
     assertThat(comments).isEmpty();
@@ -173,6 +182,26 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase {
     assertThat(dao.delete("UNKNOWN")).isFalse();
   }
 
+  @Test
+  public void insert() {
+    setupData("empty");
+
+    IssueChangeDto changeDto = new IssueChangeDto()
+      .setKey("EFGH")
+      .setUserLogin("emmerik")
+      .setChangeData("Some text")
+      .setChangeType("comment")
+      .setIssueKey("ABCDE")
+      .setCreatedAt(DateUtils.parseDate("2014-09-09"))
+      .setUpdatedAt(DateUtils.parseDate("2014-09-10"))
+      .setIssueChangeCreationDate(DateUtils.parseDate("2014-09-11"));
+
+    dao.insert(session, changeDto);
+    session.commit();
+
+    checkTable("insert", "issue_changes");
+  }
+
   @Test
   public void update() {
     setupData("update");
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/empty.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/empty.xml
new file mode 100644 (file)
index 0000000..871dedc
--- /dev/null
@@ -0,0 +1,3 @@
+<dataset>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml
new file mode 100644 (file)
index 0000000..a91841a
--- /dev/null
@@ -0,0 +1,15 @@
+<dataset>
+
+  <issue_changes
+    id="1"
+    kee="EFGH"
+    issue_key="ABCDE"
+    user_login="emmerik"
+    change_type="comment"
+    change_data="Some text"
+    created_at="2014-09-09"
+    updated_at="2014-09-10"
+    issue_change_creation_date="2014-09-11"
+    />
+
+</dataset>