@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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' | |||
</select> | |||
@@ -180,5 +180,18 @@ | |||
#{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 <> 'CLOSED' | |||
</select> | |||
</mapper> | |||
@@ -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<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)); | |||
} | |||
} | |||
} |
@@ -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<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())) { |
@@ -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); | |||
} | |||
} |
@@ -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<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()); | |||
} | |||
} |
@@ -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(); |