]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9567 load issues from DB instead of ES in WS batch/issues
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 18 Jul 2017 19:10:17 +0000 (21:10 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 19 Jul 2017 18:47:45 +0000 (20:47 +0200)
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java

index 094bd2b694e07c619e7afc120b78283d9b938988..94c1c6b072bf6cb3c806850792c01ff355533949 100644 (file)
@@ -28,11 +28,15 @@ import java.util.Map;
 import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import org.apache.ibatis.session.ResultHandler;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 import org.sonar.db.RowNotFoundException;
+import org.sonar.db.WildcardPosition;
+import org.sonar.db.component.ComponentDto;
 
 import static com.google.common.collect.FluentIterable.from;
+import static org.sonar.db.DaoDatabaseUtils.buildLikeValue;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 
 public class IssueDao implements Dao {
@@ -90,6 +94,15 @@ public class IssueDao implements Dao {
     return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid);
   }
 
+  public void scrollNonClosedByComponentUuid(DbSession dbSession, String componentUuid, ResultHandler<IssueDto> handler) {
+    mapper(dbSession).selectNonClosedByComponentUuid(componentUuid, handler);
+  }
+
+  public void scrollNonClosedByModuleOrProject(DbSession dbSession, ComponentDto module, ResultHandler<IssueDto> handler) {
+    String likeModuleUuidPath = buildLikeValue(module.moduleUuidPath(), WildcardPosition.AFTER);
+    mapper(dbSession).scrollNonClosedByModuleOrProject(module.projectUuid(), likeModuleUuidPath, handler);
+  }
+
   public void insert(DbSession session, IssueDto dto) {
     mapper(session).insert(dto);
   }
index d1102b487af5e95ca4b3ea053c01c71d411fecfe..77355540661f173e74a4b1c0e03fdbb017713506 100644 (file)
@@ -28,8 +28,6 @@ public interface IssueMapper {
 
   IssueDto selectByKey(String key);
 
-  void selectNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> resultHandler);
-
   Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid);
 
   List<IssueDto> selectByKeys(List<String> keys);
@@ -39,4 +37,11 @@ public interface IssueMapper {
   int update(IssueDto issue);
 
   int updateIfBeforeSelectedDate(IssueDto issue);
+
+  void selectNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler);
+
+  void scrollNonClosedByModuleOrProject(
+    @Param("projectUuid") String projectUuid,
+    @Param("likeModuleUuidPath") String likeModuleUuidPath,
+    ResultHandler<IssueDto> handler);
 }
index 2b7fe7599d845008977b8e536ef00eb127db8bea..5211809d2b856bb38870e74fddfdbee556a6713a 100644 (file)
     inner join projects p on p.uuid=i.component_uuid
     inner join projects root on root.uuid=i.project_uuid
     where
-    i.component_uuid=#{componentUuid,jdbcType=VARCHAR} and
+    i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
     i.status &lt;&gt; 'CLOSED'
   </select>
 
       #{key,jdbcType=VARCHAR}
     </foreach>
   </select>
+
+  <select id="scrollNonClosedByModuleOrProject" parameterType="map" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+    select
+    <include refid="issueColumns"/>
+    from issues i
+    inner join rules r on r.id = i.rule_id
+    inner join projects p on p.uuid = i.component_uuid
+    inner join projects root on root.uuid = i.project_uuid
+    where
+    i.project_uuid = #{projectUuid, jdbcType=VARCHAR} and
+    p.module_uuid_path like  #{likeModuleUuidPath, jdbcType=VARCHAR} escape '/' and
+    i.status &lt;&gt; 'CLOSED'
+  </select>
 </mapper>
 
index 03c8a6317aedd5c1c5083f3fa6db9d71168aba5e..1692b5451b914146694beb527d38106ed6d13f95 100644 (file)
  */
 package org.sonar.db.issue;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -30,11 +34,14 @@ import org.sonar.db.RowNotFoundException;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleTesting;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
 
 public class IssueDaoTest {
 
@@ -47,17 +54,17 @@ public class IssueDaoTest {
   private static final String ISSUE_KEY2 = "I2";
 
   @Rule
-  public ExpectedException thrown = ExpectedException.none();
+  public ExpectedException expectedException = ExpectedException.none();
   @Rule
-  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+  public DbTester db = DbTester.create(System2.INSTANCE);
 
-  private IssueDao underTest = dbTester.getDbClient().issueDao();
+  private IssueDao underTest = db.getDbClient().issueDao();
 
   @Test
   public void selectByKeyOrFail() {
     prepareTables();
 
-    IssueDto issue = underTest.selectOrFailByKey(dbTester.getSession(), ISSUE_KEY1);
+    IssueDto issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
     assertThat(issue.getKee()).isEqualTo(ISSUE_KEY1);
     assertThat(issue.getId()).isGreaterThan(0L);
     assertThat(issue.getComponentUuid()).isEqualTo(FILE_UUID);
@@ -92,12 +99,12 @@ public class IssueDaoTest {
 
   @Test
   public void selectByKeyOrFail_fails_if_key_not_found() {
-    thrown.expect(RowNotFoundException.class);
-    thrown.expectMessage("Issue with key 'DOES_NOT_EXIST' does not exist");
+    expectedException.expect(RowNotFoundException.class);
+    expectedException.expectMessage("Issue with key 'DOES_NOT_EXIST' does not exist");
 
     prepareTables();
 
-    underTest.selectOrFailByKey(dbTester.getSession(), "DOES_NOT_EXIST");
+    underTest.selectOrFailByKey(db.getSession(), "DOES_NOT_EXIST");
   }
 
   @Test
@@ -105,7 +112,7 @@ public class IssueDaoTest {
     // contains I1 and I2
     prepareTables();
 
-    List<IssueDto> issues = underTest.selectByKeys(dbTester.getSession(), asList("I1", "I2", "I3"));
+    List<IssueDto> issues = underTest.selectByKeys(db.getSession(), asList("I1", "I2", "I3"));
     // results are not ordered, so do not use "containsExactly"
     assertThat(issues).extracting("key").containsOnly("I1", "I2");
   }
@@ -115,13 +122,64 @@ public class IssueDaoTest {
     // contains I1 and I2
     prepareTables();
 
-    Iterable<IssueDto> issues = underTest.selectByOrderedKeys(dbTester.getSession(), asList("I1", "I2", "I3"));
+    Iterable<IssueDto> issues = underTest.selectByOrderedKeys(db.getSession(), asList("I1", "I2", "I3"));
     assertThat(issues).extracting("key").containsExactly("I1", "I2");
 
-    issues = underTest.selectByOrderedKeys(dbTester.getSession(), asList("I2", "I3", "I1"));
+    issues = underTest.selectByOrderedKeys(db.getSession(), asList("I2", "I3", "I1"));
     assertThat(issues).extracting("key").containsExactly("I2", "I1");
   }
 
+  @Test
+  public void scrollNonClosedByComponentUuid() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto file = db.components().insertComponent(newFileDto(project));
+    IssueDto openIssue1OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null));
+    IssueDto openIssue2OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null));
+    IssueDto closedIssueOnFile = db.issues().insert(rule, project, file, i -> i.setStatus("CLOSED").setResolution("FIXED"));
+    IssueDto openIssueOnProject = db.issues().insert(rule, project, project, i -> i.setStatus("OPEN").setResolution(null));
+
+    Accumulator accumulator = new Accumulator();
+    underTest.scrollNonClosedByComponentUuid(db.getSession(), file.uuid(), accumulator);
+    accumulator.assertThatContainsOnly(openIssue1OnFile, openIssue2OnFile);
+
+    accumulator.clear();
+    underTest.scrollNonClosedByComponentUuid(db.getSession(), project.uuid(), accumulator);
+    accumulator.assertThatContainsOnly(openIssueOnProject);
+
+    accumulator.clear();
+    underTest.scrollNonClosedByComponentUuid(db.getSession(), "does_not_exist", accumulator);
+    assertThat(accumulator.list).isEmpty();
+  }
+
+  @Test
+  public void scrollNonClosedByModuleOrProject() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto anotherProject = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project));
+    ComponentDto file = db.components().insertComponent(newFileDto(module));
+    IssueDto openIssue1OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null));
+    IssueDto openIssue2OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null));
+    IssueDto closedIssueOnFile = db.issues().insert(rule, project, file, i -> i.setStatus("CLOSED").setResolution("FIXED"));
+    IssueDto openIssueOnModule = db.issues().insert(rule, project, module, i -> i.setStatus("OPEN").setResolution(null));
+    IssueDto openIssueOnProject = db.issues().insert(rule, project, project, i -> i.setStatus("OPEN").setResolution(null));
+    IssueDto openIssueOnAnotherProject = db.issues().insert(rule, anotherProject, anotherProject, i -> i.setStatus("OPEN").setResolution(null));
+
+    Accumulator accumulator = new Accumulator();
+    underTest.scrollNonClosedByModuleOrProject(db.getSession(), project, accumulator);
+    accumulator.assertThatContainsOnly(openIssue1OnFile, openIssue2OnFile, openIssueOnModule, openIssueOnProject);
+
+    accumulator.clear();
+    underTest.scrollNonClosedByModuleOrProject(db.getSession(), module, accumulator);
+    accumulator.assertThatContainsOnly(openIssue1OnFile, openIssue2OnFile, openIssueOnModule);
+
+    accumulator.clear();
+    ComponentDto notPersisted = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization());
+    underTest.scrollNonClosedByModuleOrProject(db.getSession(), notPersisted, accumulator);
+    assertThat(accumulator.list).isEmpty();
+  }
+
   private static IssueDto newIssueDto(String key) {
     IssueDto dto = new IssueDto();
     dto.setComponent(new ComponentDto().setKey("struts:Action").setId(123L).setUuid("component-uuid"));
@@ -149,19 +207,38 @@ public class IssueDaoTest {
   }
 
   private void prepareTables() {
-    dbTester.rules().insertRule(RULE);
-    OrganizationDto organizationDto = dbTester.organizations().insert();
-    ComponentDto projectDto = dbTester.components().insertPrivateProject(organizationDto, (t) -> t.setUuid(PROJECT_UUID).setKey(PROJECT_KEY));
-    dbTester.components().insertComponent(ComponentTesting.newFileDto(projectDto).setUuid(FILE_UUID).setKey(FILE_KEY));
-    underTest.insert(dbTester.getSession(), newIssueDto(ISSUE_KEY1)
+    db.rules().insertRule(RULE);
+    OrganizationDto organizationDto = db.organizations().insert();
+    ComponentDto projectDto = db.components().insertPrivateProject(organizationDto, (t) -> t.setUuid(PROJECT_UUID).setKey(PROJECT_KEY));
+    db.components().insertComponent(newFileDto(projectDto).setUuid(FILE_UUID).setKey(FILE_KEY));
+    underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY1)
       .setMessage("the message")
       .setRuleId(RULE.getId())
       .setComponentUuid(FILE_UUID)
       .setProjectUuid(PROJECT_UUID));
-    underTest.insert(dbTester.getSession(), newIssueDto(ISSUE_KEY2)
+    underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY2)
       .setRuleId(RULE.getId())
       .setComponentUuid(FILE_UUID)
       .setProjectUuid(PROJECT_UUID));
-    dbTester.getSession().commit();
+    db.getSession().commit();
+  }
+
+  private static class Accumulator implements ResultHandler<IssueDto> {
+    private final List<IssueDto> list = new ArrayList<>();
+
+    private void clear() {
+      list.clear();
+    }
+
+    @Override
+    public void handleResult(ResultContext<? extends IssueDto> resultContext) {
+      list.add(resultContext.getResultObject());
+    }
+
+    private void assertThatContainsOnly(IssueDto... issues) {
+      assertThat(list)
+        .extracting(IssueDto::getKey)
+        .containsExactlyInAnyOrder(Arrays.stream(issues).map(IssueDto::getKey).toArray(String[]::new));
+    }
   }
 }
index e73f74597c2f72508be6404d5da1d7e9cdce190c..d53b87ea481e89eb79db7e603f3d4e03678d10ae 100644 (file)
  */
 package org.sonar.server.batch;
 
+import com.google.common.base.Splitter;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import org.apache.ibatis.session.ResultHandler;
 import org.sonar.api.resources.Scopes;
+import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDto;
 import org.sonar.scanner.protocol.input.ScannerInput;
 import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.issue.index.IssueDoc;
-import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.MediaTypes;
 
 import static com.google.common.collect.Maps.newHashMap;
+import static java.lang.String.format;
 import static org.sonar.api.web.UserRole.USER;
 import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
@@ -46,15 +48,14 @@ import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 public class IssuesAction implements BatchWsAction {
 
   private static final String PARAM_KEY = "key";
+  private static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings();
 
   private final DbClient dbClient;
-  private final IssueIndex issueIndex;
   private final UserSession userSession;
   private final ComponentFinder componentFinder;
 
-  public IssuesAction(DbClient dbClient, IssueIndex issueIndex, UserSession userSession, ComponentFinder componentFinder) {
+  public IssuesAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
     this.dbClient = dbClient;
-    this.issueIndex = issueIndex;
     this.userSession = userSession;
     this.componentFinder = componentFinder;
   }
@@ -77,38 +78,53 @@ public class IssuesAction implements BatchWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    response.stream().setMediaType(MediaTypes.PROTOBUF);
-
-    try (DbSession session = dbClient.openSession(false)) {
+    try (DbSession dbSession = dbClient.openSession(false)) {
       String componentKey = request.mandatoryParam(PARAM_KEY);
-      ComponentDto component = componentFinder.getByKey(session, componentKey);
+      ComponentDto component = componentFinder.getByKey(dbSession, componentKey);
       userSession.checkComponentPermission(USER, component);
 
-      Map<String, String> keysByUUid = keysByUUid(session, component);
+      Map<String, String> keysByUUid = keysByUUid(dbSession, component);
+
+      ScannerInput.ServerIssue.Builder responseBuilder = ScannerInput.ServerIssue.newBuilder();
+      response.stream().setMediaType(MediaTypes.PROTOBUF);
+      OutputStream output = response.stream().output();
 
-      ScannerInput.ServerIssue.Builder issueBuilder = ScannerInput.ServerIssue.newBuilder();
-      for (Iterator<IssueDoc> issueDocIterator = issueIndex.selectIssuesForBatch(component); issueDocIterator.hasNext();) {
-        handleIssue(issueDocIterator.next(), issueBuilder, keysByUUid, response.stream().output());
+      ResultHandler<IssueDto> handler = resultContext -> {
+        IssueDto issue = resultContext.getResultObject();
+        handleIssue(issue, responseBuilder, keysByUUid, output);
+      };
+      switch (component.scope()) {
+        case Scopes.PROJECT:
+          dbClient.issueDao().scrollNonClosedByModuleOrProject(dbSession, component, handler);
+          break;
+        case Scopes.FILE:
+          dbClient.issueDao().scrollNonClosedByComponentUuid(dbSession, component.uuid(), handler);
+          break;
+        default:
+          // only projects, modules and files are supported. Other types of components are not allowed.
+          throw new IllegalStateException(format("Component of scope '%s' is not allowed", component.scope()));
       }
     }
   }
 
-  private static void handleIssue(IssueDoc issue, ScannerInput.ServerIssue.Builder issueBuilder, Map<String, String> keysByUUid, OutputStream out) {
-    issueBuilder.setKey(issue.key());
-    issueBuilder.setModuleKey(keysByUUid.get(issue.moduleUuid()));
-    setNullable(issue.filePath(), issueBuilder::setPath);
-    issueBuilder.setRuleRepository(issue.ruleKey().repository());
-    issueBuilder.setRuleKey(issue.ruleKey().rule());
-    setNullable(issue.checksum(), issueBuilder::setChecksum);
-    setNullable(issue.assignee(), issueBuilder::setAssigneeLogin);
-    setNullable(issue.line(), issueBuilder::setLine);
-    setNullable(issue.message(), issueBuilder::setMsg);
-    issueBuilder.setSeverity(org.sonar.scanner.protocol.Constants.Severity.valueOf(issue.severity()));
+  private static void handleIssue(IssueDto issue, ScannerInput.ServerIssue.Builder issueBuilder,
+                                  Map<String, String> keysByUUid, OutputStream out) {
+    issueBuilder.setKey(issue.getKey());
+    String moduleUuid = extractModuleUuid(issue);
+    issueBuilder.setModuleKey(keysByUUid.get(moduleUuid));
+    setNullable(issue.getFilePath(), issueBuilder::setPath);
+    issueBuilder.setRuleRepository(issue.getRuleRepo());
+    issueBuilder.setRuleKey(issue.getRule());
+    setNullable(issue.getChecksum(), issueBuilder::setChecksum);
+    setNullable(issue.getAssignee(), issueBuilder::setAssigneeLogin);
+    setNullable(issue.getLine(), issueBuilder::setLine);
+    setNullable(issue.getMessage(), issueBuilder::setMsg);
+    issueBuilder.setSeverity(org.sonar.scanner.protocol.Constants.Severity.valueOf(issue.getSeverity()));
     issueBuilder.setManualSeverity(issue.isManualSeverity());
-    issueBuilder.setStatus(issue.status());
-    setNullable(issue.resolution(), issueBuilder::setResolution);
-    issueBuilder.setType(issue.type().name());
-    issueBuilder.setCreationDate(issue.creationDate().getTime());
+    issueBuilder.setStatus(issue.getStatus());
+    setNullable(issue.getResolution(), issueBuilder::setResolution);
+    issueBuilder.setType(RuleType.valueOf(issue.getType()).name());
+    issueBuilder.setCreationDate(issue.getIssueCreationTime());
     try {
       issueBuilder.build().writeDelimitedTo(out);
     } catch (IOException e) {
@@ -117,6 +133,11 @@ public class IssuesAction implements BatchWsAction {
     issueBuilder.clear();
   }
 
+  private static String extractModuleUuid(IssueDto issue) {
+    List<String> split = MODULE_PATH_SPLITTER.splitToList(issue.getModuleUuidPath());
+    return split.get(split.size()-1);
+  }
+
   private Map<String, String> keysByUUid(DbSession session, ComponentDto component) {
     Map<String, String> keysByUUid = newHashMap();
     if (Scopes.PROJECT.equals(component.scope())) {
index 44d24651123c5252fe14c1d62b9b038306f1cace..4c5e3076d34bbf76779e0926e7a19405e07a31e5 100644 (file)
@@ -26,7 +26,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -39,8 +38,6 @@ import org.apache.commons.lang.BooleanUtils;
 import org.apache.commons.lang.StringUtils;
 import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.action.search.SearchType;
-import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -54,12 +51,9 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
 import org.elasticsearch.search.aggregations.metrics.min.Min;
 import org.elasticsearch.search.aggregations.metrics.sum.SumBuilder;
 import org.joda.time.Duration;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.resources.Scopes;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
 import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.EsUtils;
@@ -644,42 +638,4 @@ public class IssueIndex {
     }
     return boolQuery;
   }
-
-  /**
-   * Return non closed issues for a given project, module, or file. Other kind of components are not allowed.
-   * Only fields needed for the batch are returned.
-   */
-  public Iterator<IssueDoc> selectIssuesForBatch(ComponentDto component) {
-    BoolQueryBuilder filter = boolQuery()
-      .must(createAuthorizationFilter(true))
-      .mustNot(termsQuery(IssueIndexDefinition.FIELD_ISSUE_STATUS, Issue.STATUS_CLOSED));
-
-    switch (component.scope()) {
-      case Scopes.PROJECT:
-        filter.must(termsQuery(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, component.uuid()));
-        break;
-      case Scopes.FILE:
-        filter.must(termsQuery(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, component.uuid()));
-        break;
-      default:
-        throw new IllegalStateException(format("Component of scope '%s' is not allowed", component.scope()));
-    }
-
-    SearchRequestBuilder requestBuilder = client
-      .prepareSearch(INDEX_TYPE_ISSUE)
-      .setSearchType(SearchType.SCAN)
-      .setScroll(TimeValue.timeValueMinutes(EsUtils.SCROLL_TIME_IN_MINUTES))
-      .setSize(10_000)
-      .setFetchSource(
-        new String[] {IssueIndexDefinition.FIELD_ISSUE_KEY, IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID,
-          IssueIndexDefinition.FIELD_ISSUE_FILE_PATH, IssueIndexDefinition.FIELD_ISSUE_SEVERITY, IssueIndexDefinition.FIELD_ISSUE_MANUAL_SEVERITY,
-          IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, IssueIndexDefinition.FIELD_ISSUE_STATUS, IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE,
-          IssueIndexDefinition.FIELD_ISSUE_LINE, IssueIndexDefinition.FIELD_ISSUE_MESSAGE, IssueIndexDefinition.FIELD_ISSUE_CHECKSUM,
-          IssueIndexDefinition.FIELD_ISSUE_TYPE, IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT},
-        null)
-      .setQuery(boolQuery().must(matchAllQuery()).filter(filter));
-    SearchResponse response = requestBuilder.get();
-
-    return EsUtils.scroll(client, response.getScrollId(), IssueDoc::new);
-  }
 }
index 8d9ace5f123513a13fd5e06ba3a15059c4fd7ffd..746bce1adfb70eba6d5c2436b83403af570541b6 100644 (file)
  */
 package org.sonar.server.batch;
 
-import com.google.common.base.Throwables;
 import java.io.IOException;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.core.util.Protobuf;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.issue.IssueDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.scanner.protocol.Constants.Severity;
 import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
 import org.sonar.server.component.TestComponentFinder;
-import org.sonar.server.es.EsTester;
 import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.issue.index.IssueIndexDefinition;
-import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.issue.index.IssueIteratorFactory;
-import org.sonar.server.permission.index.AuthorizationTypeSupport;
-import org.sonar.server.permission.index.PermissionIndexerTester;
+import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.db.rule.RuleTesting.newRule;
 
 public class IssuesActionTest {
 
-  private static final String PROJECT_KEY = "struts";
-  private static final String PROJECT_UUID = "ABCD";
-  private static final String MODULE_KEY = "struts-core";
-  private static final String MODULE_UUID = "BCDE";
-  private final static String FILE_KEY = "Action.java";
-  private static final String FILE_UUID = "CDEF";
-
   private System2 system2 = System2.INSTANCE;
 
   @Rule
-  public ExpectedException thrown = ExpectedException.none();
+  public ExpectedException expectedException = ExpectedException.none();
   @Rule
   public DbTester db = DbTester.create(system2);
   @Rule
-  public EsTester es = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
-  @Rule
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
 
-  private static RuleDefinitionDto RULE_DEFINITION = newRule(RuleKey.of("squid", "AvoidCycle"));
-
-  private IssueIndexer issueIndexer = new IssueIndexer(es.client(), new IssueIteratorFactory(db.getDbClient()));
-  private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, issueIndexer);
-  private WsActionTester tester = new WsActionTester(new IssuesAction(db.getDbClient(),
-    new IssueIndex(es.client(), system2, userSessionRule, new AuthorizationTypeSupport(userSessionRule)),
-    userSessionRule, TestComponentFinder.from(db)));
+  private WsActionTester tester = new WsActionTester(new IssuesAction(db.getDbClient(), userSessionRule, TestComponentFinder.from(db)));
 
   @Test
-  public void return_minimal_fields() throws Exception {
-    ComponentDto project = db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setKey(PROJECT_KEY));
-    ComponentDto module = db.components().insertComponent(newModuleDto(MODULE_UUID, project).setKey(MODULE_KEY));
-    ComponentDto file = db.components().insertComponent(newFileDto(module, null, FILE_UUID).setKey(FILE_KEY).setPath(null));
-    db.rules().insert(RULE_DEFINITION);
-    db.issues().insert(RULE_DEFINITION, project, file, issue -> issue
-      .setKee("EFGH")
-      .setSeverity("BLOCKER")
-      .setStatus("RESOLVED")
-      .setType(BUG)
-      .setResolution(null)
-      .setManualSeverity(false)
-      .setMessage(null)
-      .setLine(null)
-      .setChecksum(null)
-      .setAssignee(null));
-    indexIssues(project);
-    addBrowsePermissionOnComponent(project);
-
-    ServerIssue serverIssue = call(PROJECT_KEY);
-
-    assertThat(serverIssue.getKey()).isEqualTo("EFGH");
-    assertThat(serverIssue.getModuleKey()).isEqualTo(MODULE_KEY);
+  public void test_nullable_fields() throws Exception {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project));
+    ComponentDto file = db.components().insertComponent(newFileDto(module, null).setPath(null));
+    IssueDto issue = db.issues().insert(rule, project, file, i ->
+      i.setSeverity("BLOCKER")
+        // non-null fields
+        .setStatus("OPEN")
+        .setType(BUG)
+        .setManualSeverity(true)
+
+        // all the nullable fields
+        .setResolution(null)
+        .setMessage(null)
+        .setLine(null)
+        .setChecksum(null)
+        .setAssignee(null));
+    addPermissionTo(project);
+
+    ServerIssue serverIssue = call(project.key());
+
+    assertThat(serverIssue.getKey()).isEqualTo(issue.getKey());
+    assertThat(serverIssue.getModuleKey()).isEqualTo(module.getKey());
+    assertThat(serverIssue.getRuleRepository()).isEqualTo(rule.getRepositoryKey());
+    assertThat(serverIssue.getRuleKey()).isEqualTo(rule.getRuleKey());
+    assertThat(serverIssue.getStatus()).isEqualTo("OPEN");
+    assertThat(serverIssue.getSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(serverIssue.getType()).isEqualTo(BUG.name());
+    assertThat(serverIssue.getManualSeverity()).isTrue();
+
     assertThat(serverIssue.hasPath()).isFalse();
-    assertThat(serverIssue.getRuleRepository()).isEqualTo("squid");
-    assertThat(serverIssue.getRuleKey()).isEqualTo("AvoidCycle");
     assertThat(serverIssue.hasLine()).isFalse();
     assertThat(serverIssue.hasMsg()).isFalse();
     assertThat(serverIssue.hasResolution()).isFalse();
-    assertThat(serverIssue.getStatus()).isEqualTo("RESOLVED");
-    assertThat(serverIssue.getSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(serverIssue.getType()).isEqualTo(BUG.name());
-    assertThat(serverIssue.getManualSeverity()).isFalse();
     assertThat(serverIssue.hasChecksum()).isFalse();
     assertThat(serverIssue.hasAssigneeLogin()).isFalse();
   }
 
   @Test
-  public void issues_from_project() throws Exception {
-    OrganizationDto organizationDto = db.organizations().insert();
-    ComponentDto project = db.components().insertComponent(newPrivateProjectDto(organizationDto, PROJECT_UUID).setKey(PROJECT_KEY));
-    ComponentDto module = db.components().insertComponent(newModuleDto(MODULE_UUID, project).setKey(MODULE_KEY));
-    ComponentDto file = db.components().insertComponent(newFileDto(module, null, FILE_UUID).setKey(FILE_KEY).setPath("src/org/struts/Action.java"));
-    db.rules().insert(RULE_DEFINITION);
-    db.issues().insert(RULE_DEFINITION, project, file, issue -> issue
-      .setKee("EFGH")
-      .setSeverity("BLOCKER")
-      .setStatus("RESOLVED")
-      .setResolution("FALSE-POSITIVE")
-      .setManualSeverity(false)
-      .setMessage("Do not use this method")
-      .setLine(200)
-      .setChecksum("123456")
-      .setAssignee("john"));
-    indexIssues(project);
-    addBrowsePermissionOnComponent(project);
-
-    ServerIssue serverIssue = call(PROJECT_KEY);
-
-    assertThat(serverIssue.getKey()).isEqualTo("EFGH");
-    assertThat(serverIssue.getModuleKey()).isEqualTo(MODULE_KEY);
-    assertThat(serverIssue.getPath()).isEqualTo("src/org/struts/Action.java");
-    assertThat(serverIssue.getRuleRepository()).isEqualTo("squid");
-    assertThat(serverIssue.getRuleKey()).isEqualTo("AvoidCycle");
-    assertThat(serverIssue.getLine()).isEqualTo(200);
-    assertThat(serverIssue.getMsg()).isEqualTo("Do not use this method");
-    assertThat(serverIssue.getResolution()).isEqualTo("FALSE-POSITIVE");
-    assertThat(serverIssue.getStatus()).isEqualTo("RESOLVED");
+  public void test_fields_with_non_null_values() throws Exception {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project));
+    ComponentDto file = db.components().insertComponent(newFileDto(module, null));
+    IssueDto issue = db.issues().insert(rule, project, file, i ->
+      i.setSeverity("BLOCKER")
+        .setStatus("OPEN")
+        .setType(BUG)
+        .setManualSeverity(true)
+        .setResolution("FIXED")
+        .setMessage("the message")
+        .setLine(10)
+        .setChecksum("ABC")
+        .setAssignee("foo"));
+    addPermissionTo(project);
+
+    ServerIssue serverIssue = call(project.key());
+
+    assertThat(serverIssue.getKey()).isEqualTo(issue.getKey());
+    assertThat(serverIssue.getModuleKey()).isEqualTo(module.getKey());
+    assertThat(serverIssue.getRuleRepository()).isEqualTo(rule.getRepositoryKey());
+    assertThat(serverIssue.getRuleKey()).isEqualTo(rule.getRuleKey());
+    assertThat(serverIssue.getStatus()).isEqualTo("OPEN");
     assertThat(serverIssue.getSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(serverIssue.getManualSeverity()).isFalse();
-    assertThat(serverIssue.getChecksum()).isEqualTo("123456");
-    assertThat(serverIssue.getAssigneeLogin()).isEqualTo("john");
+    assertThat(serverIssue.getType()).isEqualTo(BUG.name());
+    assertThat(serverIssue.getManualSeverity()).isTrue();
+    assertThat(serverIssue.getPath()).isEqualTo(file.path());
+    assertThat(serverIssue.getLine()).isEqualTo(issue.getLine());
+    assertThat(serverIssue.getMsg()).isEqualTo(issue.getMessage());
+    assertThat(serverIssue.getResolution()).isEqualTo(issue.getResolution());
+    assertThat(serverIssue.getChecksum()).isEqualTo(issue.getChecksum());
+    assertThat(serverIssue.getAssigneeLogin()).isEqualTo(issue.getAssignee());
   }
 
   @Test
-  public void issues_from_module() throws Exception {
-    ComponentDto project = db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setKey(PROJECT_KEY));
-    ComponentDto module = db.components().insertComponent(newModuleDto(MODULE_UUID, project).setKey(MODULE_KEY));
-    ComponentDto file = db.components().insertComponent(newFileDto(module, null, FILE_UUID).setKey(FILE_KEY).setPath("src/org/struts/Action.java"));
-    db.rules().insert(RULE_DEFINITION);
-    db.issues().insert(RULE_DEFINITION, project, file, issue -> issue
-      .setKee("EFGH")
-      .setSeverity("BLOCKER")
-      .setStatus("RESOLVED")
-      .setResolution("FALSE-POSITIVE")
-      .setManualSeverity(false)
-      .setMessage("Do not use this method")
-      .setLine(200)
-      .setChecksum("123456")
-      .setAssignee("john"));
-    indexIssues(project);
-    addBrowsePermissionOnComponent(project);
-
-    ServerIssue serverIssue = call(PROJECT_KEY);
-
-    assertThat(serverIssue.getKey()).isEqualTo("EFGH");
-    assertThat(serverIssue.getModuleKey()).isEqualTo(MODULE_KEY);
-    assertThat(serverIssue.getPath()).isEqualTo("src/org/struts/Action.java");
-    assertThat(serverIssue.getRuleRepository()).isEqualTo("squid");
-    assertThat(serverIssue.getRuleKey()).isEqualTo("AvoidCycle");
-    assertThat(serverIssue.getLine()).isEqualTo(200);
-    assertThat(serverIssue.getMsg()).isEqualTo("Do not use this method");
-    assertThat(serverIssue.getResolution()).isEqualTo("FALSE-POSITIVE");
-    assertThat(serverIssue.getStatus()).isEqualTo("RESOLVED");
-    assertThat(serverIssue.getSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(serverIssue.getManualSeverity()).isFalse();
-    assertThat(serverIssue.getChecksum()).isEqualTo("123456");
-    assertThat(serverIssue.getAssigneeLogin()).isEqualTo("john");
+  public void return_issues_of_project() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project));
+    ComponentDto file = db.components().insertComponent(newFileDto(module, null));
+    IssueDto issueOnFile = db.issues().insert(rule, project, file, i -> i.setKee("ON_FILE"));
+    IssueDto issueOnModule = db.issues().insert(rule, project, module, i -> i.setKee("ON_MODULE"));
+    IssueDto issueOnProject = db.issues().insert(rule, project, project, i -> i.setKee("ON_PROJECT"));
+
+    addPermissionTo(project);
+    try (CloseableIterator<ServerIssue> result = callStream(project.key())) {
+      assertThat(result)
+        .extracting(ServerIssue::getKey, ServerIssue::getModuleKey)
+        .containsExactlyInAnyOrder(
+          tuple(issueOnFile.getKey(), module.key()),
+          tuple(issueOnModule.getKey(), module.key()),
+          tuple(issueOnProject.getKey(), project.key()));
+    }
   }
 
   @Test
-  public void issues_from_file() throws Exception {
-    ComponentDto project = db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setKey(PROJECT_KEY));
-    ComponentDto module = db.components().insertComponent(newModuleDto(MODULE_UUID, project).setKey(MODULE_KEY));
-    ComponentDto file = db.components().insertComponent(newFileDto(module, null, FILE_UUID).setKey(FILE_KEY).setPath("src/org/struts/Action.java"));
-    db.rules().insert(RULE_DEFINITION);
-    db.issues().insert(RULE_DEFINITION, project, file, issue -> issue
-      .setKee("EFGH")
-      .setSeverity("BLOCKER")
-      .setStatus("RESOLVED")
-      .setResolution("FALSE-POSITIVE")
-      .setManualSeverity(false)
-      .setMessage("Do not use this method")
-      .setLine(200)
-      .setChecksum("123456")
-      .setAssignee("john"));
-    indexIssues(project);
-    addBrowsePermissionOnComponent(project);
-
-    ServerIssue serverIssue = call(FILE_KEY);
-
-    assertThat(serverIssue.getKey()).isEqualTo("EFGH");
-    assertThat(serverIssue.getModuleKey()).isEqualTo(MODULE_KEY);
-    assertThat(serverIssue.getPath()).isEqualTo("src/org/struts/Action.java");
-    assertThat(serverIssue.getRuleRepository()).isEqualTo("squid");
-    assertThat(serverIssue.getRuleKey()).isEqualTo("AvoidCycle");
-    assertThat(serverIssue.getLine()).isEqualTo(200);
-    assertThat(serverIssue.getMsg()).isEqualTo("Do not use this method");
-    assertThat(serverIssue.getResolution()).isEqualTo("FALSE-POSITIVE");
-    assertThat(serverIssue.getStatus()).isEqualTo("RESOLVED");
-    assertThat(serverIssue.getSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(serverIssue.getManualSeverity()).isFalse();
-    assertThat(serverIssue.getChecksum()).isEqualTo("123456");
-    assertThat(serverIssue.getAssigneeLogin()).isEqualTo("john");
+  public void return_issues_of_module() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project));
+    ComponentDto file = db.components().insertComponent(newFileDto(module, null));
+    IssueDto issueOnFile = db.issues().insert(rule, project, file, i -> i.setKee("ON_FILE"));
+    IssueDto issueOnModule = db.issues().insert(rule, project, module, i -> i.setKee("ON_MODULE"));
+    IssueDto issueOnProject = db.issues().insert(rule, project, project, i -> i.setKee("ON_PROJECT"));
+
+    addPermissionTo(project);
+    try (CloseableIterator<ServerIssue> result = callStream(module.key())) {
+      assertThat(result)
+        .extracting(ServerIssue::getKey, ServerIssue::getModuleKey)
+        .containsExactlyInAnyOrder(
+          tuple(issueOnFile.getKey(), module.key()),
+          tuple(issueOnModule.getKey(), module.key()));
+    }
   }
 
   @Test
-  public void issues_attached_on_module() throws Exception {
-    OrganizationDto organizationDto = db.organizations().insert();
-    ComponentDto project = db.components().insertComponent(newPrivateProjectDto(organizationDto, PROJECT_UUID).setKey(PROJECT_KEY));
-    ComponentDto module = db.components().insertComponent(newModuleDto(MODULE_UUID, project).setKey(MODULE_KEY));
-    db.rules().insert(RULE_DEFINITION);
-    db.issues().insert(RULE_DEFINITION, project, module, issue -> issue
-      .setKee("EFGH")
-      .setSeverity("BLOCKER")
-      .setStatus("RESOLVED")
-      .setResolution("FALSE-POSITIVE")
-      .setManualSeverity(false)
-      .setMessage("Do not use this method")
-      .setLine(200)
-      .setChecksum("123456")
-      .setAssignee("john"));
-    indexIssues(project);
-    addBrowsePermissionOnComponent(project);
-
-    ServerIssue serverIssue = call(MODULE_KEY);
-
-    assertThat(serverIssue.getKey()).isEqualTo("EFGH");
-    assertThat(serverIssue.getModuleKey()).isEqualTo(MODULE_KEY);
-    assertThat(serverIssue.hasPath()).isFalse();
-    assertThat(serverIssue.getRuleRepository()).isEqualTo("squid");
-    assertThat(serverIssue.getRuleKey()).isEqualTo("AvoidCycle");
-    assertThat(serverIssue.getLine()).isEqualTo(200);
-    assertThat(serverIssue.getMsg()).isEqualTo("Do not use this method");
-    assertThat(serverIssue.getResolution()).isEqualTo("FALSE-POSITIVE");
-    assertThat(serverIssue.getStatus()).isEqualTo("RESOLVED");
-    assertThat(serverIssue.getSeverity()).isEqualTo(Severity.BLOCKER);
-    assertThat(serverIssue.getManualSeverity()).isFalse();
-    assertThat(serverIssue.getChecksum()).isEqualTo("123456");
-    assertThat(serverIssue.getAssigneeLogin()).isEqualTo("john");
+  public void return_issues_of_file() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project));
+    ComponentDto file = db.components().insertComponent(newFileDto(module, null));
+    IssueDto issueOnFile = db.issues().insert(rule, project, file);
+    IssueDto issueOnModule = db.issues().insert(rule, project, module);
+    IssueDto issueOnProject = db.issues().insert(rule, project, project);
+
+    addPermissionTo(project);
+    try (CloseableIterator<ServerIssue> result = callStream(file.key())) {
+      assertThat(result)
+        .extracting(ServerIssue::getKey, ServerIssue::getModuleKey)
+        .containsExactlyInAnyOrder(
+          tuple(issueOnFile.getKey(), module.key()));
+    }
   }
 
   @Test
-  public void project_issues_attached_file_on_removed_module() throws Exception {
-    ComponentDto project = db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setKey(PROJECT_KEY));
-    // File and module are removed
-    ComponentDto module = db.components().insertComponent(newModuleDto(MODULE_UUID, project).setKey(MODULE_KEY).setEnabled(false));
-    ComponentDto file = db.components().insertComponent(newFileDto(module, null, FILE_UUID).setKey(FILE_KEY).setPath("src/org/struts/Action.java").setEnabled(false));
-    db.rules().insert(RULE_DEFINITION);
-    db.issues().insert(RULE_DEFINITION, project, file, issue -> issue
-      .setKee("EFGH")
-      .setSeverity("BLOCKER")
-      .setStatus("RESOLVED")
-      .setResolution("FALSE-POSITIVE")
-      .setManualSeverity(false)
-      .setMessage("Do not use this method")
-      .setLine(200)
-      .setChecksum("123456")
-      .setAssignee("john"));
-    indexIssues(project);
-    addBrowsePermissionOnComponent(project);
-
-    ServerIssue serverIssue = call(PROJECT_KEY);
-
-    assertThat(serverIssue.getKey()).isEqualTo("EFGH");
-    // Module key of removed file should be returned
-    assertThat(serverIssue.getModuleKey()).isEqualTo(MODULE_KEY);
+  public void fail_if_requested_component_is_a_directory() throws IOException {
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto directory = db.components().insertComponent(newDirectory(project, "src/main/java"));
+    addPermissionTo(project);
+
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Component of scope 'DIR' is not allowed");
+
+    call(directory.key());
   }
 
   @Test
-  public void fail_without_browse_permission_on_file() throws Exception {
+  public void issues_on_disabled_modules_are_returned() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(newModuleDto(project).setEnabled(false));
+    ComponentDto file = db.components().insertComponent(newFileDto(module, null).setEnabled(false));
+    IssueDto issue = db.issues().insert(rule, project, file);
+
+    addPermissionTo(project);
+    try (CloseableIterator<ServerIssue> result = callStream(project.key())) {
+      // Module key of removed file should be returned
+      assertThat(result)
+        .extracting(ServerIssue::getKey, ServerIssue::getModuleKey)
+        .containsExactly(tuple(issue.getKey(), module.key()));
+    }
+  }
+
+  @Test
+  public void fail_if_user_does_not_have_permission_on_project() {
     ComponentDto project = db.components().insertPrivateProject();
     ComponentDto file = db.components().insertComponent(newFileDto(project));
 
-    thrown.expect(ForbiddenException.class);
+    expectedException.expect(ForbiddenException.class);
 
     tester.newRequest().setParam("key", file.key()).execute();
   }
 
-  private void indexIssues(ComponentDto project) {
-    issueIndexer.indexOnStartup(null);
-    authorizationIndexerTester.allowOnlyAnyone(project);
+  @Test
+  public void fail_if_component_does_not_exist() {
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Component key 'does_not_exist' not found");
+
+    tester.newRequest().setParam("key", "does_not_exist").execute();
   }
 
-  private void addBrowsePermissionOnComponent(ComponentDto project) {
+  private void addPermissionTo(ComponentDto project) {
     userSessionRule.addProjectPermission(UserRole.USER, project);
   }
 
-  private ServerIssue call(String componentKey) {
-    try {
-      TestResponse response = tester.newRequest().setParam("key", componentKey).execute();
-      return ServerIssue.parseDelimitedFrom(response.getInputStream());
-    } catch (IOException e) {
-      throw Throwables.propagate(e);
-    }
+  private ServerIssue call(String componentKey) throws IOException {
+    TestResponse response = tester.newRequest().setParam("key", componentKey).execute();
+    return ServerIssue.parseDelimitedFrom(response.getInputStream());
+  }
+
+  private CloseableIterator<ServerIssue> callStream(String componentKey) {
+    TestResponse response = tester.newRequest().setParam("key", componentKey).execute();
+    return Protobuf.readStream(response.getInputStream(), ServerIssue.parser());
   }
 }
index 8417ea9602d1d833c7250d1262126787b2086f64..33d6646797cd50849b097822db02089f255f59c8 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.issue.index;
 
 import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -32,7 +31,6 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.resources.Scopes;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.Duration;
@@ -63,10 +61,8 @@ import static com.google.common.collect.Lists.newArrayList;
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.entry;
-import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.sonar.api.rules.RuleType.BUG;
 import static org.sonar.api.utils.DateUtils.parseDate;
 import static org.sonar.api.utils.DateUtils.parseDateTime;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
@@ -187,22 +183,22 @@ public class IssueIndexTest {
     assertThat(
       underTest.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).moduleUuids(newArrayList(file.uuid())).build(), new SearchOptions())
         .getDocs())
-          .isEmpty();
+      .isEmpty();
     assertThat(
       underTest.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).moduleUuids(newArrayList(module.uuid())).build(), new SearchOptions())
         .getDocs())
-          .hasSize(1);
+      .hasSize(1);
     assertThat(
       underTest.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).moduleUuids(newArrayList(subModule.uuid())).build(), new SearchOptions())
         .getDocs())
-          .hasSize(2);
+      .hasSize(2);
     assertThat(
       underTest.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).moduleUuids(newArrayList(project.uuid())).build(), new SearchOptions())
         .getDocs())
-          .isEmpty();
+      .isEmpty();
     assertThat(
       underTest.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).moduleUuids(newArrayList("unknown")).build(), new SearchOptions()).getDocs())
-        .isEmpty();
+      .isEmpty();
   }
 
   @Test
@@ -419,7 +415,7 @@ public class IssueIndexTest {
     assertThat(
       underTest.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE, Issue.RESOLUTION_FIXED)).build(), new SearchOptions())
         .getDocs())
-          .hasSize(2);
+      .hasSize(2);
     assertThat(underTest.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE)).build(), new SearchOptions()).getDocs()).hasSize(1);
     assertThat(underTest.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_REMOVED)).build(), new SearchOptions()).getDocs()).isEmpty();
   }
@@ -751,8 +747,8 @@ public class IssueIndexTest {
     SearchOptions SearchOptions = fixtureForCreatedAtFacet();
 
     Map<String, Long> createdAt = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
-      .createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(),
+        .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
+        .createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(),
       SearchOptions).getFacets().get("createdAt");
     assertThat(createdAt).containsOnly(
       entry("2014-08-25T01:00:00+0000", 0L),
@@ -767,8 +763,8 @@ public class IssueIndexTest {
     SearchOptions SearchOptions = fixtureForCreatedAtFacet();
 
     Map<String, Long> createdAt = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
-      .createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(),
+        .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
+        .createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(),
       SearchOptions).getFacets().get("createdAt");
     assertThat(createdAt).containsOnly(
       entry("2014-08-01T01:00:00+0000", 0L),
@@ -784,8 +780,8 @@ public class IssueIndexTest {
     SearchOptions SearchOptions = fixtureForCreatedAtFacet();
 
     Map<String, Long> createdAt = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2011-01-01T00:00:00+0100"))
-      .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
+        .createdAfter(parseDateTime("2011-01-01T00:00:00+0100"))
+        .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
       SearchOptions).getFacets().get("createdAt");
     assertThat(createdAt).containsOnly(
       entry("2010-01-01T01:00:00+0000", 0L),
@@ -802,8 +798,8 @@ public class IssueIndexTest {
     SearchOptions SearchOptions = fixtureForCreatedAtFacet();
 
     Map<String, Long> createdAt = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2014-09-01T00:00:00-0100"))
-      .createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(),
+        .createdAfter(parseDateTime("2014-09-01T00:00:00-0100"))
+        .createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(),
       SearchOptions).getFacets().get("createdAt");
     assertThat(createdAt).containsOnly(
       entry("2014-09-01T01:00:00+0000", 2L));
@@ -833,7 +829,7 @@ public class IssueIndexTest {
     SearchOptions SearchOptions = fixtureForCreatedAtFacet();
 
     Map<String, Long> createdAt = underTest.search(IssueQuery.builder()
-      .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
+        .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
       SearchOptions).getFacets().get("createdAt");
     assertThat(createdAt).containsOnly(
       entry("2011-01-01T01:00:00+0000", 1L),
@@ -901,7 +897,7 @@ public class IssueIndexTest {
       String key = "ISSUE" + i;
       issues.add(newDoc(key, file));
     }
-    indexIssues(issues.toArray(new IssueDoc[] {}));
+    indexIssues(issues.toArray(new IssueDoc[]{}));
 
     IssueQuery.Builder query = IssueQuery.builder();
     SearchResult<IssueDoc> result = underTest.search(query.build(), new SearchOptions().setLimit(Integer.MAX_VALUE));
@@ -1200,98 +1196,6 @@ public class IssueIndexTest {
     assertThat(underTest.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(1);
   }
 
-  @Test
-  public void search_issues_for_batch_return_needed_fields() {
-    ComponentDto project = newPrivateProjectDto(newOrganizationDto(), "PROJECT");
-    ComponentDto file = newFileDto(project, null).setPath("src/File.xoo");
-
-    IssueDoc issue = newDoc("ISSUE", file)
-      .setRuleKey("squid:S001")
-      .setChecksum("12345")
-      .setAssignee("john")
-      .setLine(11)
-      .setMessage("the message")
-      .setSeverity(Severity.BLOCKER)
-      .setManualSeverity(true)
-      .setStatus(Issue.STATUS_RESOLVED)
-      .setResolution(Issue.RESOLUTION_FIXED)
-      .setType(BUG)
-      .setFuncCreationDate(new Date());
-    indexIssues(issue);
-
-    List<IssueDoc> issues = Lists.newArrayList(underTest.selectIssuesForBatch(file));
-    assertThat(issues).hasSize(1);
-    IssueDoc result = issues.get(0);
-    assertThat(result.key()).isEqualTo("ISSUE");
-    assertThat(result.moduleUuid()).isEqualTo("PROJECT");
-    assertThat(result.filePath()).isEqualTo("src/File.xoo");
-    assertThat(result.ruleKey()).isEqualTo(RuleKey.of("squid", "S001"));
-    assertThat(result.checksum()).isEqualTo("12345");
-    assertThat(result.assignee()).isEqualTo("john");
-    assertThat(result.line()).isEqualTo(11);
-    assertThat(result.message()).isEqualTo("the message");
-    assertThat(result.severity()).isEqualTo(Severity.BLOCKER);
-    assertThat(result.isManualSeverity()).isTrue();
-    assertThat(result.status()).isEqualTo(Issue.STATUS_RESOLVED);
-    assertThat(result.resolution()).isEqualTo(Issue.RESOLUTION_FIXED);
-    assertThat(result.type()).isEqualTo(BUG);
-    assertThat(result.creationDate()).isNotNull();
-  }
-
-  @Test
-  public void search_issues_for_batch() {
-    ComponentDto project = ComponentTesting.newPrivateProjectDto(newOrganizationDto());
-    ComponentDto module = ComponentTesting.newModuleDto(project);
-    ComponentDto subModule = ComponentTesting.newModuleDto(module);
-    ComponentDto file = newFileDto(subModule, null);
-
-    indexIssues(
-      newDoc("ISSUE3", module),
-      newDoc("ISSUE5", subModule),
-      newDoc("ISSUE2", file),
-      // Close Issue, should never be returned
-      newDoc("CLOSE_ISSUE", file).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED));
-
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(project))).hasSize(3);
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(module))).hasSize(3);
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(subModule))).hasSize(2);
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(file))).hasSize(1);
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(ComponentTesting.newPrivateProjectDto(newOrganizationDto())))).isEmpty();
-  }
-
-  @Test
-  public void fail_to_search_issues_for_batch_on_not_allowed_scope() {
-    try {
-      underTest.selectIssuesForBatch(new ComponentDto().setScope(Scopes.DIRECTORY));
-      failBecauseExceptionWasNotThrown(IllegalStateException.class);
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("Component of scope 'DIR' is not allowed");
-    }
-  }
-
-  @Test
-  public void search_issues_for_batch_return_only_authorized_issues() {
-    OrganizationDto org = newOrganizationDto();
-    ComponentDto project1 = ComponentTesting.newPrivateProjectDto(org);
-    ComponentDto project2 = ComponentTesting.newPrivateProjectDto(org);
-    ComponentDto file1 = newFileDto(project1, null);
-    ComponentDto file2 = newFileDto(project2, null);
-    GroupDto allowedGroup = newGroupDto();
-    GroupDto otherGroup = newGroupDto();
-
-    // project1 can be seen by allowedGroup
-    indexIssue(newDoc("ISSUE1", file1));
-    authorizationIndexerTester.allowOnlyGroup(project1, allowedGroup);
-    // project3 can be seen by nobody
-    indexIssue(newDoc("ISSUE3", file2));
-
-    userSessionRule.logIn().setGroups(allowedGroup);
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(project1))).hasSize(1);
-
-    userSessionRule.logIn().setGroups(otherGroup);
-    assertThat(Lists.newArrayList(underTest.selectIssuesForBatch(project2))).isEmpty();
-  }
-
   @Test
   public void list_tags() {
     RuleDefinitionDto r1 = dbTester.rules().insert();