]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11753 move "New Issues" notification to email specific algo
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 25 Mar 2019 09:24:56 +0000 (10:24 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 23 Apr 2019 08:37:52 +0000 (10:37 +0200)
14 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStep.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStepTest.java
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java [deleted file]
server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationHandler.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java
server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java [deleted file]
server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationHandlerTest.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
server/sonar-server/src/main/java/org/sonar/server/notification/ws/DispatchersImpl.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/test/java/org/sonar/server/notification/ws/DispatchersImplTest.java

index dee1f1bb3731dda2bfbfacb99afa202203213b81..02c2e2a7f9e3a3516304dbb2ca88077ca8db71eb 100644 (file)
@@ -57,6 +57,7 @@ import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
 import org.sonar.server.issue.notification.NewIssuesStatistics;
 import org.sonar.server.notification.NotificationService;
 
+import static java.util.Collections.singleton;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toMap;
 import static java.util.stream.StreamSupport.stream;
@@ -101,9 +102,9 @@ public class SendIssueNotificationsStep implements ComputationStep {
     Component project = treeRootHolder.getRoot();
     NotificationStatistics notificationStatistics = new NotificationStatistics();
     // FIXME do we still need this fail fast?
-//    if (service.hasProjectSubscribersForTypes(project.getUuid(), NOTIF_TYPES)) {
-      doExecute(notificationStatistics, project);
-//    }
+    // if (service.hasProjectSubscribersForTypes(project.getUuid(), NOTIF_TYPES)) {
+    doExecute(notificationStatistics, project);
+    // }
     notificationStatistics.dumpTo(context);
   }
 
@@ -171,8 +172,11 @@ public class SendIssueNotificationsStep implements ComputationStep {
       .setAnalysisDate(new Date(analysisDate))
       .setStatistics(project.getName(), globalStatistics)
       .setDebt(Duration.create(globalStatistics.effort().getOnLeak()));
-    notificationStatistics.newIssuesDeliveries += service.deliver(notification);
+    notificationStatistics.newIssuesDeliveries += service.deliverEmails(singleton(notification));
     notificationStatistics.newIssues++;
+
+    // compatibility with old API
+    notificationStatistics.newIssuesDeliveries += service.deliver(notification);
   }
 
   private void sendMyNewIssuesNotification(NewIssuesStatistics statistics, Component project, long analysisDate, NotificationStatistics notificationStatistics) {
index 37caed258329ec360103f76982a075908101e5b3..f17efdeb44246882c0e4276c73478661249c5c1e 100644 (file)
@@ -286,9 +286,10 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
     TestComputationStepContext context = new TestComputationStepContext();
     underTest.execute(context);
 
-    verify(notificationService).deliver(newIssuesNotificationMock);
+    verify(notificationService).deliverEmails(ImmutableSet.of(newIssuesNotificationMock));
     verify(notificationService).deliverEmails(ImmutableSet.of(myNewIssuesNotificationMock));
     // old API compatibility call
+    verify(notificationService).deliver(newIssuesNotificationMock);
     verify(notificationService).deliver(myNewIssuesNotificationMock);
     verify(myNewIssuesNotificationMock).setAssignee(any(UserDto.class));
     verify(myNewIssuesNotificationMock).setProject(PROJECT.getKey(), PROJECT.getName(), null, null);
index f0d236a35fc455594c381709af05a045b0b7d462..a09aaff37b5591c78ed7923f53791d16cf5e72ee 100644 (file)
@@ -105,8 +105,8 @@ import org.sonar.server.issue.notification.IssueChangesEmailTemplate;
 import org.sonar.server.issue.notification.MyNewIssuesEmailTemplate;
 import org.sonar.server.issue.notification.MyNewIssuesNotificationHandler;
 import org.sonar.server.issue.notification.NewIssuesEmailTemplate;
-import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
 import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
+import org.sonar.server.issue.notification.NewIssuesNotificationHandler;
 import org.sonar.server.issue.workflow.FunctionExecutor;
 import org.sonar.server.issue.workflow.IssueWorkflow;
 import org.sonar.server.l18n.ServerI18n;
@@ -406,8 +406,8 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
       IssueChangesEmailTemplate.class,
       ChangesOnMyIssueNotificationDispatcher.class,
       ChangesOnMyIssueNotificationDispatcher.newMetadata(),
-      NewIssuesNotificationDispatcher.class,
-      NewIssuesNotificationDispatcher.newMetadata(),
+      NewIssuesNotificationHandler.class,
+      NewIssuesNotificationHandler.newMetadata(),
       MyNewIssuesNotificationHandler.class,
       MyNewIssuesNotificationHandler.newMetadata(),
       DoNotFixNotificationDispatcher.class,
index a1e11bafa111ec9d67e34ea4fbc0914a0bfc1a0e..54d0d1aeb25e15717100930a99f308c89117db53 100644 (file)
@@ -44,11 +44,6 @@ public class MyNewIssuesNotification extends NewIssuesNotification {
     return this;
   }
 
-  @CheckForNull
-  public String getProjectKey() {
-    return getFieldValue("projectKey");
-  }
-
   @CheckForNull
   public String getAssignee() {
     return getFieldValue(FIELD_ASSIGNEE);
index b50ca9ca2a1d9b6011b233ee1cfb45de04f4bec8..28eb3f673b3d9d42311728bf23a09b42c16cbe0d 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.ToIntFunction;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.notifications.Notification;
 import org.sonar.api.rule.RuleKey;
@@ -92,6 +93,11 @@ public class NewIssuesNotification extends Notification {
     return this;
   }
 
+  @CheckForNull
+  public String getProjectKey() {
+    return getFieldValue(FIELD_PROJECT_KEY);
+  }
+
   public NewIssuesNotification setProjectVersion(@Nullable String version) {
     if (version != null) {
       setFieldValue(FIELD_PROJECT_VERSION, version);
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java
deleted file mode 100644 (file)
index d71ce53..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.issue.notification;
-
-import com.google.common.collect.Multimap;
-import java.util.Collection;
-import java.util.Map;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.server.notification.NotificationDispatcher;
-import org.sonar.server.notification.NotificationDispatcherMetadata;
-import org.sonar.server.notification.NotificationManager;
-
-import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER;
-
-/**
- * This dispatcher means: "notify me when new issues are introduced during project analysis"
- */
-public class NewIssuesNotificationDispatcher extends NotificationDispatcher {
-
-  public static final String KEY = "NewIssues";
-  private final NotificationManager manager;
-
-  public NewIssuesNotificationDispatcher(NotificationManager manager) {
-    super(NewIssuesNotification.TYPE);
-    this.manager = manager;
-  }
-
-  @Override
-  public String getKey() {
-    return KEY;
-  }
-
-  public static NotificationDispatcherMetadata newMetadata() {
-    return NotificationDispatcherMetadata.create(KEY)
-      .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, String.valueOf(true))
-      .setProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION, String.valueOf(true));
-  }
-
-  @Override
-  public void dispatch(Notification notification, Context context) {
-    String projectKey = notification.getFieldValue("projectKey");
-    Multimap<String, NotificationChannel> subscribedRecipients = manager
-      .findSubscribedRecipientsForDispatcher(this, projectKey, ALL_MUST_HAVE_ROLE_USER);
-
-    for (Map.Entry<String, Collection<NotificationChannel>> channelsByRecipients : subscribedRecipients.asMap().entrySet()) {
-      String userLogin = channelsByRecipients.getKey();
-      for (NotificationChannel channel : channelsByRecipients.getValue()) {
-        context.addUser(userLogin, channel);
-      }
-    }
-  }
-
-}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationHandler.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationHandler.java
new file mode 100644 (file)
index 0000000..0dd5ee1
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.issue.notification;
+
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Stream;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationHandler;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.notification.email.EmailNotificationChannel;
+import org.sonar.server.notification.email.EmailNotificationChannel.EmailDeliveryRequest;
+
+import static org.sonar.core.util.stream.MoreCollectors.index;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
+import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER;
+
+public class NewIssuesNotificationHandler implements NotificationHandler<NewIssuesNotification> {
+
+  public static final String KEY = "NewIssues";
+
+  private final NotificationManager notificationManager;
+  private final EmailNotificationChannel emailNotificationChannel;
+
+  public NewIssuesNotificationHandler(NotificationManager notificationManager,  EmailNotificationChannel emailNotificationChannel) {
+    this.notificationManager = notificationManager;
+    this.emailNotificationChannel = emailNotificationChannel;
+  }
+
+  public static NotificationDispatcherMetadata newMetadata() {
+    return NotificationDispatcherMetadata.create(KEY)
+      .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, String.valueOf(true))
+      .setProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION, String.valueOf(true));
+  }
+
+  @Override
+  public Class<NewIssuesNotification> getNotificationClass() {
+    return NewIssuesNotification.class;
+  }
+
+  @Override
+  public int deliver(Collection<NewIssuesNotification> notifications) {
+    if (notifications.isEmpty() || !emailNotificationChannel.isActivated()) {
+      return 0;
+    }
+
+    Multimap<String, NewIssuesNotification> notificationsByProjectKey = notifications.stream()
+      .filter(t -> t.getProjectKey() != null)
+      .collect(index(NewIssuesNotification::getProjectKey));
+    if (notificationsByProjectKey.isEmpty()) {
+      return 0;
+    }
+
+    Set<EmailDeliveryRequest> deliveryRequests = notificationsByProjectKey.asMap().entrySet()
+      .stream()
+      .flatMap(e -> toEmailDeliveryRequests(e.getKey(), e.getValue()))
+      .collect(toSet(notifications.size()));
+    if (deliveryRequests.isEmpty()) {
+      return 0;
+    }
+    return emailNotificationChannel.deliver(deliveryRequests);
+  }
+
+  private Stream<? extends EmailDeliveryRequest> toEmailDeliveryRequests(String projectKey, Collection<NewIssuesNotification> notifications) {
+    return notificationManager.findSubscribedEmailRecipients(KEY, projectKey, ALL_MUST_HAVE_ROLE_USER)
+      .stream()
+      .flatMap(emailRecipient -> notifications.stream()
+        .map(notification -> new EmailDeliveryRequest(emailRecipient.getEmail(), notification)));
+  }
+
+}
index 7669b6805e693db04377870a4e7a3ca7a103e951..de3eb27e3af2e0f1997724737a012d023720818e 100644 (file)
@@ -25,7 +25,6 @@ import org.sonar.db.DbClient;
 import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserTesting;
 
-import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_ASSIGNEE;
@@ -50,19 +49,4 @@ public class MyNewIssuesNotificationTest {
     assertThat(underTest.getType()).isEqualTo(MyNewIssuesNotification.MY_NEW_ISSUES_NOTIF_TYPE);
   }
 
-  @Test
-  public void getProjectKey_returns_null_if_setProject_has_no_been_called() {
-    assertThat(underTest.getProjectKey()).isNull();
-  }
-
-  @Test
-  public void getProjectKey_returns_projectKey_if_setProject_has_been_called() {
-    String projectKey = randomAlphabetic(5);
-    String projectName = randomAlphabetic(6);
-    String branchName = randomAlphabetic(7);
-    String pullRequest = randomAlphabetic(8);
-    underTest.setProject(projectKey, projectName, branchName, pullRequest);
-
-    assertThat(underTest.getProjectKey()).isEqualTo(projectKey);
-  }
 }
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java
deleted file mode 100644 (file)
index 182d593..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.issue.notification;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.web.UserRole;
-import org.sonar.server.notification.NotificationDispatcher;
-import org.sonar.server.notification.NotificationManager;
-
-import static org.mockito.Mockito.*;
-
-public class NewIssuesNotificationDispatcherTest {
-
-  private NotificationManager notifications = mock(NotificationManager.class);
-  private NotificationDispatcher.Context context = mock(NotificationDispatcher.Context.class);
-  private NotificationChannel emailChannel = mock(NotificationChannel.class);
-  private NotificationChannel twitterChannel = mock(NotificationChannel.class);
-  private NewIssuesNotificationDispatcher dispatcher = mock(NewIssuesNotificationDispatcher.class);
-
-  @Before
-  public void setUp() {
-    dispatcher = new NewIssuesNotificationDispatcher(notifications);
-  }
-
-  @Test
-  public void shouldNotDispatchIfNotNewViolationsNotification() {
-    Notification notification = new Notification("other-notif");
-    dispatcher.performDispatch(notification, context);
-
-    verify(context, never()).addUser(any(String.class), any(NotificationChannel.class));
-  }
-
-  @Test
-  public void shouldDispatchToUsersWhoHaveSubscribedAndFlaggedProjectAsFavourite() {
-    Multimap<String, NotificationChannel> recipients = HashMultimap.create();
-    recipients.put("user1", emailChannel);
-    recipients.put("user2", twitterChannel);
-    when(notifications.findSubscribedRecipientsForDispatcher(dispatcher, "struts", new NotificationManager.SubscriberPermissionsOnProject(UserRole.USER))).thenReturn(recipients);
-
-    Notification notification = new Notification(NewIssuesNotification.TYPE)
-      .setFieldValue("projectKey", "struts");
-    dispatcher.performDispatch(notification, context);
-
-    verify(context).addUser("user1", emailChannel);
-    verify(context).addUser("user2", twitterChannel);
-    verifyNoMoreInteractions(context);
-  }
-}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationHandlerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationHandlerTest.java
new file mode 100644 (file)
index 0000000..c6eb895
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.issue.notification;
+
+import java.util.Collections;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.notification.NotificationManager.EmailRecipient;
+import org.sonar.server.notification.email.EmailNotificationChannel;
+import org.sonar.server.notification.email.EmailNotificationChannel.EmailDeliveryRequest;
+
+import static java.util.Collections.emptySet;
+import static java.util.stream.Collectors.toSet;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
+import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;
+import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER;
+
+public class NewIssuesNotificationHandlerTest {
+  private static final String NEW_ISSUES_DISPATCHER_KEY = "NewIssues";
+  private NotificationManager notificationManager = mock(NotificationManager.class);
+  private EmailNotificationChannel emailNotificationChannel = mock(EmailNotificationChannel.class);
+  private NewIssuesNotificationHandler underTest = new NewIssuesNotificationHandler(notificationManager, emailNotificationChannel);
+
+  @Test
+  public void verify_myNewIssues_notification_dispatcher_key() {
+    NotificationDispatcherMetadata metadata = NewIssuesNotificationHandler.newMetadata();
+
+    assertThat(metadata.getDispatcherKey()).isEqualTo(NEW_ISSUES_DISPATCHER_KEY);
+  }
+
+  @Test
+  public void myNewIssues_notification_is_enable_at_global_level() {
+    NotificationDispatcherMetadata metadata = NewIssuesNotificationHandler.newMetadata();
+
+    assertThat(metadata.getProperty(GLOBAL_NOTIFICATION)).isEqualTo("true");
+  }
+
+  @Test
+  public void myNewIssues_notification_is_enable_at_project_level() {
+    NotificationDispatcherMetadata metadata = NewIssuesNotificationHandler.newMetadata();
+
+    assertThat(metadata.getProperty(PER_PROJECT_NOTIFICATION)).isEqualTo("true");
+  }
+
+  @Test
+  public void getNotificationClass_is_NewIssuesNotification() {
+    assertThat(underTest.getNotificationClass()).isEqualTo(NewIssuesNotification.class);
+  }
+
+  @Test
+  public void deliver_has_no_effect_if_notifications_is_empty() {
+    when(emailNotificationChannel.isActivated()).thenReturn(true);
+    int deliver = underTest.deliver(Collections.emptyList());
+
+    assertThat(deliver).isZero();
+    verifyZeroInteractions(notificationManager, emailNotificationChannel);
+  }
+
+  @Test
+  public void deliver_has_no_effect_if_emailNotificationChannel_is_disabled() {
+    when(emailNotificationChannel.isActivated()).thenReturn(false);
+    Set<NewIssuesNotification> notifications = IntStream.range(0, 1 + new Random().nextInt(10))
+      .mapToObj(i -> mock(NewIssuesNotification.class))
+      .collect(toSet());
+
+    int deliver = underTest.deliver(notifications);
+
+    assertThat(deliver).isZero();
+    verifyZeroInteractions(notificationManager);
+    verify(emailNotificationChannel).isActivated();
+    verifyNoMoreInteractions(emailNotificationChannel);
+    notifications.forEach(Mockito::verifyZeroInteractions);
+  }
+
+  @Test
+  public void deliver_has_no_effect_if_no_notification_has_projectKey() {
+    when(emailNotificationChannel.isActivated()).thenReturn(true);
+    Set<NewIssuesNotification> notifications = IntStream.range(0, 1 + new Random().nextInt(10))
+      .mapToObj(i -> newNotification(null))
+      .collect(toSet());
+
+    int deliver = underTest.deliver(notifications);
+
+    assertThat(deliver).isZero();
+    verifyZeroInteractions(notificationManager);
+    verify(emailNotificationChannel).isActivated();
+    verifyNoMoreInteractions(emailNotificationChannel);
+    notifications.forEach(notification -> {
+      verify(notification).getProjectKey();
+      verifyNoMoreInteractions(notification);
+    });
+  }
+
+  @Test
+  public void deliver_has_no_effect_if_no_notification_has_subscribed_recipients_to_NewIssue_notifications() {
+    String projectKey = randomAlphabetic(12);
+    NewIssuesNotification notification = newNotification(projectKey);
+    when(emailNotificationChannel.isActivated()).thenReturn(true);
+    when(notificationManager.findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey, ALL_MUST_HAVE_ROLE_USER))
+      .thenReturn(emptySet());
+
+    int deliver = underTest.deliver(Collections.singleton(notification));
+
+    assertThat(deliver).isZero();
+    verify(notificationManager).findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey, ALL_MUST_HAVE_ROLE_USER);
+    verifyNoMoreInteractions(notificationManager);
+    verify(emailNotificationChannel).isActivated();
+    verifyNoMoreInteractions(emailNotificationChannel);
+  }
+
+  @Test
+  public void deliver_ignores_notification_without_projectKey() {
+    String projectKey = randomAlphabetic(10);
+    Set<NewIssuesNotification> withProjectKey = IntStream.range(0, 1 + new Random().nextInt(5))
+      .mapToObj(i -> newNotification(projectKey))
+      .collect(toSet());
+    Set<NewIssuesNotification> noProjectKey = IntStream.range(0, 1 + new Random().nextInt(5))
+      .mapToObj(i -> newNotification(null))
+      .collect(toSet());
+    Set<EmailRecipient> emailRecipients = IntStream.range(0, 1 + new Random().nextInt(10))
+      .mapToObj(i -> "user_" + i)
+      .map(login -> new EmailRecipient(login, emailOf(login)))
+      .collect(toSet());
+    Set<EmailDeliveryRequest> expectedRequests = emailRecipients.stream()
+      .flatMap(emailRecipient -> withProjectKey.stream().map(notif -> new EmailDeliveryRequest(emailRecipient.getEmail(), notif)))
+      .collect(toSet());
+    when(emailNotificationChannel.isActivated()).thenReturn(true);
+    when(notificationManager.findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey, ALL_MUST_HAVE_ROLE_USER))
+      .thenReturn(emailRecipients);
+
+    Set<NewIssuesNotification> notifications = Stream.of(withProjectKey.stream(), noProjectKey.stream())
+      .flatMap(t -> t)
+      .collect(toSet());
+    int deliver = underTest.deliver(notifications);
+
+    assertThat(deliver).isZero();
+    verify(notificationManager).findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey, ALL_MUST_HAVE_ROLE_USER);
+    verifyNoMoreInteractions(notificationManager);
+    verify(emailNotificationChannel).isActivated();
+    verify(emailNotificationChannel).deliver(expectedRequests);
+    verifyNoMoreInteractions(emailNotificationChannel);
+  }
+
+  @Test
+  public void deliver_checks_by_projectKey_if_notifications_have_subscribed_assignee_to_MyNewIssue_notifications() {
+    String projectKey1 = randomAlphabetic(10);
+    String projectKey2 = randomAlphabetic(11);
+    Set<NewIssuesNotification> notifications1 = randomSetOfNotifications(projectKey1);
+    Set<NewIssuesNotification> notifications2 = randomSetOfNotifications(projectKey2);
+    when(emailNotificationChannel.isActivated()).thenReturn(true);
+
+    Set<EmailRecipient> emailRecipients1 = IntStream.range(0, 1 + new Random().nextInt(10))
+      .mapToObj(i -> "user1_" + i)
+      .map(login -> new EmailRecipient(login, emailOf(login)))
+      .collect(toSet());
+    Set<EmailRecipient> emailRecipients2 = IntStream.range(0, 1 + new Random().nextInt(10))
+      .mapToObj(i -> "user2_" + i)
+      .map(login -> new EmailRecipient(login, emailOf(login)))
+      .collect(toSet());
+    when(notificationManager.findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER))
+      .thenReturn(emailRecipients1);
+    when(notificationManager.findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey2, ALL_MUST_HAVE_ROLE_USER))
+      .thenReturn(emailRecipients2);
+    Set<EmailDeliveryRequest> expectedRequests = Stream.concat(
+      emailRecipients1.stream()
+        .flatMap(emailRecipient -> notifications1.stream().map(notif -> new EmailDeliveryRequest(emailRecipient.getEmail(), notif))),
+      emailRecipients2.stream()
+        .flatMap(emailRecipient -> notifications2.stream().map(notif -> new EmailDeliveryRequest(emailRecipient.getEmail(), notif))))
+      .collect(toSet());
+
+    int deliver = underTest.deliver(Stream.concat(notifications1.stream(), notifications2.stream()).collect(toSet()));
+
+    assertThat(deliver).isZero();
+    verify(notificationManager).findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER);
+    verify(notificationManager).findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey2, ALL_MUST_HAVE_ROLE_USER);
+    verifyNoMoreInteractions(notificationManager);
+    verify(emailNotificationChannel).isActivated();
+    verify(emailNotificationChannel).deliver(expectedRequests);
+    verifyNoMoreInteractions(emailNotificationChannel);
+  }
+
+  @Test
+  public void deliver_send_notifications_to_all_subscribers_of_all_projects() {
+    String projectKey1 = randomAlphabetic(10);
+    String projectKey2 = randomAlphabetic(11);
+    Set<NewIssuesNotification> notifications1 = randomSetOfNotifications(projectKey1);
+    Set<NewIssuesNotification> notifications2 = randomSetOfNotifications(projectKey2);
+    when(emailNotificationChannel.isActivated()).thenReturn(true);
+    when(notificationManager.findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER))
+      .thenReturn(emptySet());
+    when(notificationManager.findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey2, ALL_MUST_HAVE_ROLE_USER))
+      .thenReturn(emptySet());
+
+    int deliver = underTest.deliver(Stream.concat(notifications1.stream(), notifications2.stream()).collect(toSet()));
+
+    assertThat(deliver).isZero();
+    verify(notificationManager).findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER);
+    verify(notificationManager).findSubscribedEmailRecipients(NEW_ISSUES_DISPATCHER_KEY, projectKey2, ALL_MUST_HAVE_ROLE_USER);
+    verifyNoMoreInteractions(notificationManager);
+    verify(emailNotificationChannel).isActivated();
+    verifyNoMoreInteractions(emailNotificationChannel);
+  }
+
+  private static Set<NewIssuesNotification> randomSetOfNotifications(@Nullable String projectKey) {
+    return IntStream.range(0, 1 + new Random().nextInt(5))
+      .mapToObj(i -> newNotification(projectKey))
+      .collect(Collectors.toSet());
+  }
+
+  private static NewIssuesNotification newNotification(@Nullable String projectKey) {
+    NewIssuesNotification notification = mock(NewIssuesNotification.class);
+    when(notification.getProjectKey()).thenReturn(projectKey);
+    return notification;
+  }
+
+  private static String emailOf(String assignee1) {
+    return assignee1 + "@donut";
+  }
+
+}
index c7f54833a889c480422da18f477042968ac947dc..e3d7b02dbb0226438ec087fa52f21cf2070abf0f 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.db.user.UserDto;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.api.rules.RuleType.BUG;
@@ -97,6 +98,30 @@ public class NewIssuesNotificationTest {
     assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_VERSION)).isNull();
   }
 
+  @Test
+  public void getProjectKey_returns_null_if_setProject_has_no_been_called() {
+    assertThat(underTest.getProjectKey()).isNull();
+  }
+
+  @Test
+  public void getProjectKey_returns_projectKey_if_setProject_has_been_called() {
+    String projectKey = randomAlphabetic(5);
+    String projectName = randomAlphabetic(6);
+    String branchName = randomAlphabetic(7);
+    String pullRequest = randomAlphabetic(8);
+    underTest.setProject(projectKey, projectName, branchName, pullRequest);
+
+    assertThat(underTest.getProjectKey()).isEqualTo(projectKey);
+  }
+
+  @Test
+  public void getProjectKey_returns_value_of_field_projectKey() {
+    String projectKey = randomAlphabetic(5);
+    underTest.setFieldValue("projectKey", projectKey);
+
+    assertThat(underTest.getProjectKey()).isEqualTo(projectKey);
+  }
+
   @Test
   public void set_date() {
     Date date = new Date();
index 605aa2583cf398c91a4fae0ed635ac6f2085bd00..758c6aff096e7a55cfe4936393280be6a0e0b17d 100644 (file)
@@ -28,7 +28,7 @@ import org.sonar.api.config.Configuration;
 import org.sonar.process.ProcessProperties;
 import org.sonar.server.event.NewAlerts;
 import org.sonar.server.issue.notification.DoNotFixNotificationDispatcher;
-import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
+import org.sonar.server.issue.notification.NewIssuesNotificationHandler;
 import org.sonar.server.notification.NotificationCenter;
 
 import static org.sonar.core.util.stream.MoreCollectors.toList;
@@ -40,7 +40,7 @@ public class DispatchersImpl implements Dispatchers, Startable {
   private static final Set<String> GLOBAL_DISPATCHERS_TO_IGNORE_ON_SONAR_CLOUD = ImmutableSet.of(
     NewAlerts.KEY,
     DoNotFixNotificationDispatcher.KEY,
-    NewIssuesNotificationDispatcher.KEY);
+    NewIssuesNotificationHandler.KEY);
 
   private final NotificationCenter notificationCenter;
   private final Configuration configuration;
index b44de2ea7736036e5ae1e744d531bc4ba3105fdd..d1960a8d73ec72f4ee95fd55dc343f04edc8a137 100644 (file)
@@ -86,8 +86,8 @@ import org.sonar.server.issue.notification.IssueChangesEmailTemplate;
 import org.sonar.server.issue.notification.MyNewIssuesEmailTemplate;
 import org.sonar.server.issue.notification.MyNewIssuesNotificationHandler;
 import org.sonar.server.issue.notification.NewIssuesEmailTemplate;
-import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
 import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
+import org.sonar.server.issue.notification.NewIssuesNotificationHandler;
 import org.sonar.server.issue.ws.IssueWsModule;
 import org.sonar.server.language.ws.LanguageWs;
 import org.sonar.server.log.ServerLogging;
@@ -412,8 +412,8 @@ public class PlatformLevel4 extends PlatformLevel {
       IssueChangesEmailTemplate.class,
       ChangesOnMyIssueNotificationDispatcher.class,
       ChangesOnMyIssueNotificationDispatcher.newMetadata(),
-      NewIssuesNotificationDispatcher.class,
-      NewIssuesNotificationDispatcher.newMetadata(),
+      NewIssuesNotificationHandler.class,
+      NewIssuesNotificationHandler.newMetadata(),
       MyNewIssuesNotificationHandler.class,
       MyNewIssuesNotificationHandler.newMetadata(),
       DoNotFixNotificationDispatcher.class,
index 7fd2cdc1eee4d5006d0a32d46942f578ad7ae919..b7b00908b34e92f8f266f0f22c75d0febf19e118 100644 (file)
@@ -25,7 +25,7 @@ import org.sonar.api.notifications.NotificationChannel;
 import org.sonar.server.event.NewAlerts;
 import org.sonar.server.issue.notification.DoNotFixNotificationDispatcher;
 import org.sonar.server.issue.notification.MyNewIssuesNotificationHandler;
-import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
+import org.sonar.server.issue.notification.NewIssuesNotificationHandler;
 import org.sonar.server.notification.NotificationCenter;
 import org.sonar.server.notification.NotificationDispatcherMetadata;
 
@@ -40,7 +40,7 @@ public class DispatchersImplTest {
       NotificationDispatcherMetadata.create(MyNewIssuesNotificationHandler.KEY)
         .setProperty(GLOBAL_NOTIFICATION, "true")
         .setProperty(PER_PROJECT_NOTIFICATION, "true"),
-      NotificationDispatcherMetadata.create(NewIssuesNotificationDispatcher.KEY)
+      NotificationDispatcherMetadata.create(NewIssuesNotificationHandler.KEY)
         .setProperty(GLOBAL_NOTIFICATION, "true"),
       NotificationDispatcherMetadata.create(NewAlerts.KEY)
         .setProperty(GLOBAL_NOTIFICATION, "true")
@@ -60,7 +60,7 @@ public class DispatchersImplTest {
     underTest.start();
 
     assertThat(underTest.getGlobalDispatchers()).containsExactly(
-      NewAlerts.KEY, DoNotFixNotificationDispatcher.KEY, NewIssuesNotificationDispatcher.KEY, MyNewIssuesNotificationHandler.KEY);
+      NewAlerts.KEY, DoNotFixNotificationDispatcher.KEY, NewIssuesNotificationHandler.KEY, MyNewIssuesNotificationHandler.KEY);
   }
 
   @Test