]> source.dussan.org Git - sonarqube.git/commitdiff
Allow to set a default assignee on issues - SONAR-6154 116/head
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Tue, 24 Feb 2015 14:32:16 +0000 (15:32 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Thu, 26 Feb 2015 11:15:43 +0000 (12:15 +0100)
18 files changed:
server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java
server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java
server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContextTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java
sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
sonar-core/src/main/java/org/sonar/core/user/UserDao.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java

index 6c341d54e0e3f59e018dd0fa93134a850db40b9b..db97f41b18202d18d319f097c728bb464c112853 100644 (file)
@@ -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;
   }
 
index a7709c8e139a71903bd5f720e45e0c38763a103e..45f9efb454aaae3feb14101065e65112f5c39aa4 100644 (file)
 
 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;
+  }
 }
index 77ee77fb2727be93bbc4a7a9a8285e40463814b7..7d24da7e5f8081be7eec2f9912982c42765e75dd 100644 (file)
@@ -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());
index b503e2c74ac0305b0f01df24042f55d51f5fb64a..6e4a6c44349d4356669310fbb6d804f96e4d2090 100644 (file)
 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<DefaultIssue>.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<BatchReport.Issue> 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;
+    }
+  }
 }
index d9fa522e40ff3e9decedeaf6423d00c21a0df17b..8e9417f46269cfc8e33c0c15fa7a1cc8dcde7107 100644 (file)
@@ -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);
index 13b1897e2e9da6c9e7a51959bc5560129e233f09..f28de356546a49761116ad38458353f8334ef6aa 100644 (file)
@@ -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();
   }
index 0e0a7acb4ac46e08a86fea9b6162aa5ae5425a8e..1b67325466a6ca8f286e82e8a8754ae050c0399f 100644 (file)
@@ -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);
index d72ffb963354a2e1636e2f80622afe73c65de3fa..c8fceca3a3d4064fbb3b4c4aa330e867e57217e3 100644 (file)
@@ -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 (file)
index 0000000..c9f07e3
--- /dev/null
@@ -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
index e983b901cef52bf55e7fa1e9614044312854509a..5518adb9582f406b74142bd9fb49e97a29cd7fca 100644 (file)
@@ -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 {
index 93a8ee4229057c8546bfb876a5b5c3b5efdd7a8f..fcd90b2c4cd418af385f7df413d2b32f911bff69 100644 (file)
@@ -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()));
   }
 }
index 2d3c8204029303b98f6b4364a90beb3a06f9d504..4470bcc2294ce2bcbef2eeaba095b56c5430c7d0 100644 (file)
@@ -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.<BatchReport.Issue>emptyList());
index 004265d841c2e0b7c65ff9876bff6dc2e0551a2e..0287a364966c4cddece7606b1182dec2c24316c3 100644 (file)
@@ -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);
index a98bb1ddb0034132ec7eb64c7b42d29ac70d048e..879bf0726da7d67ff2d88ea6be652c4a8d525013 100644 (file)
@@ -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));
   }
 }
index 97977cd2162d25c01c390452d27dadf47aabdb80..dafff170cb8cb133492e13cbc9cfbd69bde95215 100644 (file)
@@ -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;
   }
 }
index 0cff129eb8a6de4a50a702e3d3339dc94f059f39..97f7e6a26cdec5350ef01f28ac48344b9be209e1 100644 (file)
@@ -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<UserDto> selectUsersByLogins(List<String> logins) {
     List<UserDto> 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<UserDto> selectUsersByLogins(SqlSession session, List<String> logins) {
+  public List<UserDto> selectUsersByLogins(DbSession session, List<String> logins) {
     List<UserDto> users = Lists.newArrayList();
     if (!logins.isEmpty()) {
       UserMapper mapper = session.getMapper(UserMapper.class);
index 818f275957de0051e6d15cf41cc1afef090d6ab3..90cde500ab3832d1157297fdfc529e4482f28374 100644 (file)
@@ -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
index 1df01833ae5974d9b942b97362292939a3da21d4..39d28c1b8cc8904f09eaa2418b36d82ea378466b 100644 (file)
@@ -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";
+
 }