diff options
10 files changed, 270 insertions, 71 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java index 7b93ecfb41f..1fa8b6d024c 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java @@ -73,20 +73,20 @@ public class IssueDao implements BatchComponent, ServerComponent { } @VisibleForTesting - List<IssueDto> selectIssues(IssueQuery query, @Nullable Integer userId, Integer maxResult) { + List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, Integer maxResult) { SqlSession session = mybatis.openSession(); try { - return selectIssues(query, userId, maxResult, session); + return selectIssueIds(query, userId, maxResult, session); } finally { MyBatis.closeQuietly(session); } } @VisibleForTesting - List<IssueDto> selectIssues(IssueQuery query) { + List<IssueDto> selectIssueIds(IssueQuery query) { SqlSession session = mybatis.openSession(); try { - return selectIssues(query, null, Integer.MAX_VALUE, session); + return selectIssueIds(query, null, Integer.MAX_VALUE, session); } finally { MyBatis.closeQuietly(session); } @@ -95,13 +95,31 @@ public class IssueDao implements BatchComponent, ServerComponent { /** * The returned IssueDto list contains only the issue id and the sort column */ + public List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, SqlSession session){ + return selectIssueIds(query, userId, query.maxResults(), session); + } + + private List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, Integer maxResults, SqlSession session){ + IssueMapper mapper = session.getMapper(IssueMapper.class); + return mapper.selectIssues(query, query.componentRoots(), userId, query.requiredRole(), maxResults, true); + } + + public List<IssueDto> selectIssues(IssueQuery query) { + SqlSession session = mybatis.openSession(); + try { + return selectIssues(query, null, Integer.MAX_VALUE, session); + } finally { + MyBatis.closeQuietly(session); + } + } + public List<IssueDto> selectIssues(IssueQuery query, @Nullable Integer userId, SqlSession session){ return selectIssues(query, userId, query.maxResults(), session); } - private List<IssueDto> selectIssues(IssueQuery query, @Nullable Integer userId, Integer maxResults, SqlSession session){ + public List<IssueDto> selectIssues(IssueQuery query, @Nullable Integer userId, Integer maxResults, SqlSession session){ IssueMapper mapper = session.getMapper(IssueMapper.class); - return mapper.selectIssues(query, query.componentRoots(), userId, query.requiredRole(), maxResults); + return mapper.selectIssues(query, query.componentRoots(), userId, query.requiredRole(), maxResults, false); } @VisibleForTesting diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java index 42c9b091340..d8ea5863a07 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java @@ -33,8 +33,13 @@ public interface IssueMapper { List<IssueDto> selectNonClosedIssuesByModule(int rootComponentId); + /** + * Return a paginated list of authorized issue ids for a user. + * If the role is null, then the authorisation check is disabled. + */ List<IssueDto> selectIssues(@Param("query") IssueQuery query, @Param("componentRootKeys") Collection<String> componentRootKeys, - @Nullable @Param("userId") Integer userId, @Param("role") String role, @Param("maxResults") Integer maxResult); + @Nullable @Param("userId") Integer userId, @Nullable @Param("role") String role, + @Param("maxResults") Integer maxResult, @Param("returnOnlyIdAndSortColumns") boolean returnOnlyIdAndSortColumns); void insert(IssueDto issue); diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml index 505f9ce267e..61ef848f10f 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml @@ -186,30 +186,75 @@ </select> <select id="selectIssues" parameterType="map" resultType="Issue" fetchSize="100000"> - select i.id - <include refid="sortColumn"/> + select + <choose> + <when test="returnOnlyIdAndSortColumns == true"> + i.id + <include refid="sortColumn"/> + from issues i + </when> + <otherwise> + <include refid="issueColumns"/> + from issues i + inner join rules r on r.id=i.rule_id + inner join projects p on p.id=i.component_id + inner join projects root on root.id=i.root_component_id + </otherwise> + </choose> <include refid="selectQueryConditions"/> - limit #{maxResults} + <if test="maxResults != null"> + limit #{maxResults} + </if> </select> <!-- SQL Server --> <select id="selectIssues" parameterType="map" resultType="Issue" fetchSize="100000" databaseId="mssql"> - select top (#{maxResults}) i.id - <include refid="sortColumn"/> + select + <if test="maxResults != null"> + top (#{maxResults}) + </if> + <choose> + <when test="returnOnlyIdAndSortColumns == true"> + i.id + <include refid="sortColumn"/> + from issues i + </when> + <otherwise> + <include refid="issueColumns"/> + from issues i + inner join rules r on r.id=i.rule_id + inner join projects p on p.id=i.component_id + inner join projects root on root.id=i.root_component_id + </otherwise> + </choose> <include refid="selectQueryConditions"/> </select> <!-- Oracle --> <select id="selectIssues" parameterType="map" resultType="Issue" fetchSize="100000" databaseId="oracle"> - select * from (select i.id - <include refid="sortColumn"/> + select * from (select + <choose> + <when test="returnOnlyIdAndSortColumns == true"> + i.id + <include refid="sortColumn"/> + </when> + <otherwise> + <include refid="issueColumns"/> + from issues i + inner join rules r on r.id=i.rule_id + inner join projects p on p.id=i.component_id + inner join projects root on root.id=i.root_component_id + </otherwise> + </choose> <include refid="selectQueryConditions"/> - ) where rownum <= #{maxResults} + ) + <if test="maxResults != null"> + where rownum <= #{maxResults} + </if> </select> <sql id="selectQueryConditions"> - from issues i - <if test="componentRootKeys.size() == 0"> + <if test="componentRootKeys.size() == 0 and role != null"> inner join (<include refid="org.sonar.core.user.AuthorizationMapper.selectAuthorizedRootProjectsIdsQuery" />) authorizedProjects on authorizedProjects.root_project_id=i.root_component_id </if> <if test="componentRootKeys.size() > 0"> diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml index beab090bafd..accae5e1b88 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml @@ -9,6 +9,7 @@ <if test="'ASSIGNEE'.equals(column)"> i.assignee </if> + from issues i <include refid="org.sonar.core.issue.db.IssueMapper.selectQueryConditions"/> </select> diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java index 035fc1a636a..180725f7150 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java @@ -84,7 +84,19 @@ public class IssueDaoTest extends AbstractDaoTestCase { IssueQuery query = IssueQuery.builder().requiredRole("user").build(); - List<IssueDto> results = dao.selectIssues(query); + List<IssueDto> results = dao.selectIssueIds(query); + assertThat(results).hasSize(3); + IssueDto issue = results.get(0); + assertThat(issue.getId()).isNotNull(); + } + + @Test + public void should_select_all_without_authorisation() { + setupData("should_select_all"); + + IssueQuery query = IssueQuery.builder().requiredRole(null).build(); + + List<IssueDto> results = dao.selectIssueIds(query); assertThat(results).hasSize(3); IssueDto issue = results.get(0); assertThat(issue.getId()).isNotNull(); @@ -95,13 +107,13 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_rules"); IssueQuery query = IssueQuery.builder().rules(newArrayList(RuleKey.of("squid", "AvoidCycle"))).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).hasSize(2); + assertThat(dao.selectIssueIds(query)).hasSize(2); query = IssueQuery.builder().rules(newArrayList(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("squid", "NullRef"))).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).hasSize(3); + assertThat(dao.selectIssueIds(query)).hasSize(3); query = IssueQuery.builder().rules(newArrayList(RuleKey.of("squid", "Other"))).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).isEmpty(); + assertThat(dao.selectIssueIds(query)).isEmpty(); } @Test @@ -109,10 +121,10 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_date_creation"); IssueQuery query = IssueQuery.builder().createdAfter(DateUtils.parseDate("2013-04-15")).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).hasSize(1); + assertThat(dao.selectIssueIds(query)).hasSize(1); query = IssueQuery.builder().createdBefore(DateUtils.parseDate("2013-04-17")).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).hasSize(2); + assertThat(dao.selectIssueIds(query)).hasSize(2); } @Test @@ -120,21 +132,21 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_component"); IssueQuery query = IssueQuery.builder().components(newArrayList("Action.java")).requiredRole("user").build(); - List<IssueDto> issues = newArrayList(dao.selectIssues(query)); + List<IssueDto> issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(1); assertThat(issues.get(0).getId()).isEqualTo(100); query = IssueQuery.builder().components(newArrayList("Filter.java")).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(1); assertThat(issues.get(0).getId()).isEqualTo(101); query = IssueQuery.builder().components(newArrayList("struts-core")).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).isEmpty(); query = IssueQuery.builder().components(newArrayList("struts")).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).isEmpty(); } @@ -143,22 +155,22 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_component_root"); IssueQuery query = IssueQuery.builder().componentRoots(newArrayList("struts")).requiredRole("user").build(); - List<IssueDto> issues = newArrayList(dao.selectIssues(query)); + List<IssueDto> issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(2); assertThat(getIssueIds(issues)).containsOnly(100l, 101l); query = IssueQuery.builder().componentRoots(newArrayList("struts-core")).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(2); assertThat(getIssueIds(issues)).containsOnly(100l, 101l); query = IssueQuery.builder().componentRoots(newArrayList("Filter.java")).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(1); assertThat(issues.get(0).getId()).isEqualTo(101); query = IssueQuery.builder().componentRoots(newArrayList("not-found")).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).isEmpty(); } @@ -167,15 +179,15 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_assigned"); IssueQuery query = IssueQuery.builder().assigned(true).requiredRole("user").build(); - List<IssueDto> issues = newArrayList(dao.selectIssues(query)); + List<IssueDto> issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(2); query = IssueQuery.builder().assigned(false).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(1); query = IssueQuery.builder().assigned(null).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(3); } @@ -184,15 +196,15 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_planned"); IssueQuery query = IssueQuery.builder().planned(true).requiredRole("user").build(); - List<IssueDto> issues = newArrayList(dao.selectIssues(query)); + List<IssueDto> issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(2); query = IssueQuery.builder().planned(false).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(1); query = IssueQuery.builder().planned(null).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(3); } @@ -201,15 +213,15 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_resolved"); IssueQuery query = IssueQuery.builder().resolved(true).requiredRole("user").build(); - List<IssueDto> issues = newArrayList(dao.selectIssues(query)); + List<IssueDto> issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(2); query = IssueQuery.builder().resolved(false).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(1); query = IssueQuery.builder().resolved(null).requiredRole("user").build(); - issues = newArrayList(dao.selectIssues(query)); + issues = newArrayList(dao.selectIssueIds(query)); assertThat(issues).hasSize(3); } @@ -218,13 +230,13 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_by_action_plans"); IssueQuery query = IssueQuery.builder().actionPlans(newArrayList("ABC")).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).hasSize(2); + assertThat(dao.selectIssueIds(query)).hasSize(2); query = IssueQuery.builder().actionPlans(newArrayList("ABC", "DEF")).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).hasSize(3); + assertThat(dao.selectIssueIds(query)).hasSize(3); query = IssueQuery.builder().actionPlans(newArrayList("<Unkown>")).requiredRole("user").build(); - assertThat(dao.selectIssues(query)).isEmpty(); + assertThat(dao.selectIssueIds(query)).isEmpty(); } @Test @@ -232,10 +244,10 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("should_select_issues_for_authorized_projects"); IssueQuery query = IssueQuery.builder().requiredRole("user").build(); - List<IssueDto> results = dao.selectIssues(query, 100, 10); + List<IssueDto> results = dao.selectIssueIds(query, 100, 10); assertThat(results).hasSize(2); - results = dao.selectIssues(query, null, 10); + results = dao.selectIssueIds(query, null, 10); assertThat(results).isEmpty(); } @@ -244,7 +256,7 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_issues_return_limited_results"); IssueQuery query = IssueQuery.builder().requiredRole("user").build(); - List<IssueDto> results = dao.selectIssues(query, null, 2); + List<IssueDto> results = dao.selectIssueIds(query, null, 2); assertThat(results).hasSize(2); } @@ -253,27 +265,27 @@ public class IssueDaoTest extends AbstractDaoTestCase { setupData("shared", "should_select_issues_with_sort_column"); IssueQuery query = IssueQuery.builder().sort(IssueQuery.SORT_BY_ASSIGNEE).requiredRole("user").build(); - List<IssueDto> results = dao.selectIssues(query); + List<IssueDto> results = dao.selectIssueIds(query); assertThat(results.get(0).getAssignee()).isNotNull(); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_SEVERITY).requiredRole("user").build(); - results = dao.selectIssues(query); + results = dao.selectIssueIds(query); assertThat(results.get(0).getSeverity()).isNotNull(); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_STATUS).requiredRole("user").build(); - results = dao.selectIssues(query); + results = dao.selectIssueIds(query); assertThat(results.get(0).getStatus()).isNotNull(); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_CREATION_DATE).requiredRole("user").build(); - results = dao.selectIssues(query); + results = dao.selectIssueIds(query); assertThat(results.get(0).getIssueCreationDate()).isNotNull(); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_UPDATE_DATE).requiredRole("user").build(); - results = dao.selectIssues(query); + results = dao.selectIssueIds(query); assertThat(results.get(0).getIssueUpdateDate()).isNotNull(); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_CLOSE_DATE).requiredRole("user").build(); - results = dao.selectIssues(query); + results = dao.selectIssueIds(query); assertThat(results.get(0).getIssueCloseDate()).isNotNull(); } @@ -319,6 +331,43 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(results).isEmpty(); } + @Test + public void should_select_issues_all_columns() { + setupData("shared", "should_return_all_columns"); + + IssueQuery query = IssueQuery.builder().requiredRole("user").build(); + + List<IssueDto> results = dao.selectIssues(query); + assertThat(results).hasSize(1); + IssueDto issue = results.get(0); + assertThat(issue.getKee()).isEqualTo("ABCDE"); + assertThat(issue.getId()).isEqualTo(100L); + assertThat(issue.getComponentId()).isEqualTo(401); + assertThat(issue.getRootComponentId()).isEqualTo(399); + assertThat(issue.getRuleId()).isEqualTo(500); + assertThat(issue.getSeverity()).isEqualTo("BLOCKER"); + assertThat(issue.isManualSeverity()).isFalse(); + assertThat(issue.getMessage()).isNull(); + assertThat(issue.getLine()).isEqualTo(200); + assertThat(issue.getEffortToFix()).isEqualTo(4.2); + assertThat(issue.getStatus()).isEqualTo("OPEN"); + assertThat(issue.getResolution()).isEqualTo("FIXED"); + assertThat(issue.getChecksum()).isEqualTo("XXX"); + assertThat(issue.getAuthorLogin()).isEqualTo("karadoc"); + assertThat(issue.getReporter()).isEqualTo("arthur"); + assertThat(issue.getAssignee()).isEqualTo("perceval"); + assertThat(issue.getIssueAttributes()).isEqualTo("JIRA=FOO-1234"); + assertThat(issue.getIssueCreationDate()).isNotNull(); + assertThat(issue.getIssueUpdateDate()).isNotNull(); + assertThat(issue.getIssueCloseDate()).isNotNull(); + assertThat(issue.getCreatedAt()).isNotNull(); + assertThat(issue.getUpdatedAt()).isNotNull(); + assertThat(issue.getRuleRepo()).isEqualTo("squid"); + assertThat(issue.getRule()).isEqualTo("AvoidCycle"); + assertThat(issue.getComponentKey()).isEqualTo("Action.java"); + assertThat(issue.getRootComponentKey()).isEqualTo("struts"); + } + private List<Long> getIssueIds(List<IssueDto> issues) { return newArrayList(Iterables.transform(issues, new Function<IssueDto, Long>() { @Override diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_return_all_columns.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_return_all_columns.xml new file mode 100644 index 00000000000..9a5cc947917 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_return_all_columns.xml @@ -0,0 +1,28 @@ +<dataset> + + <issues + id="100" + kee="ABCDE" + component_id="401" + root_component_id="399" + rule_id="500" + severity="BLOCKER" + manual_severity="[false]" + message="[null]" + line="200" + effort_to_fix="4.2" + status="OPEN" + resolution="FIXED" + checksum="XXX" + reporter="arthur" + assignee="perceval" + author_login="karadoc" + issue_attributes="JIRA=FOO-1234" + issue_creation_date="2013-04-16" + issue_update_date="2013-04-16" + issue_close_date="2013-04-16" + created_at="2013-04-16" + updated_at="2013-04-16" + /> + +</dataset> 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 index ca929ec205e..5bac54257a5 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ActionPlanService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ActionPlanService.java @@ -24,13 +24,15 @@ 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.db.ActionPlanDao; -import org.sonar.core.issue.db.ActionPlanDto; -import org.sonar.core.issue.db.ActionPlanStatsDao; -import org.sonar.core.issue.db.ActionPlanStatsDto; +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; @@ -39,10 +41,7 @@ import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; @@ -55,12 +54,20 @@ public class ActionPlanService implements ServerComponent { private final ActionPlanStatsDao actionPlanStatsDao; private final ResourceDao resourceDao; private final AuthorizationDao authorizationDao; + private final IssueDao issueDao; + private final IssueUpdater issueUpdater; + private final IssueStorage issueStorage; - public ActionPlanService(ActionPlanDao actionPlanDao, ActionPlanStatsDao actionPlanStatsDao, ResourceDao resourceDao, AuthorizationDao authorizationDao) { + + public ActionPlanService(ActionPlanDao actionPlanDao, ActionPlanStatsDao actionPlanStatsDao, ResourceDao resourceDao, AuthorizationDao authorizationDao, + IssueDao issueDao, IssueUpdater issueUpdater, IssueStorage issueStorage) { this.actionPlanDao = actionPlanDao; this.actionPlanStatsDao = actionPlanStatsDao; this.resourceDao = resourceDao; this.authorizationDao = authorizationDao; + this.issueDao = issueDao; + this.issueUpdater = issueUpdater; + this.issueStorage = issueStorage; } public ActionPlan create(ActionPlan actionPlan, UserSession userSession) { @@ -78,10 +85,32 @@ public class ActionPlanService implements ServerComponent { } public void delete(String actionPlanKey, UserSession userSession) { - checkAuthorization(userSession, findActionPlanDto(actionPlanKey).getProjectKey(), UserRole.ADMIN); + ActionPlanDto dto = findActionPlanDto(actionPlanKey); + checkAuthorization(userSession, dto.getProjectKey(), UserRole.ADMIN); + 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); checkAuthorization(userSession, actionPlanDto.getProjectKey(), UserRole.ADMIN); diff --git a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java index 2cd7a861fdb..a36d69709ad 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java @@ -99,7 +99,7 @@ public class DefaultIssueFinder implements IssueFinder { SqlSession sqlSession = myBatis.openSession(); try { // 1. Select the authorized ids of all the issues that match the query - List<IssueDto> authorizedIssues = issueDao.selectIssues(query, UserSession.get().userId(), sqlSession); + List<IssueDto> authorizedIssues = issueDao.selectIssueIds(query, UserSession.get().userId(), sqlSession); // 2. Sort all authorized issues List<IssueDto> authorizedSortedIssues = sort(authorizedIssues, query, authorizedIssues.size()); 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 index 0f310202f79..f27f8155f62 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ActionPlanServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ActionPlanServiceTest.java @@ -22,14 +22,17 @@ package org.sonar.server.issue; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; 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.db.ActionPlanDao; -import org.sonar.core.issue.db.ActionPlanDto; -import org.sonar.core.issue.db.ActionPlanStatsDao; -import org.sonar.core.issue.db.ActionPlanStatsDto; +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; @@ -51,6 +54,10 @@ public class ActionPlanServiceTest { private ResourceDao resourceDao = mock(ResourceDao.class); private AuthorizationDao authorizationDao = mock(AuthorizationDao.class); private UserSession userSession = mock(UserSession.class); + private IssueDao issueDao = mock(IssueDao.class); + private IssueUpdater issueUpdater = mock(IssueUpdater.class); + private IssueStorage issueStorage = mock(IssueStorage.class); + private ActionPlanService actionPlanService; @Before @@ -59,7 +66,7 @@ public class ActionPlanServiceTest { when(userSession.userId()).thenReturn(10); when(authorizationDao.isAuthorizedComponentId(anyLong(), eq(10), anyString())).thenReturn(true); - actionPlanService = new ActionPlanService(actionPlanDao, actionPlanStatsDao, resourceDao, authorizationDao); + actionPlanService = new ActionPlanService(actionPlanDao, actionPlanStatsDao, resourceDao, authorizationDao, issueDao, issueUpdater, issueStorage); } @Test @@ -121,6 +128,23 @@ public class ActionPlanServiceTest { } @Test + public void should_unplan_all_linked_issues_when_deleting_an_action_plan() { + when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD")); + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey("org.sonar.Sample").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((String) null), any(IssueChangeContext.class))).thenReturn(true); + + ArgumentCaptor<DefaultIssue> captor = ArgumentCaptor.forClass(DefaultIssue.class); + actionPlanService.delete("ABCD", userSession); + verify(actionPlanDao).delete("ABCD"); + verify(authorizationDao).isAuthorizedComponentId(anyLong(), anyInt(), eq(UserRole.ADMIN)); + verify(issueUpdater).plan(captor.capture(), eq((String) null), any(IssueChangeContext.class)); + verify(issueStorage).save(newArrayList(captor.getAllValues())); + } + + @Test public void should_find_by_key() { when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD")); when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey("org.sonar.Sample").setId(1l)); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java index af2689bc7ce..1ed91d8bbbf 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java @@ -85,7 +85,7 @@ public class DefaultIssueFinderTest { when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); - verify(issueDao).selectIssues(eq(query), anyInt(), any(SqlSession.class)); + verify(issueDao).selectIssueIds(eq(query), anyInt(), any(SqlSession.class)); assertThat(results.issues()).hasSize(2); DefaultIssue issue = (DefaultIssue) results.issues().iterator().next(); @@ -109,7 +109,7 @@ public class DefaultIssueFinderTest { .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); - when(issueDao.selectIssues(eq(query), anyInt(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectIssueIds(eq(query), anyInt(), any(SqlSession.class))).thenReturn(dtoList); when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); @@ -275,7 +275,7 @@ public class DefaultIssueFinderTest { @Test public void should_get_empty_result_when_no_issue() { IssueQuery query = IssueQuery.builder().build(); - when(issueDao.selectIssues(eq(query), anyInt(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList()); + when(issueDao.selectIssueIds(eq(query), anyInt(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList()); when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList()); IssueQueryResult results = finder.find(query); |