aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java10
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java8
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java10
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/NewAlerts.java74
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeEmailTemplate.java (renamed from server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/AlertsEmailTemplate.java)4
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotification.java34
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandler.java88
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/NewAlertsTest.java85
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeEmailTemplateTest.java (renamed from server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/AlertsEmailTemplateTest.java)7
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandlerTest.java252
-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.java10
13 files changed, 409 insertions, 183 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java
index a19b3c0553c..1d14912da2d 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java
@@ -22,7 +22,6 @@ package org.sonar.ce.task.projectanalysis.step;
import java.util.Optional;
import javax.annotation.Nullable;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.notifications.Notification;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
@@ -42,6 +41,9 @@ import org.sonar.ce.task.projectanalysis.metric.Metric;
import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.server.notification.NotificationService;
+import org.sonar.server.qualitygate.notification.QGChangeNotification;
+
+import static java.util.Collections.singleton;
/**
* This step must be executed after computation of quality gate measure {@link QualityGateMeasuresStep}
@@ -128,7 +130,8 @@ public class QualityGateEventsStep implements ComputationStep {
* @param rawStatus OK or ERROR + optional text
*/
private void notifyUsers(Component project, String label, QualityGateStatus rawStatus, boolean isNewAlert) {
- Notification notification = new Notification("alerts")
+ QGChangeNotification notification = new QGChangeNotification();
+ notification
.setDefaultMessage(String.format("Alert on %s: %s", project.getName(), label))
.setFieldValue("projectName", project.getName())
.setFieldValue("projectKey", project.getKey())
@@ -141,6 +144,9 @@ public class QualityGateEventsStep implements ComputationStep {
if (!branch.isMain()) {
notification.setFieldValue("branch", branch.getName());
}
+ notificationService.deliverEmails(singleton(notification));
+
+ // compatibility with old API
notificationService.deliver(notification);
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java
index 12251c53a1b..23e7c2d4d5b 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java
@@ -19,8 +19,8 @@
*/
package org.sonar.ce.task.projectanalysis.step;
+import java.util.Collection;
import java.util.Optional;
-import java.util.Random;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -44,6 +44,7 @@ import org.sonar.ce.task.step.TestComputationStepContext;
import org.sonar.db.component.BranchType;
import org.sonar.server.notification.NotificationService;
import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.notification.QGChangeNotification;
import static java.util.Collections.emptyList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
@@ -177,8 +178,13 @@ public class QualityGateEventsStepTest {
assertThat(event.getDescription()).isEqualTo(ALERT_TEXT);
assertThat(event.getData()).isNull();
+ ArgumentCaptor<Collection> collectionCaptor = ArgumentCaptor.forClass(Collection.class);
+ verify(notificationService).deliverEmails(collectionCaptor.capture());
verify(notificationService).deliver(notificationArgumentCaptor.capture());
Notification notification = notificationArgumentCaptor.getValue();
+ assertThat(collectionCaptor.getValue()).hasSize(1);
+ assertThat(collectionCaptor.getValue().iterator().next()).isSameAs(notification);
+ assertThat(notification).isInstanceOf(QGChangeNotification.class);
assertThat(notification.getType()).isEqualTo("alerts");
assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
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 f17fed85c8a..2f43b9b4ad3 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
@@ -91,7 +91,6 @@ import org.sonar.server.component.index.ComponentIndexer;
import org.sonar.server.config.ConfigurationProvider;
import org.sonar.server.es.EsModule;
import org.sonar.server.es.ProjectIndexersImpl;
-import org.sonar.server.qualitygate.notification.NewAlerts;
import org.sonar.server.extension.CoreExtensionBootstraper;
import org.sonar.server.extension.CoreExtensionStopper;
import org.sonar.server.favorite.FavoriteUpdater;
@@ -115,7 +114,6 @@ import org.sonar.server.metric.CoreCustomMetrics;
import org.sonar.server.metric.DefaultMetricFinder;
import org.sonar.server.notification.DefaultNotificationManager;
import org.sonar.server.notification.NotificationService;
-import org.sonar.server.qualitygate.notification.AlertsEmailTemplate;
import org.sonar.server.notification.email.EmailNotificationChannel;
import org.sonar.server.organization.BillingValidationsProxyImpl;
import org.sonar.server.organization.DefaultOrganizationProviderImpl;
@@ -140,6 +138,8 @@ import org.sonar.server.plugins.ServerExtensionInstaller;
import org.sonar.server.property.InternalPropertiesImpl;
import org.sonar.server.qualitygate.QualityGateEvaluatorImpl;
import org.sonar.server.qualitygate.QualityGateFinder;
+import org.sonar.server.qualitygate.notification.QGChangeEmailTemplate;
+import org.sonar.server.qualitygate.notification.QGChangeNotificationHandler;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.DefaultRuleFinder;
import org.sonar.server.rule.index.RuleIndex;
@@ -384,8 +384,8 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
// components,
FavoriteUpdater.class,
ProjectIndexersImpl.class,
- NewAlerts.class,
- NewAlerts.newMetadata(),
+ QGChangeNotificationHandler.class,
+ QGChangeNotificationHandler.newMetadata(),
ProjectMeasuresIndexer.class,
ComponentIndexer.class,
@@ -413,7 +413,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
DoNotFixNotificationHandler.newMetadata(),
// Notifications
- AlertsEmailTemplate.class,
+ QGChangeEmailTemplate.class,
EmailSettings.class,
NotificationService.class,
DefaultNotificationManager.class,
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/NewAlerts.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/NewAlerts.java
deleted file mode 100644
index 61adbc65d4d..00000000000
--- a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/NewAlerts.java
+++ /dev/null
@@ -1,74 +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.qualitygate.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 each new alert event".
- *
- * @since 3.5
- */
-public class NewAlerts extends NotificationDispatcher {
-
- public static final String KEY = "NewAlerts";
- private final NotificationManager notifications;
-
- public NewAlerts(NotificationManager notifications) {
- super("alerts");
- this.notifications = notifications;
- }
-
- @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");
- if (projectKey != null) {
- Multimap<String, NotificationChannel> subscribedRecipients = notifications
- .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/qualitygate/notification/AlertsEmailTemplate.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeEmailTemplate.java
index 1fa513208a4..284658af2db 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/AlertsEmailTemplate.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeEmailTemplate.java
@@ -32,11 +32,11 @@ import org.sonar.plugins.emailnotifications.api.EmailTemplate;
*
* @since 3.5
*/
-public class AlertsEmailTemplate extends EmailTemplate {
+public class QGChangeEmailTemplate extends EmailTemplate {
private EmailSettings configuration;
- public AlertsEmailTemplate(EmailSettings configuration) {
+ public QGChangeEmailTemplate(EmailSettings configuration) {
this.configuration = configuration;
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotification.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotification.java
new file mode 100644
index 00000000000..07ff928af93
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotification.java
@@ -0,0 +1,34 @@
+/*
+ * 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.qualitygate.notification;
+
+import javax.annotation.CheckForNull;
+import org.sonar.api.notifications.Notification;
+
+public class QGChangeNotification extends Notification {
+ public QGChangeNotification() {
+ super("alerts");
+ }
+
+ @CheckForNull
+ public String getProjectKey() {
+ return getFieldValue("projectKey");
+ }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandler.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandler.java
new file mode 100644
index 00000000000..18402cff45e
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandler.java
@@ -0,0 +1,88 @@
+/*
+ * 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.qualitygate.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 QGChangeNotificationHandler implements NotificationHandler<QGChangeNotification> {
+
+ public static final String KEY = "NewAlerts";
+
+ private final NotificationManager notificationManager;
+ private final EmailNotificationChannel emailNotificationChannel;
+
+ public QGChangeNotificationHandler(NotificationManager notificationManager, EmailNotificationChannel emailNotificationChannel) {
+ this.notificationManager = notificationManager;
+ this.emailNotificationChannel = emailNotificationChannel;
+ }
+
+ @Override
+ public Class<QGChangeNotification> getNotificationClass() {
+ return QGChangeNotification.class;
+ }
+
+ 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 int deliver(Collection<QGChangeNotification> notifications) {
+ if (notifications.isEmpty() || !emailNotificationChannel.isActivated()) {
+ return 0;
+ }
+
+ Multimap<String, QGChangeNotification> notificationsByProjectKey = notifications.stream()
+ .filter(t -> t.getProjectKey() != null)
+ .collect(index(QGChangeNotification::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<QGChangeNotification> 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/qualitygate/notification/NewAlertsTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/NewAlertsTest.java
deleted file mode 100644
index 48a7b9f036c..00000000000
--- a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/NewAlertsTest.java
+++ /dev/null
@@ -1,85 +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.qualitygate.notification;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-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.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-public class NewAlertsTest {
-
- private NotificationManager notificationManager = mock(NotificationManager.class);
- private NotificationDispatcher.Context context = mock(NotificationDispatcher.Context.class);
- private NotificationChannel emailChannel = mock(NotificationChannel.class);
- private NotificationChannel twitterChannel = mock(NotificationChannel.class);
- private NewAlerts dispatcher = new NewAlerts(notificationManager);
-
- @Test
- public void should_not_dispatch_if_not_alerts_notification() {
- Notification notification = new Notification("other-notif");
- dispatcher.performDispatch(notification, context);
-
- verify(context, never()).addUser(any(String.class), any(NotificationChannel.class));
- }
-
- @Test
- public void should_dispatch_to_users_who_have_subscribed() {
- Multimap<String, NotificationChannel> recipients = HashMultimap.create();
- recipients.put("user1", emailChannel);
- recipients.put("user2", twitterChannel);
- when(notificationManager.findSubscribedRecipientsForDispatcher(dispatcher, "key_34", new NotificationManager.SubscriberPermissionsOnProject(UserRole.USER)))
- .thenReturn(recipients);
-
- Notification notification = new Notification("alerts")
- .setFieldValue("projectKey", "key_34");
- dispatcher.performDispatch(notification, context);
-
- verify(context).addUser("user1", emailChannel);
- verify(context).addUser("user2", twitterChannel);
- verifyNoMoreInteractions(context);
- }
-
- @Test
- public void should_not_dispatch_if_missing_project_key() {
- Multimap<String, NotificationChannel> recipients = HashMultimap.create();
- recipients.put("user1", emailChannel);
- recipients.put("user2", twitterChannel);
- when(notificationManager.findSubscribedRecipientsForDispatcher(dispatcher, "key_34", new NotificationManager.SubscriberPermissionsOnProject(UserRole.USER)))
- .thenReturn(recipients);
-
- Notification notification = new Notification("alerts");
- dispatcher.performDispatch(notification, context);
-
- verifyNoMoreInteractions(context);
- }
-
-}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/AlertsEmailTemplateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeEmailTemplateTest.java
index e8a06c216bb..c6ca1b64d4c 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/AlertsEmailTemplateTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeEmailTemplateTest.java
@@ -24,7 +24,6 @@ import org.junit.Test;
import org.sonar.api.config.EmailSettings;
import org.sonar.api.notifications.Notification;
import org.sonar.plugins.emailnotifications.api.EmailMessage;
-import org.sonar.server.qualitygate.notification.AlertsEmailTemplate;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
@@ -32,15 +31,15 @@ import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class AlertsEmailTemplateTest {
+public class QGChangeEmailTemplateTest {
- private AlertsEmailTemplate template;
+ private QGChangeEmailTemplate template;
@Before
public void setUp() {
EmailSettings configuration = mock(EmailSettings.class);
when(configuration.getServerBaseURL()).thenReturn("http://nemo.sonarsource.org");
- template = new AlertsEmailTemplate(configuration);
+ template = new QGChangeEmailTemplate(configuration);
}
@Test
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandlerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandlerTest.java
new file mode 100644
index 00000000000..a9faf4d26db
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/notification/QGChangeNotificationHandlerTest.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.qualitygate.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.email.EmailNotificationChannel;
+
+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 QGChangeNotificationHandlerTest {
+ private static final String QG_CHANGE_DISPATCHER_KEY = "NewAlerts";
+ private NotificationManager notificationManager = mock(NotificationManager.class);
+ private EmailNotificationChannel emailNotificationChannel = mock(EmailNotificationChannel.class);
+ private QGChangeNotificationHandler underTest = new QGChangeNotificationHandler(notificationManager, emailNotificationChannel);
+
+ @Test
+ public void verify_qgChange_notification_dispatcher_key() {
+ NotificationDispatcherMetadata metadata = QGChangeNotificationHandler.newMetadata();
+
+ assertThat(metadata.getDispatcherKey()).isEqualTo(QG_CHANGE_DISPATCHER_KEY);
+ }
+
+ @Test
+ public void qgChange_notification_is_enable_at_global_level() {
+ NotificationDispatcherMetadata metadata = QGChangeNotificationHandler.newMetadata();
+
+ assertThat(metadata.getProperty(GLOBAL_NOTIFICATION)).isEqualTo("true");
+ }
+
+ @Test
+ public void qgChange_notification_is_enable_at_project_level() {
+ NotificationDispatcherMetadata metadata = QGChangeNotificationHandler.newMetadata();
+
+ assertThat(metadata.getProperty(PER_PROJECT_NOTIFICATION)).isEqualTo("true");
+ }
+
+ @Test
+ public void getNotificationClass_is_QGChangeNotification() {
+ assertThat(underTest.getNotificationClass()).isEqualTo(QGChangeNotification.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<QGChangeNotification> notifications = IntStream.range(0, 1 + new Random().nextInt(10))
+ .mapToObj(i -> mock(QGChangeNotification.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<QGChangeNotification> 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_QGChange_notifications() {
+ String projectKey = randomAlphabetic(12);
+ QGChangeNotification notification = newNotification(projectKey);
+ when(emailNotificationChannel.isActivated()).thenReturn(true);
+ when(notificationManager.findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey, ALL_MUST_HAVE_ROLE_USER))
+ .thenReturn(emptySet());
+
+ int deliver = underTest.deliver(Collections.singleton(notification));
+
+ assertThat(deliver).isZero();
+ verify(notificationManager).findSubscribedEmailRecipients(QG_CHANGE_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<QGChangeNotification> withProjectKey = IntStream.range(0, 1 + new Random().nextInt(5))
+ .mapToObj(i -> newNotification(projectKey))
+ .collect(toSet());
+ Set<QGChangeNotification> noProjectKey = IntStream.range(0, 1 + new Random().nextInt(5))
+ .mapToObj(i -> newNotification(null))
+ .collect(toSet());
+ Set<NotificationManager.EmailRecipient> emailRecipients = IntStream.range(0, 1 + new Random().nextInt(10))
+ .mapToObj(i -> "user_" + i)
+ .map(login -> new NotificationManager.EmailRecipient(login, emailOf(login)))
+ .collect(toSet());
+ Set<EmailNotificationChannel.EmailDeliveryRequest> expectedRequests = emailRecipients.stream()
+ .flatMap(emailRecipient -> withProjectKey.stream().map(notif -> new EmailNotificationChannel.EmailDeliveryRequest(emailRecipient.getEmail(), notif)))
+ .collect(toSet());
+ when(emailNotificationChannel.isActivated()).thenReturn(true);
+ when(notificationManager.findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey, ALL_MUST_HAVE_ROLE_USER))
+ .thenReturn(emailRecipients);
+
+ Set<QGChangeNotification> notifications = Stream.of(withProjectKey.stream(), noProjectKey.stream())
+ .flatMap(t -> t)
+ .collect(toSet());
+ int deliver = underTest.deliver(notifications);
+
+ assertThat(deliver).isZero();
+ verify(notificationManager).findSubscribedEmailRecipients(QG_CHANGE_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_QGChange_notifications() {
+ String projectKey1 = randomAlphabetic(10);
+ String projectKey2 = randomAlphabetic(11);
+ Set<QGChangeNotification> notifications1 = randomSetOfNotifications(projectKey1);
+ Set<QGChangeNotification> notifications2 = randomSetOfNotifications(projectKey2);
+ when(emailNotificationChannel.isActivated()).thenReturn(true);
+
+ Set<NotificationManager.EmailRecipient> emailRecipients1 = IntStream.range(0, 1 + new Random().nextInt(10))
+ .mapToObj(i -> "user1_" + i)
+ .map(login -> new NotificationManager.EmailRecipient(login, emailOf(login)))
+ .collect(toSet());
+ Set<NotificationManager.EmailRecipient> emailRecipients2 = IntStream.range(0, 1 + new Random().nextInt(10))
+ .mapToObj(i -> "user2_" + i)
+ .map(login -> new NotificationManager.EmailRecipient(login, emailOf(login)))
+ .collect(toSet());
+ when(notificationManager.findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER))
+ .thenReturn(emailRecipients1);
+ when(notificationManager.findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey2, ALL_MUST_HAVE_ROLE_USER))
+ .thenReturn(emailRecipients2);
+ Set<EmailNotificationChannel.EmailDeliveryRequest> expectedRequests = Stream.concat(
+ emailRecipients1.stream()
+ .flatMap(emailRecipient -> notifications1.stream().map(notif -> new EmailNotificationChannel.EmailDeliveryRequest(emailRecipient.getEmail(), notif))),
+ emailRecipients2.stream()
+ .flatMap(emailRecipient -> notifications2.stream().map(notif -> new EmailNotificationChannel.EmailDeliveryRequest(emailRecipient.getEmail(), notif))))
+ .collect(toSet());
+
+ int deliver = underTest.deliver(Stream.concat(notifications1.stream(), notifications2.stream()).collect(toSet()));
+
+ assertThat(deliver).isZero();
+ verify(notificationManager).findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER);
+ verify(notificationManager).findSubscribedEmailRecipients(QG_CHANGE_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<QGChangeNotification> notifications1 = randomSetOfNotifications(projectKey1);
+ Set<QGChangeNotification> notifications2 = randomSetOfNotifications(projectKey2);
+ when(emailNotificationChannel.isActivated()).thenReturn(true);
+ when(notificationManager.findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER))
+ .thenReturn(emptySet());
+ when(notificationManager.findSubscribedEmailRecipients(QG_CHANGE_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(QG_CHANGE_DISPATCHER_KEY, projectKey1, ALL_MUST_HAVE_ROLE_USER);
+ verify(notificationManager).findSubscribedEmailRecipients(QG_CHANGE_DISPATCHER_KEY, projectKey2, ALL_MUST_HAVE_ROLE_USER);
+ verifyNoMoreInteractions(notificationManager);
+ verify(emailNotificationChannel).isActivated();
+ verifyNoMoreInteractions(emailNotificationChannel);
+ }
+
+ private static Set<QGChangeNotification> randomSetOfNotifications(@Nullable String projectKey) {
+ return IntStream.range(0, 1 + new Random().nextInt(5))
+ .mapToObj(i -> newNotification(projectKey))
+ .collect(Collectors.toSet());
+ }
+
+ private static QGChangeNotification newNotification(@Nullable String projectKey) {
+ QGChangeNotification notification = mock(QGChangeNotification.class);
+ when(notification.getProjectKey()).thenReturn(projectKey);
+ return notification;
+ }
+
+ private static String emailOf(String assignee1) {
+ return assignee1 + "@giraffe";
+ }
+
+
+
+}
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 5800cca5d34..4b557e8c96b 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
@@ -26,10 +26,10 @@ import java.util.function.Predicate;
import org.sonar.api.Startable;
import org.sonar.api.config.Configuration;
import org.sonar.process.ProcessProperties;
-import org.sonar.server.qualitygate.notification.NewAlerts;
import org.sonar.server.issue.notification.DoNotFixNotificationHandler;
import org.sonar.server.issue.notification.NewIssuesNotificationHandler;
import org.sonar.server.notification.NotificationCenter;
+import org.sonar.server.qualitygate.notification.QGChangeNotificationHandler;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
@@ -38,7 +38,7 @@ import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_P
public class DispatchersImpl implements Dispatchers, Startable {
private static final Set<String> GLOBAL_DISPATCHERS_TO_IGNORE_ON_SONAR_CLOUD = ImmutableSet.of(
- NewAlerts.KEY,
+ QGChangeNotificationHandler.KEY,
DoNotFixNotificationHandler.KEY,
NewIssuesNotificationHandler.KEY);
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 bdb47697472..e05feaf20fd 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
@@ -64,7 +64,6 @@ import org.sonar.server.es.RecoveryIndexer;
import org.sonar.server.es.metadata.EsDbCompatibilityImpl;
import org.sonar.server.es.metadata.MetadataIndex;
import org.sonar.server.es.metadata.MetadataIndexDefinition;
-import org.sonar.server.qualitygate.notification.NewAlerts;
import org.sonar.server.extension.CoreExtensionBootstraper;
import org.sonar.server.extension.CoreExtensionStopper;
import org.sonar.server.favorite.FavoriteModule;
@@ -151,6 +150,7 @@ import org.sonar.server.projecttag.ws.ProjectTagsWsModule;
import org.sonar.server.property.InternalPropertiesImpl;
import org.sonar.server.property.ws.PropertiesWs;
import org.sonar.server.qualitygate.QualityGateModule;
+import org.sonar.server.qualitygate.notification.QGChangeNotificationHandler;
import org.sonar.server.qualityprofile.BuiltInQProfileDefinitionsBridge;
import org.sonar.server.qualityprofile.BuiltInQProfileRepositoryImpl;
import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationDispatcher;
@@ -385,8 +385,8 @@ public class PlatformLevel4 extends PlatformLevel {
ComponentService.class,
ComponentUpdater.class,
ComponentFinder.class,
- NewAlerts.class,
- NewAlerts.newMetadata(),
+ QGChangeNotificationHandler.class,
+ QGChangeNotificationHandler.newMetadata(),
ComponentCleanerService.class,
ComponentIndexDefinition.class,
ComponentIndex.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 8b6c25b88fe..1fcd38247fb 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
@@ -22,12 +22,12 @@ package org.sonar.server.notification.ws;
import org.junit.Test;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.server.qualitygate.notification.NewAlerts;
import org.sonar.server.issue.notification.DoNotFixNotificationHandler;
import org.sonar.server.issue.notification.MyNewIssuesNotificationHandler;
import org.sonar.server.issue.notification.NewIssuesNotificationHandler;
import org.sonar.server.notification.NotificationCenter;
import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.qualitygate.notification.QGChangeNotificationHandler;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
@@ -42,7 +42,7 @@ public class DispatchersImplTest {
.setProperty(PER_PROJECT_NOTIFICATION, "true"),
NotificationDispatcherMetadata.create(NewIssuesNotificationHandler.KEY)
.setProperty(GLOBAL_NOTIFICATION, "true"),
- NotificationDispatcherMetadata.create(NewAlerts.KEY)
+ NotificationDispatcherMetadata.create(QGChangeNotificationHandler.KEY)
.setProperty(GLOBAL_NOTIFICATION, "true")
.setProperty(PER_PROJECT_NOTIFICATION, "true"),
NotificationDispatcherMetadata.create(DoNotFixNotificationHandler.KEY)
@@ -60,7 +60,7 @@ public class DispatchersImplTest {
underTest.start();
assertThat(underTest.getGlobalDispatchers()).containsExactly(
- NewAlerts.KEY, DoNotFixNotificationHandler.KEY, NewIssuesNotificationHandler.KEY, MyNewIssuesNotificationHandler.KEY);
+ QGChangeNotificationHandler.KEY, DoNotFixNotificationHandler.KEY, NewIssuesNotificationHandler.KEY, MyNewIssuesNotificationHandler.KEY);
}
@Test
@@ -77,7 +77,7 @@ public class DispatchersImplTest {
underTest.start();
assertThat(underTest.getProjectDispatchers()).containsExactly(
- NewAlerts.KEY, DoNotFixNotificationHandler.KEY, MyNewIssuesNotificationHandler.KEY);
+ QGChangeNotificationHandler.KEY, DoNotFixNotificationHandler.KEY, MyNewIssuesNotificationHandler.KEY);
}
@Test
@@ -87,6 +87,6 @@ public class DispatchersImplTest {
underTest.start();
assertThat(underTest.getProjectDispatchers()).containsOnly(
- MyNewIssuesNotificationHandler.KEY, NewAlerts.KEY, DoNotFixNotificationHandler.KEY);
+ MyNewIssuesNotificationHandler.KEY, QGChangeNotificationHandler.KEY, DoNotFixNotificationHandler.KEY);
}
}