From f76df3e934c7b76141c58f24ac3303ae358cdcf4 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 25 Sep 2014 15:07:29 +0200 Subject: [PATCH] SONAR-5531 Return comments and take into account extra fields param --- .../server/issue/DefaultIssueFinder.java | 6 +- .../server/issue/DefaultIssueService.java | 3 +- .../server/issue/index/IssueNormalizer.java | 3 - .../server/issue/ws/IssueActionsWriter.java | 8 +- .../sonar/server/issue/ws/SearchAction.java | 112 ++++++++++------ .../search/ws/SearchRequestHandler.java | 3 - .../issue/ws/SearchActionMediumTest.java | 120 ++++++++++++++---- .../{single_result.json => issue.json} | 27 ++-- .../issue_with_action_plan.json | 23 ++++ .../issue_with_comment.json | 25 ++++ .../issue_with_extra_fields.json | 19 +++ .../sonar/core/issue/db/IssueChangeDao.java | 20 +-- .../core/issue/db/IssueChangeDaoTest.java | 39 +++++- .../issue/db/IssueChangeDaoTest/empty.xml | 3 + .../db/IssueChangeDaoTest/insert-result.xml | 15 +++ 15 files changed, 327 insertions(+), 99 deletions(-) rename server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/{single_result.json => issue.json} (59%) create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_extra_fields.json create mode 100644 sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/empty.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java index 41364c818af..0c72c7c8bef 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java @@ -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 authorizedIssues = issueDao.selectIssueIds(query, UserSession.get().userId(), sqlSession); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java index fd24180b2de..9eb6f17acb3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java @@ -137,8 +137,7 @@ public class DefaultIssueService implements IssueService { List outTransitions = workflow.outTransitions(issue); List allowedTransitions = new ArrayList(); 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); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java index f8c6cb2048a..42f9a0b79a8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java @@ -118,9 +118,6 @@ public class IssueNormalizer extends BaseNormalizer { 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 */ diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java index 4057524f0f8..23a2c1093d8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java @@ -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 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 actions(DefaultIssue issue) { + private List actions(Issue issue) { List actions = newArrayList(); String login = UserSession.get().login(); if (login != null) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 14f5dc61fa3..9de0b8b32fb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -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 { @@ -70,6 +79,7 @@ public class SearchAction extends SearchRequestHandler { 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 { 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 { @Override protected Result doSearch(IssueQuery query, QueryContext context) { - return ((DefaultIssueService)service).search(query, context); - } - - @Override - protected void doResultResponse(Request request, QueryContext context, Result 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 { @Override protected void doContextResponse(Request request, QueryContext context, Result result, JsonWriter json) { - // Insert the projects and component name; - Set ruleKeys = new HashSet(); - Set projectKeys = new HashSet(); - Set componentKeys = new HashSet(); - Set actionPlanKeys = new HashSet(); - List userLogins = new ArrayList(); + List issueKeys = newArrayList(); + Set ruleKeys = newHashSet(); + Set projectKeys = newHashSet(); + Set componentKeys = newHashSet(); + Set actionPlanKeys = newHashSet(); + List userLogins = newArrayList(); + Map usersByLogin = newHashMap(); + Multimap 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.emptyList()); - writeUsers(json, userFinder.findByLogins(userLogins)); - writeActionPlans(json, actionPlanService.findByKeys(actionPlanKeys)); - DbSession session = dbClient.openSession(false); try { + List comments = issueChangeDao.selectCommentsByIssues(session, issueKeys); + for (DefaultIssueComment issueComment : comments) { + userLogins.add(issueComment.userLogin()); + commentsByIssues.put(issueComment.issueKey(), issueComment); + } + usersByLogin = getUsersByLogin(session, userLogins); + List componentDtos = dbClient.componentDao().getByKeys(session, componentKeys); List projectDtos = dbClient.componentDao().getByKeys(session, projectKeys); + componentDtos.addAll(projectDtos); writeProjects(json, projectDtos); writeComponents(json, componentDtos); @@ -254,6 +272,13 @@ public class SearchAction extends SearchRequestHandler { session.close(); } + Map 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.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 { json.endArray(); } - private void writeIssues(Result result, @Nullable List extraFields, JsonWriter json) { + private void writeIssues(Result result, Multimap commentsByIssues, Map usersByLogin, Map actionPlanByKeys, + @Nullable List extraFields, JsonWriter json) { json.name("issues").beginArray(); for (Issue issue : result.getHits()) { @@ -325,24 +351,22 @@ public class SearchAction extends SearchRequestHandler { .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 issueComments, Map 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 { } } - private void writeIssueExtraFields(IssueQueryResult result, Issue issue, @Nullable List extraFields, JsonWriter json) { + private void writeIssueExtraFields(Issue issue, Map usersByLogin, Map actionPlanByKeys, @Nullable List 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 { 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 { json.endArray(); } - private void writeUsers(JsonWriter json, List users) { + private void writeUsers(JsonWriter json, Map 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 { json.endArray(); } - private void writeActionPlans(JsonWriter json, List plans) { + private void writeActionPlans(JsonWriter json, Collection plans) { if (!plans.isEmpty()) { json.name("actionPlans").beginArray(); for (ActionPlan actionPlan : plans) { @@ -467,6 +491,22 @@ public class SearchAction extends SearchRequestHandler { } } + private Map getUsersByLogin(DbSession session, List userLogins) { + Map usersByLogin = newHashMap(); + for (User user : userFinder.findByLogins(userLogins)) { + usersByLogin.put(user.login(), user); + } + return usersByLogin; + } + + private Map getActionPlanByKeys(Collection actionPlanKeys) { + Map 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) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java b/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java index 7ede3e3a093..80600912b15 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java @@ -54,8 +54,6 @@ public abstract class SearchRequestHandler implements RequestHand protected abstract QUERY doQuery(Request request); - protected abstract void doResultResponse(Request request, QueryContext context, Result result, JsonWriter json); - protected abstract void doContextResponse(Request request, QueryContext context, Result result, JsonWriter json); protected abstract void doDefinition(WebService.NewAction action); @@ -106,7 +104,6 @@ public abstract class SearchRequestHandler 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); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java index 1557bfe81d0..3e515920e2e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java @@ -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/single_result.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json similarity index 59% rename from server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/single_result.json rename to server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json index 46e78743fd9..4700d9a1bef 100644 --- 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/issue.json @@ -13,7 +13,11 @@ "resolution": "OPEN", "severity": "MAJOR", "debt": "10min", - "author": "john", + "author": "John", + "assignee": "simon", + "reporter": "fabrice", + "actionPlan": "AP-ABCD", + "updateDate": "2014-12-04T00:00:00+0100", "fUpdateAge": "less than a minute" } ], @@ -27,26 +31,29 @@ ], "components": [ { - "key": "MyComponent", - "id": 2 + "key": "MyComponent" }, { - "key": "MyProject", - "id": 1 + "key": "MyProject" } ], "projects": [ { - "key": "MyProject", - "id": 1 + "key": "MyProject" } ], "users": [ { - "login": "john", - "name": "John", + "login": "simon", + "name": "Simon", "active": true, - "email": "john@email.com" + "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 index 00000000000..878af790fef --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json @@ -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 index 00000000000..8da9530a83f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json @@ -0,0 +1,25 @@ +{ + "issues": [ + { + "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2", + "comments": [ + { + "key": "COMMENT-ABCD", + "login": "john", + "userName": "John", + "htmlText": "My comment", + "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 index 00000000000..09d89a59f74 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_extra_fields.json @@ -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/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java index 458781bae75..92e971323fc 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java @@ -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 selectCommentsByIssues(SqlSession session, Collection issueKeys) { + public List selectCommentsByIssues(DbSession session, Collection issueKeys) { List 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 selectChangelogByIssue(String issueKey) { - SqlSession session = mybatis.openSession(false); + DbSession session = mybatis.openSession(false); try { List 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 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 selectByIssuesAndType(SqlSession session, Collection issueKeys, String changeType) { + List selectByIssuesAndType(DbSession session, Collection 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); diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java index 4d4de63457d..29dba1d2455 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java @@ -20,13 +20,14 @@ 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 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 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 comments = dao.selectCommentsByIssues(session, Collections.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 index 00000000000..871dedcb5e9 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/empty.xml @@ -0,0 +1,3 @@ + + + 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 index 00000000000..a91841afa11 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml @@ -0,0 +1,15 @@ + + + + + -- 2.39.5