aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2019-03-25 10:24:56 +0100
committersonartech <sonartech@sonarsource.com>2019-04-23 10:37:52 +0200
commit5317ebf32dc7906af57f378dc7afcf0458f737ec (patch)
tree9014de13acaf4ee411b894956c6038ea53e10510 /server
parentd39e02878cdbbfbedd62c85aa6c93d443ef29cfd (diff)
downloadsonarqube-5317ebf32dc7906af57f378dc7afcf0458f737ec.tar.gz
sonarqube-5317ebf32dc7906af57f378dc7afcf0458f737ec.zip
SONAR-11753 move "New Issues" notification to email specific algo
Diffstat (limited to 'server')
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStep.java12
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStepTest.java3
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java6
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java5
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java6
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java71
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationHandler.java89
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java16
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java70
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationHandlerTest.java252
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java25
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/notification/ws/DispatchersImpl.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/notification/ws/DispatchersImplTest.java6
14 files changed, 393 insertions, 178 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStep.java
index dee1f1bb373..02c2e2a7f9e 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStep.java
@@ -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) {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStepTest.java
index 37caed25832..f17efdeb442 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendIssueNotificationsStepTest.java
@@ -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);
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
index f0d236a35fc..a09aaff37b5 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
@@ -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,
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java
index a1e11bafa11..54d0d1aeb25 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java
@@ -45,11 +45,6 @@ public class MyNewIssuesNotification extends NewIssuesNotification {
}
@CheckForNull
- public String getProjectKey() {
- return getFieldValue("projectKey");
- }
-
- @CheckForNull
public String getAssignee() {
return getFieldValue(FIELD_ASSIGNEE);
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
index b50ca9ca2a1..28eb3f673b3 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
@@ -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
index d71ce535b8f..00000000000
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcher.java
+++ /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
index 00000000000..0dd5ee1e096
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/NewIssuesNotificationHandler.java
@@ -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)));
+ }
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java
index 7669b6805e6..de3eb27e3af 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java
@@ -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
index 182d5933493..00000000000
--- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationDispatcherTest.java
+++ /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
index 00000000000..c6eb8954195
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationHandlerTest.java
@@ -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";
+ }
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
index c7f54833a88..e3d7b02dbb0 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
@@ -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;
@@ -98,6 +99,30 @@ public class NewIssuesNotificationTest {
}
@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();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/DispatchersImpl.java b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/DispatchersImpl.java
index 605aa2583cf..758c6aff096 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/DispatchersImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/DispatchersImpl.java
@@ -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;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index b44de2ea773..d1960a8d73e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -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,
diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/DispatchersImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/DispatchersImplTest.java
index 7fd2cdc1eee..b7b00908b34 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/DispatchersImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/DispatchersImplTest.java
@@ -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