From 285daf04bf6e791aa4c23797eeb723f276ea82b7 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 18 Jul 2017 21:10:17 +0200 Subject: [PATCH] SONAR-9567 load issues from DB instead of ES in WS batch/issues --- .../java/org/sonar/db/issue/IssueDao.java | 13 + .../java/org/sonar/db/issue/IssueMapper.java | 9 +- .../org/sonar/db/issue/IssueMapper.xml | 15 +- .../java/org/sonar/db/issue/IssueDaoTest.java | 111 ++++- .../org/sonar/server/batch/IssuesAction.java | 79 ++-- .../sonar/server/issue/index/IssueIndex.java | 44 -- .../sonar/server/batch/IssuesActionTest.java | 393 ++++++++---------- .../server/issue/index/IssueIndexTest.java | 128 +----- 8 files changed, 359 insertions(+), 433 deletions(-) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java index 094bd2b694e..94c1c6b072b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java @@ -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 handler) { + mapper(dbSession).selectNonClosedByComponentUuid(componentUuid, handler); + } + + public void scrollNonClosedByModuleOrProject(DbSession dbSession, ComponentDto module, ResultHandler 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); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java index d1102b487af..77355540661 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java @@ -28,8 +28,6 @@ public interface IssueMapper { IssueDto selectByKey(String key); - void selectNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler resultHandler); - Set selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid); List selectByKeys(List keys); @@ -39,4 +37,11 @@ public interface IssueMapper { int update(IssueDto issue); int updateIfBeforeSelectedDate(IssueDto issue); + + void selectNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler handler); + + void scrollNonClosedByModuleOrProject( + @Param("projectUuid") String projectUuid, + @Param("likeModuleUuidPath") String likeModuleUuidPath, + ResultHandler handler); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index 2b7fe7599d8..5211809d2b8 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -158,7 +158,7 @@ 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 <> 'CLOSED' @@ -180,5 +180,18 @@ #{key,jdbcType=VARCHAR} + + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java index 03c8a6317ae..1692b5451b9 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java @@ -19,7 +19,11 @@ */ 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 issues = underTest.selectByKeys(dbTester.getSession(), asList("I1", "I2", "I3")); + List 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 issues = underTest.selectByOrderedKeys(dbTester.getSession(), asList("I1", "I2", "I3")); + Iterable 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 { + private final List list = new ArrayList<>(); + + private void clear() { + list.clear(); + } + + @Override + public void handleResult(ResultContext 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)); + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java index e73f74597c2..d53b87ea481 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java @@ -19,26 +19,28 @@ */ 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 keysByUUid = keysByUUid(session, component); + Map 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 issueDocIterator = issueIndex.selectIssuesForBatch(component); issueDocIterator.hasNext();) { - handleIssue(issueDocIterator.next(), issueBuilder, keysByUUid, response.stream().output()); + ResultHandler 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 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 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 split = MODULE_PATH_SPLITTER.splitToList(issue.getModuleUuidPath()); + return split.get(split.size()-1); + } + private Map keysByUUid(DbSession session, ComponentDto component) { Map keysByUUid = newHashMap(); if (Scopes.PROJECT.equals(component.scope())) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 44d24651123..4c5e3076d34 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -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 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); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java index 8d9ace5f123..746bce1adfb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java @@ -19,304 +19,241 @@ */ 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 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 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 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 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 callStream(String componentKey) { + TestResponse response = tester.newRequest().setParam("key", componentKey).execute(); + return Protobuf.readStream(response.getInputStream(), ServerIssue.parser()); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java index 8417ea9602d..33d6646797c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java @@ -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 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 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 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 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 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 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 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(); -- 2.39.5