]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4449 Issue display should not fail on deleted action plan
authorJulien Lancelot <julien.lancelot@gmail.com>
Fri, 28 Jun 2013 15:54:26 +0000 (17:54 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Fri, 28 Jun 2013 16:00:14 +0000 (18:00 +0200)
sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_return_all_columns.xml [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/ActionPlanService.java
sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
sonar-server/src/test/java/org/sonar/server/issue/ActionPlanServiceTest.java
sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java

index 7b93ecfb41f923d3bd0e52918f1b1c918a2240b1..1fa8b6d024c5be5bf92aa77e4566375e5c50d107 100644 (file)
@@ -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
index 42c9b0913403843ef332c23ea244edf1f77d9352..d8ea5863a070d489d569c9fae43de842ac25efad 100644 (file)
@@ -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);
 
index 505f9ce267e76eaccbe70a96d375cfa45c65057f..61ef848f10f1df06d532704c6fd8a94bb693fb50 100644 (file)
   </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 &lt;= #{maxResults}
+    )
+    <if test="maxResults != null">
+      where rownum &lt;= #{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">
index beab090bafddb6c88cd4198c52cd5215e64a14b6..accae5e1b88fb2b8f7c43f24e4802b462f9c5022 100644 (file)
@@ -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>
 
index 035fc1a636a2111adfabeb8065aab18359fba78c..180725f7150f43a98e10f284f9fab7b30bee6fed 100644 (file)
@@ -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 (file)
index 0000000..9a5cc94
--- /dev/null
@@ -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>
index ca929ec205eb04ca380ef0293e2d508069d6c181..5bac54257a563c91244f1d790eef1ef40287b5fb 100644 (file)
@@ -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);
index 2cd7a861fdb9914b50e4bb1e2a5e6978c7c4452d..a36d69709adccd674a1bcfb4257263bb2ea27840 100644 (file)
@@ -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());
index 0f310202f79bde22bcc967931ca5a7b7467813f1..f27f8155f629f2e99141f5b50869017e83395402 100644 (file)
@@ -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
@@ -120,6 +127,23 @@ public class ActionPlanServiceTest {
     verify(authorizationDao).isAuthorizedComponentId(anyLong(), anyInt(), eq(UserRole.ADMIN));
   }
 
+  @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"));
index af2689bc7ce1687060d68414e85b3d1e35a53050..1ed91d8bbbfebf9d816526085ea05f0c7b11850e 100644 (file)
@@ -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);