From: Teryk Bellahsene Date: Tue, 24 Feb 2015 14:32:16 +0000 (+0100) Subject: Allow to set a default assignee on issues - SONAR-6154 X-Git-Tag: 5.1-RC1~51^2~4 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fpull%2F116%2Fhead;p=sonarqube.git Allow to set a default assignee on issues - SONAR-6154 --- diff --git a/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java b/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java index 6c341d54e0e..db97f41b182 100644 --- a/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java +++ b/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java @@ -28,13 +28,8 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; import org.sonar.core.persistence.DbSession; -import org.sonar.core.purge.IdUuidPair; -import org.sonar.core.purge.PurgeConfiguration; -import org.sonar.core.purge.PurgeDao; -import org.sonar.core.purge.PurgeListener; -import org.sonar.core.purge.PurgeProfiler; +import org.sonar.core.purge.*; import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.properties.ProjectSettingsFactory; import javax.annotation.Nullable; @@ -49,33 +44,30 @@ public class ProjectCleaner implements ServerComponent { private final PurgeListener purgeListener; private final PurgeDao purgeDao; private final DefaultPeriodCleaner periodCleaner; - private final ProjectSettingsFactory projectSettingsFactory; private final IssueIndex issueIndex; public ProjectCleaner(PurgeDao purgeDao, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler, PurgeListener purgeListener, - ProjectSettingsFactory projectSettingsFactory, IssueIndex issueIndex) { + IssueIndex issueIndex) { this.purgeDao = purgeDao; this.periodCleaner = periodCleaner; this.profiler = profiler; this.purgeListener = purgeListener; - this.projectSettingsFactory = projectSettingsFactory; this.issueIndex = issueIndex; } - public ProjectCleaner purge(DbSession session, IdUuidPair idUuidPair) { + public ProjectCleaner purge(DbSession session, IdUuidPair idUuidPair, Settings projectSettings) { long start = System.currentTimeMillis(); profiler.reset(); - Settings settings = projectSettingsFactory.newProjectSettings(session, idUuidPair.getId()); - PurgeConfiguration configuration = newDefaultPurgeConfiguration(settings, idUuidPair); + PurgeConfiguration configuration = newDefaultPurgeConfiguration(projectSettings, idUuidPair); - cleanHistoricalData(session, configuration.rootProjectIdUuid().getId(), settings); + cleanHistoricalData(session, configuration.rootProjectIdUuid().getId(), projectSettings); doPurge(session, configuration); deleteIndexedIssuesBefore(idUuidPair.getUuid(), configuration.maxLiveDateOfClosedIssues()); session.commit(); - logProfiling(start, settings); + logProfiling(start, projectSettings); return this; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java index a7709c8e139..45f9efb454a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java @@ -20,16 +20,19 @@ package org.sonar.server.computation; -import com.google.common.base.Preconditions; +import org.sonar.api.config.Settings; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.component.ComponentDto; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.server.computation.step.ParseReportStep; +import static com.google.common.base.Preconditions.checkState; + public class ComputationContext { private final AnalysisReportDto reportDto; private final ComponentDto project; + private Settings projectSettings; /** * Cache of analysis date as it can be accessed several times @@ -50,11 +53,20 @@ public class ComputationContext { } public BatchReport.Metadata getReportMetadata() { - Preconditions.checkState(reportMetadata != null, "Report metadata is available after execution of " + ParseReportStep.class); + checkState(reportMetadata != null, "Report metadata is available after execution of " + ParseReportStep.class); return reportMetadata; } public void setReportMetadata(BatchReport.Metadata m) { this.reportMetadata = m; } + + public Settings getProjectSettings() { + return projectSettings; + } + + public void setProjectSettings(Settings projectSettings) { + checkState(this.projectSettings == null, "can't set project settings twice"); + this.projectSettings = projectSettings; + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java index 77ee77fb272..7d24da7e5f8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java @@ -36,6 +36,7 @@ import org.sonar.server.activity.ActivityService; import org.sonar.server.computation.step.ComputationStep; import org.sonar.server.computation.step.ComputationSteps; import org.sonar.server.db.DbClient; +import org.sonar.server.properties.ProjectSettingsFactory; public class ComputationService implements ServerComponent { @@ -44,11 +45,13 @@ public class ComputationService implements ServerComponent { private final DbClient dbClient; private final ComputationSteps steps; private final ActivityService activityService; + private final ProjectSettingsFactory projectSettingsFactory; - public ComputationService(DbClient dbClient, ComputationSteps steps, ActivityService activityService) { + public ComputationService(DbClient dbClient, ComputationSteps steps, ActivityService activityService, ProjectSettingsFactory projectSettingsFactory) { this.dbClient = dbClient; this.steps = steps; this.activityService = activityService; + this.projectSettingsFactory = projectSettingsFactory; } public void process(AnalysisReportDto report) { @@ -58,6 +61,7 @@ public class ComputationService implements ServerComponent { ComponentDto project = loadProject(report); try { ComputationContext context = new ComputationContext(report, project); + context.setProjectSettings(projectSettingsFactory.newProjectSettings(dbClient.openSession(false), project.getId())); for (ComputationStep step : steps.orderedSteps()) { if (ArrayUtils.contains(step.supportedProjectQualifiers(), context.getProject().qualifier())) { Profiler stepProfiler = Profiler.create(LOG).startInfo(step.getDescription()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java index b503e2c74ac..6e4a6c44349 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java @@ -20,40 +20,54 @@ package org.sonar.server.computation.issue; import com.google.common.collect.Sets; +import org.sonar.api.CoreProperties; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.Duration; import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.rule.RuleDto; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.user.index.UserDoc; +import org.sonar.server.user.index.UserIndex; import org.sonar.server.util.cache.DiskCache; +import javax.annotation.Nullable; + import java.util.Date; public class IssueComputation { + private static final Logger LOG = Loggers.get(IssueComputation.class); + private final RuleCache ruleCache; private final ScmAccountCache scmAccountCache; private final SourceLinesCache linesCache; private final DiskCache.DiskAppender diskIssuesAppender; + private final UserIndex userIndex; + private boolean hasAssigneeBeenComputed = false; + private String defaultAssignee = null; public IssueComputation(RuleCache ruleCache, SourceLinesCache linesCache, ScmAccountCache scmAccountCache, - IssueCache issueCache) { + IssueCache issueCache, UserIndex userIndex) { this.ruleCache = ruleCache; this.linesCache = linesCache; this.scmAccountCache = scmAccountCache; + this.userIndex = userIndex; this.diskIssuesAppender = issueCache.newAppender(); } public void processComponentIssues(ComputationContext context, String componentUuid, Iterable issues) { linesCache.init(componentUuid); + computeDefaultAssignee(context.getProjectSettings().getString(CoreProperties.DEFAULT_ISSUE_ASSIGNEE)); for (BatchReport.Issue reportIssue : issues) { DefaultIssue issue = toDefaultIssue(context, componentUuid, reportIssue); if (issue.isNew()) { guessAuthor(issue); - autoAssign(issue); + autoAssign(issue, defaultAssignee); copyRuleTags(issue); } diskIssuesAppender.append(issue); @@ -110,7 +124,7 @@ public class IssueComputation { } } - private void autoAssign(DefaultIssue issue) { + private void autoAssign(DefaultIssue issue, @Nullable String defaultAssignee) { // issue.assignee() can be not-null if the issue-assign-plugin is // still installed and executed during analysis if (issue.assignee() == null) { @@ -118,6 +132,9 @@ public class IssueComputation { if (scmAccount != null) { issue.setAssignee(scmAccountCache.getNullable(scmAccount)); } + if (issue.assignee() == null && defaultAssignee != null) { + issue.setAssignee(defaultAssignee); + } } } @@ -126,4 +143,17 @@ public class IssueComputation { issue.setTags(Sets.union(rule.getTags(), rule.getSystemTags())); } + private void computeDefaultAssignee(String login) { + if (hasAssigneeBeenComputed) { + return; + } + + hasAssigneeBeenComputed = true; + UserDoc user = userIndex.getNullableByLogin(login); + if (user == null) { + LOG.info("the {} property was set with an unknown login: {}", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, login); + } else { + defaultAssignee = login; + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java index d9fa522e40f..8e9417f4626 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java @@ -47,7 +47,7 @@ public class PurgeDatastoresStep implements ComputationStep { public void execute(ComputationContext context) { DbSession session = dbClient.openSession(true); try { - projectCleaner.purge(session, new IdUuidPair(context.getProject().getId(), context.getProject().uuid())); + projectCleaner.purge(session, new IdUuidPair(context.getProject().getId(), context.getProject().uuid()), context.getProjectSettings()); session.commit(); } finally { MyBatis.closeQuietly(session); diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java b/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java index 13b1897e2e9..f28de356546 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java @@ -227,7 +227,7 @@ public class InternalPermissionService implements ServerComponent { } private Long getTargetedUser(DbSession session, String userLogin) { - UserDto user = dbClient.userDao().selectActiveUserByLogin(userLogin, session); + UserDto user = dbClient.userDao().selectActiveUserByLogin(session, userLogin); badRequestIfNullResult(user, OBJECT_TYPE_USER, userLogin); return user.getId(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java index 0e0a7acb4ac..1b67325466a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java @@ -232,7 +232,7 @@ public class QProfileService implements ServerComponent { String login = profileActivity.login(); if (login != null) { - UserDto user = db.userDao().selectActiveUserByLogin(login, session); + UserDto user = db.userDao().selectActiveUserByLogin(session, login); profileActivity.authorName(user != null ? user.getName() : null); } result.getHits().add(profileActivity); diff --git a/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java b/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java index d72ffb96335..c8fceca3a3d 100644 --- a/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java @@ -55,26 +55,22 @@ public class ProjectCleanerTest { @Before public void before() throws Exception { this.projectSettingsFactory = mock(ProjectSettingsFactory.class); - when(projectSettingsFactory.newProjectSettings(any(DbSession.class), any(Long.class))).thenReturn(settings); - this.sut = new ProjectCleaner(dao, periodCleaner, profiler, purgeListener, projectSettingsFactory, issueIndex); + this.sut = new ProjectCleaner(dao, periodCleaner, profiler, purgeListener, issueIndex); } @Test public void no_profiling_when_property_is_false() throws Exception { settings.setProperty(CoreProperties.PROFILING_LOG_PROPERTY, false); - when(projectSettingsFactory.newProjectSettings(any(DbSession.class), any(Long.class))).thenReturn(settings); - sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); + sut.purge(mock(DbSession.class), mock(IdUuidPair.class), settings); verify(profiler, never()).dump(anyLong(), any(Logger.class)); } @Test public void no_indexing_when_no_issue_to_delete() throws Exception { - when(projectSettingsFactory.newProjectSettings(any(DbSession.class), any(Long.class))).thenReturn(settings); - - sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); + sut.purge(mock(DbSession.class), mock(IdUuidPair.class), settings); verifyZeroInteractions(issueIndex); } @@ -83,7 +79,7 @@ public class ProjectCleanerTest { public void profiling_when_property_is_true() throws Exception { settings.setProperty(CoreProperties.PROFILING_LOG_PROPERTY, true); - sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); + sut.purge(mock(DbSession.class), mock(IdUuidPair.class), settings); verify(profiler).dump(anyLong(), any(Logger.class)); } @@ -92,7 +88,7 @@ public class ProjectCleanerTest { public void call_period_cleaner_index_client_and_purge_dao() throws Exception { settings.setProperty(DbCleanerConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES, 5); - sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); + sut.purge(mock(DbSession.class), mock(IdUuidPair.class), settings); verify(periodCleaner).clean(any(DbSession.class), any(Long.class), any(Settings.class)); verify(dao).purge(any(DbSession.class), any(PurgeConfiguration.class), any(PurgeListener.class)); @@ -103,7 +99,7 @@ public class ProjectCleanerTest { public void if_dao_purge_fails_it_should_not_interrupt_program_execution() throws Exception { doThrow(RuntimeException.class).when(dao).purge(any(DbSession.class), any(PurgeConfiguration.class), any(PurgeListener.class)); - sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); + sut.purge(mock(DbSession.class), mock(IdUuidPair.class), settings); verify(dao).purge(any(DbSession.class), any(PurgeConfiguration.class), any(PurgeListener.class)); } @@ -112,7 +108,7 @@ public class ProjectCleanerTest { public void if_profiler_cleaning_fails_it_should_not_interrupt_program_execution() throws Exception { doThrow(RuntimeException.class).when(periodCleaner).clean(any(DbSession.class), anyLong(), any(Settings.class)); - sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); + sut.purge(mock(DbSession.class), mock(IdUuidPair.class), settings); verify(periodCleaner).clean(any(DbSession.class), anyLong(), any(Settings.class)); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContextTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContextTest.java new file mode 100644 index 00000000000..c9f07e3f1a4 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContextTest.java @@ -0,0 +1,44 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.computation; + +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.computation.db.AnalysisReportDto; + +import static org.mockito.Mockito.mock; + +public class ComputationContextTest { + + ComputationContext sut = new ComputationContext(mock(AnalysisReportDto.class), mock(ComponentDto.class)); + + @Test(expected = IllegalStateException.class) + public void getReportMetadata() throws Exception { + sut.getReportMetadata(); + } + + @Test(expected = IllegalStateException.class) + public void setProjectSettings() throws Exception { + sut.setProjectSettings(mock(Settings.class)); + sut.setProjectSettings(mock(Settings.class)); + } +} \ No newline at end of file diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java index e983b901cef..5518adb9582 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java @@ -23,6 +23,7 @@ import org.apache.commons.lang.RandomStringUtils; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.mockito.Mockito; import org.sonar.api.resources.Qualifiers; import org.sonar.core.activity.Activity; import org.sonar.core.computation.db.AnalysisReportDto; @@ -33,6 +34,7 @@ import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.step.ComputationStep; import org.sonar.server.computation.step.ComputationSteps; import org.sonar.server.db.DbClient; +import org.sonar.server.properties.ProjectSettingsFactory; import java.util.Arrays; @@ -66,7 +68,7 @@ public class ComputationServiceTest { when(steps.orderedSteps()).thenReturn(Arrays.asList(projectStep1, projectStep2, viewStep)); // load report from db and parse it - ComputationService sut = new ComputationService(dbClient, steps, activityService); + ComputationService sut = new ComputationService(dbClient, steps, activityService, mock(ProjectSettingsFactory.class, Mockito.RETURNS_DEEP_STUBS)); AnalysisReportDto report = AnalysisReportDto.newForTests(1L); report.setProjectKey("PROJECT_KEY"); assertThat(report.getStatus()).isNull(); @@ -92,7 +94,7 @@ public class ComputationServiceTest { doThrow(new UnsupportedOperationException()).when(projectStep1).execute(any(ComputationContext.class)); // load report from db and parse it - ComputationService sut = new ComputationService(dbClient, steps, activityService); + ComputationService sut = new ComputationService(dbClient, steps, activityService, mock(ProjectSettingsFactory.class, Mockito.RETURNS_DEEP_STUBS)); AnalysisReportDto report = AnalysisReportDto.newForTests(1L); report.setProjectKey("PROJECT_KEY"); try { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java index 93a8ee42290..fcd90b2c4cd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java @@ -21,18 +21,23 @@ package org.sonar.server.computation.issue; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; +import org.sonar.api.CoreProperties; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.LogTester; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.rule.RuleDto; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.user.index.UserDoc; +import org.sonar.server.user.index.UserIndex; import java.io.IOException; import java.util.Arrays; @@ -47,6 +52,9 @@ public class IssueComputationTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public LogTester logTester = new LogTester(); + IssueComputation sut; // inputs @@ -59,6 +67,8 @@ public class IssueComputationTest { .setRuleRepository(RULE_KEY.repository()) .setRuleKey(RULE_KEY.rule()) .setStatus(Issue.STATUS_OPEN); + ComputationContext context = mock(ComputationContext.class, Mockito.RETURNS_DEEP_STUBS); + UserIndex userIndex = mock(UserIndex.class); // output IssueCache outputIssues; @@ -67,7 +77,12 @@ public class IssueComputationTest { public void setUp() throws IOException { when(ruleCache.get(RULE_KEY)).thenReturn(rule); outputIssues = new IssueCache(temp.newFile(), System2.INSTANCE); - sut = new IssueComputation(ruleCache, lineCache, scmAccountCache, outputIssues); + sut = new IssueComputation(ruleCache, lineCache, scmAccountCache, outputIssues, userIndex); + } + + @After + public void after() throws Exception { + sut.afterReportProcessing(); } @Test @@ -172,8 +187,33 @@ public class IssueComputationTest { verifyZeroInteractions(scmAccountCache); } + @Test + public void assign_default_assignee_when_available() throws Exception { + inputIssue.setIsNew(true); + String wolinski = "wolinski"; + when(context.getProjectSettings().getString(CoreProperties.DEFAULT_ISSUE_ASSIGNEE)).thenReturn(wolinski); + when(userIndex.getNullableByLogin(wolinski)).thenReturn(new UserDoc()); + + process(); + + assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isEqualTo(wolinski); + assertThat(logTester.logs()).doesNotContain(String.format("the %s property was set with an unknown login: %s", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski)); + } + + @Test + public void do_not_assign_default_assignee_when_not_found_in_index() throws Exception { + inputIssue.setIsNew(true); + String wolinski = "wolinski"; + when(context.getProjectSettings().getString(CoreProperties.DEFAULT_ISSUE_ASSIGNEE)).thenReturn(wolinski); + when(userIndex.getNullableByLogin(wolinski)).thenReturn(null); + + process(); + + assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isNull(); + assertThat(logTester.logs()).contains(String.format("the %s property was set with an unknown login: %s", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski)); + } + private void process() { - sut.processComponentIssues(mock(ComputationContext.class, Mockito.RETURNS_DEEP_STUBS), "FILE_A", Arrays.asList(inputIssue.build())); - sut.afterReportProcessing(); + sut.processComponentIssues(context, "FILE_A", Arrays.asList(inputIssue.build())); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java index 2d3c8204029..4470bcc2294 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java @@ -24,6 +24,8 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.sonar.api.config.Settings; import org.sonar.api.utils.ZipUtils; import org.sonar.api.utils.internal.DefaultTempFolder; import org.sonar.batch.protocol.Constants; @@ -49,11 +51,11 @@ import static org.mockito.Mockito.verify; public class ParseReportStepTest { - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - @ClassRule public static DbTester dbTester = new DbTester(); + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + ParseReportStep sut; @Before public void setUp() throws Exception { @@ -63,13 +65,13 @@ public class ParseReportStepTest { @Test public void extract_report_from_db_and_browse_components() throws Exception { AnalysisReportDto reportDto = prepareAnalysisReportInDb(); - - IssueComputation issueComputation = mock(IssueComputation.class); DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new AnalysisReportDao()); - ParseReportStep step = new ParseReportStep(issueComputation, dbClient, new DefaultTempFolder(temp.newFolder())); + sut = new ParseReportStep(issueComputation, dbClient, new DefaultTempFolder(temp.newFolder())); ComputationContext context = new ComputationContext(reportDto, mock(ComponentDto.class)); - step.execute(context); + context.setProjectSettings(mock(Settings.class, Mockito.RETURNS_DEEP_STUBS)); + + sut.execute(context); // verify that all components are processed (currently only for issues) verify(issueComputation).processComponentIssues(context, "PROJECT_UUID", Collections.emptyList()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepMediumTest.java index 004265d841c..0287a364966 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepMediumTest.java @@ -24,6 +24,7 @@ import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.sonar.api.config.Settings; import org.sonar.api.utils.DateUtils; import org.sonar.core.component.ComponentDto; import org.sonar.core.component.SnapshotDto; @@ -33,11 +34,13 @@ import org.sonar.core.computation.dbcleaner.DbCleanerConstants; import org.sonar.core.computation.dbcleaner.ProjectCleaner; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; +import org.sonar.core.properties.PropertiesDao; import org.sonar.core.properties.PropertyDto; import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.SnapshotTesting; import org.sonar.server.computation.ComputationContext; import org.sonar.server.db.DbClient; +import org.sonar.server.properties.ProjectSettingsFactory; import org.sonar.server.tester.ServerTester; import java.util.Date; @@ -95,6 +98,7 @@ public class PurgeDatastoresStepMediumTest { dbClient.propertiesDao().setProperty(new PropertyDto().setKey(DbCleanerConstants.WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS).setValue("52")); dbSession.commit(); ComputationContext context = new ComputationContext(report, project); + context.setProjectSettings(new ProjectSettingsFactory(tester.get(Settings.class), tester.get(PropertiesDao.class)).newProjectSettings(dbSession, project.getId())); // ACT sut.execute(context); @@ -133,6 +137,7 @@ public class PurgeDatastoresStepMediumTest { dbClient.propertiesDao().setProperty(new PropertyDto().setKey(DbCleanerConstants.WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS).setValue("1").setResourceId(project.getId())); dbSession.commit(); ComputationContext context = new ComputationContext(report, project); + context.setProjectSettings(new ProjectSettingsFactory(tester.get(Settings.class), tester.get(PropertiesDao.class)).newProjectSettings(dbSession, project.getId())); // ACT sut.execute(context); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java index a98bb1ddb00..879bf0726da 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java @@ -25,6 +25,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; +import org.sonar.api.config.Settings; import org.sonar.core.component.ComponentDto; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.computation.dbcleaner.ProjectCleaner; @@ -62,6 +63,6 @@ public class PurgeDatastoresStepTest { sut.execute(context); - verify(projectCleaner).purge(any(DbSession.class), any(IdUuidPair.class)); + verify(projectCleaner).purge(any(DbSession.class), any(IdUuidPair.class), any(Settings.class)); } } diff --git a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java index 97977cd2162..dafff170cb8 100644 --- a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java +++ b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java @@ -60,6 +60,16 @@ public class CorePropertyDefinitions { .subCategory(CoreProperties.SUBCATEGORY_LOOKNFEEL) .build(), + // ISSUES + PropertyDefinition.builder(CoreProperties.DEFAULT_ISSUE_ASSIGNEE) + .name("Default Assignee") + .description("Login assigned to a new issue if an assignee has not been found.") + .category(CoreProperties.CATEGORY_GENERAL) + .subCategory(CoreProperties.SUBCATEGORY_ISSUES) + .onQualifiers(Qualifiers.PROJECT) + .type(PropertyType.STRING) + .build(), + // BATCH PropertyDefinition.builder(CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY) @@ -153,7 +163,7 @@ public class CorePropertyDefinitions { .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS_EXCLUSIONS) .multiValues(true) .build() - )); + )); return defs; } } diff --git a/sonar-core/src/main/java/org/sonar/core/user/UserDao.java b/sonar-core/src/main/java/org/sonar/core/user/UserDao.java index 0cff129eb8a..97f7e6a26cd 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/UserDao.java +++ b/sonar-core/src/main/java/org/sonar/core/user/UserDao.java @@ -68,20 +68,21 @@ public class UserDao implements BatchComponent, ServerComponent, DaoComponent { public UserDto selectActiveUserByLogin(String login) { DbSession session = mybatis.openSession(false); try { - return selectActiveUserByLogin(login, session); + return selectActiveUserByLogin(session, login); } finally { MyBatis.closeQuietly(session); } } - public UserDto selectActiveUserByLogin(String login, DbSession session) { + @CheckForNull + public UserDto selectActiveUserByLogin(DbSession session, String login) { UserMapper mapper = session.getMapper(UserMapper.class); return mapper.selectUserByLogin(login); } public List selectUsersByLogins(List logins) { List users = Lists.newArrayList(); - SqlSession session = mybatis.openSession(false); + DbSession session = mybatis.openSession(false); try { users.addAll(selectUsersByLogins(session, logins)); } finally { @@ -90,7 +91,7 @@ public class UserDao implements BatchComponent, ServerComponent, DaoComponent { return users; } - public List selectUsersByLogins(SqlSession session, List logins) { + public List selectUsersByLogins(DbSession session, List logins) { List users = Lists.newArrayList(); if (!logins.isEmpty()) { UserMapper mapper = session.getMapper(UserMapper.class); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 818f275957d..90cde500ab3 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -978,6 +978,7 @@ property.category.general.differentialViews=Differential Views property.category.general.localization=Localization property.category.general.databaseCleaner=Database Cleaner property.category.general.looknfeel=Look & Feel +property.category.general.issues=Issues property.category.security=Security property.category.security.encryption=Encryption property.category.java=Java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index 1df01833ae5..39d28c1b8cc 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -59,6 +59,11 @@ public interface CoreProperties { */ String SUBCATEGORY_LOOKNFEEL = "looknfeel"; + /** + * @since 5.1 + */ + String SUBCATEGORY_ISSUES = "issues"; + /** * @since 4.0 */ @@ -564,4 +569,9 @@ public interface CoreProperties { */ String IMPORT_UNKNOWN_FILES_KEY = "sonar.import_unknown_files"; + /** + * @since 5.1 + */ + String DEFAULT_ISSUE_ASSIGNEE = "sonar.issues.defaultAssigneeLogin"; + }