From a83c83f8e1f9c266d7c5e66fde5e2fd241a6e1d3 Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Thu, 14 Jul 2011 02:32:34 +0400 Subject: SONAR-2596 Add an event notification mechanism For the moment works only on server-side and no data persisted into DB. As a proof of concept - write message to log when comment was added to review. --- .../sonar/server/notifications/Notification.java | 28 +++ .../server/notifications/NotificationChannel.java | 50 ++++++ .../notifications/NotificationDispatcher.java | 55 ++++++ .../server/notifications/NotificationManager.java | 85 +++++++++ .../server/notifications/NotificationQueue.java | 52 ++++++ .../server/notifications/NotificationService.java | 109 ++++++++++++ .../server/notifications/email/EmailMessage.java | 110 ++++++++++++ .../notifications/email/EmailMessageTemplate.java | 32 ++++ .../email/EmailNotificationChannel.java | 111 ++++++++++++ .../reviews/CommentOnReviewAssignedToMe.java | 43 +++++ .../reviews/CommentOnReviewCreatedByMe.java | 41 +++++ .../reviews/CommentOnReviewEmailTemplate.java | 45 +++++ .../reviews/CommentOnReviewNotification.java | 53 ++++++ .../reviews/ReviewsNotificationManager.java | 62 +++++++ .../java/org/sonar/server/platform/Platform.java | 27 ++- .../main/java/org/sonar/server/ui/JRubyFacade.java | 5 + .../src/main/webapp/WEB-INF/app/models/review.rb | 4 +- .../notifications/NotificationManagerTest.java | 190 +++++++++++++++++++++ .../notifications/NotificationServiceTest.java | 51 ++++++ .../email/EmailNotificationChannelTest.java | 82 +++++++++ .../reviews/CommentOnReviewAssignedToMeTest.java | 61 +++++++ .../reviews/CommentOnReviewCreatedByMeTest.java | 53 ++++++ .../reviews/ReviewsNotificationManagerTest.java | 57 +++++++ .../sonar/server/notifications/reviews/fixture.xml | 15 ++ 24 files changed, 1418 insertions(+), 3 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/Notification.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java create mode 100644 sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java create mode 100644 sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java create mode 100644 sonar-server/src/test/resources/org/sonar/server/notifications/reviews/fixture.xml (limited to 'sonar-server') diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java b/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java new file mode 100644 index 00000000000..395f90af602 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/Notification.java @@ -0,0 +1,28 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +/** + * Marker interface for data model objects that represents notifications. + * + * @since 2.10 + */ +public interface Notification { +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java new file mode 100644 index 00000000000..1b2726768b1 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationChannel.java @@ -0,0 +1,50 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import org.sonar.api.ServerExtension; + +import java.io.Serializable; + +/** + * Provides logic to deliver notification. + * For example: + * + * + * @since 2.10 + */ +public abstract class NotificationChannel implements ServerExtension { + + /** + * @return unique key of this channel + */ + public String getKey() { + return getClass().getSimpleName(); + } + + public abstract Serializable createDataForPersistance(Notification notification, Integer userId); + + public abstract void deliver(Serializable notificationData); + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java new file mode 100644 index 00000000000..a82ee0004b1 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationDispatcher.java @@ -0,0 +1,55 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import org.sonar.api.ServerExtension; + +/** + * Provides logic to determine which users are interested in receiving notification. + * Has no knowledge about the way of delivery. + * For example: + * + * + * @since 2.10 + */ +public abstract class NotificationDispatcher implements ServerExtension { + + public interface Context { + void addUser(Integer userId); + } + + /** + * @return unique key of this dispatcher + */ + public String getKey() { + return getClass().getSimpleName(); + } + + /** + * @return recipients + */ + public abstract void dispatch(Notification notification, Context context); + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java new file mode 100644 index 00000000000..304f3c664b8 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationManager.java @@ -0,0 +1,85 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.TimeProfiler; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * @since 2.10 + */ +public class NotificationManager implements ServerComponent { // TODO should be available on batch side too + + private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class); + private static final TimeProfiler TIME_PROFILER = new TimeProfiler().setLogger(LOG); + + private NotificationQueue queue; + private NotificationDispatcher[] dispatchers; + private NotificationChannel[] channels; + + public NotificationManager(NotificationQueue queue, NotificationDispatcher[] dispatchers, NotificationChannel[] channels) { + this.queue = queue; + this.dispatchers = dispatchers; + this.channels = channels; + } + + public void scheduleForSending(Notification notification) { + TIME_PROFILER.start("Scheduling " + notification); + SetMultimap recipients = HashMultimap.create(); + for (NotificationChannel channel : channels) { + for (NotificationDispatcher dispatcher : dispatchers) { + final Set possibleRecipients = Sets.newHashSet(); + NotificationDispatcher.Context context = new NotificationDispatcher.Context() { + public void addUser(Integer userId) { + possibleRecipients.add(userId); + } + }; + dispatcher.dispatch(notification, context); + for (Integer userId : possibleRecipients) { + if (isEnabled(userId, channel, dispatcher)) { + recipients.put(userId, channel); + } + } + } + } + for (Map.Entry entry : recipients.entries()) { + Integer userId = entry.getKey(); + NotificationChannel channel = entry.getValue(); + LOG.info("For user {} via {}", userId, channel.getKey()); + Serializable notificationData = channel.createDataForPersistance(notification, userId); + queue.add(notificationData, channel); + } + TIME_PROFILER.stop(); + } + + boolean isEnabled(Integer userId, NotificationChannel channel, NotificationDispatcher dispatcher) { + return true; // FIXME for the moment we will accept everything + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java new file mode 100644 index 00000000000..bf02333d9a7 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationQueue.java @@ -0,0 +1,52 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import org.sonar.api.ServerComponent; + +import java.io.Serializable; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @since 2.10 + */ +public class NotificationQueue implements ServerComponent { + + private ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + + public static class Element { + String channelKey; + Serializable notificationData; + + public Element(String channelKey, Serializable notificationData) { + this.channelKey = channelKey; + this.notificationData = notificationData; + } + } + + public Element get() { + return queue.poll(); + } + + public void add(Serializable notificationData, NotificationChannel channel) { + queue.add(new Element(channel.getKey(), notificationData)); + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java new file mode 100644 index 00000000000..6faab4f133f --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/NotificationService.java @@ -0,0 +1,109 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import com.google.common.collect.Maps; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerComponent; +import org.sonar.server.notifications.NotificationQueue.Element; + +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * @since 2.10 + */ +public class NotificationService implements ServerComponent { + + private static Logger LOG = LoggerFactory.getLogger(NotificationService.class); + + private ScheduledExecutorService executorService; + private long period = 10; // FIXME small value just for tests + private NotificationQueue queue; + + private Map channels = Maps.newHashMap(); + + /** + * Default constructor when no channels. + */ + public NotificationService(NotificationQueue queue) { + this(queue, new NotificationChannel[0]); + LOG.warn("There is no channels - all notifications would be ignored!"); + } + + public NotificationService(NotificationQueue queue, NotificationChannel[] channels) { + this.queue = queue; + for (NotificationChannel channel : channels) { + this.channels.put(channel.getKey(), channel); + } + } + + /** + * Visibility has been relaxed for tests. + */ + void setPeriod(long milliseconds) { + this.period = milliseconds; + } + + public void start() { + executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.scheduleWithFixedDelay(new Runnable() { + public void run() { + processQueue(); + } + }, 0, period, TimeUnit.MILLISECONDS); + LOG.info("Notification service started"); + } + + public void stop() { + try { + executorService.awaitTermination(period, TimeUnit.MILLISECONDS); + executorService.shutdown(); + } catch (InterruptedException e) { + LOG.error("Error during stop of notification service", e); + } + LOG.info("Notification service stopped"); + } + + /** + * Visibility has been relaxed for tests. + */ + void processQueue() { + NotificationQueue.Element element = queue.get(); + while (element != null) { + deliver(element); + element = queue.get(); + } + } + + /** + * Visibility has been relaxed for tests. + */ + void deliver(Element element) { + NotificationChannel channel = channels.get(element.channelKey); + if (channel != null) { + channel.deliver(element.notificationData); + } + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java new file mode 100644 index 00000000000..c64b3b3e175 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java @@ -0,0 +1,110 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.email; + +import java.io.Serializable; + +/** + * @since 2.10 + */ +public class EmailMessage implements Serializable { + + private String from; + private String to; + private String subject; + private String message; + private String messageId; + + /** + * @param from full name of user, who initiated this message or null, if message was initiated by Sonar + */ + public EmailMessage setFrom(String from) { + this.from = from; + return this; + } + + /** + * @see #setFrom(String) + */ + public String getFrom() { + return from; + } + + /** + * @param to email address where to send this message + */ + public EmailMessage setTo(String to) { + this.to = to; + return this; + } + + /** + * @see #setTo(String) + */ + public String getTo() { + return to; + } + + /** + * @param subject message subject + */ + public EmailMessage setSubject(String subject) { + this.subject = subject; + return this; + } + + /** + * @see #setSubject(String) + */ + public String getSubject() { + return subject; + } + + /** + * @param message message body + */ + public EmailMessage setMessage(String message) { + this.message = message; + return this; + } + + /** + * @see #setMessage(String) + */ + public String getMessage() { + return message; + } + + /** + * @param messageId id of message for threading + */ + public EmailMessage setMessageId(String messageId) { + this.messageId = messageId; + return this; + } + + /** + * @see #setMessageId(String) + */ + public String getMessageId() { + return messageId; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java new file mode 100644 index 00000000000..ed465c724cd --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessageTemplate.java @@ -0,0 +1,32 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.email; + +import org.sonar.api.ServerExtension; +import org.sonar.server.notifications.Notification; + +/** + * @since 2.10 + */ +public abstract class EmailMessageTemplate implements ServerExtension { + + public abstract EmailMessage format(Notification notification); + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java new file mode 100644 index 00000000000..a0b12e14a13 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java @@ -0,0 +1,111 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.email; + +import org.codehaus.plexus.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.server.notifications.Notification; +import org.sonar.server.notifications.NotificationChannel; + +import java.io.Serializable; + +/** + * References: + * + * + * @since 2.10 + */ +public class EmailNotificationChannel extends NotificationChannel { + + private static final Logger LOG = LoggerFactory.getLogger(EmailNotificationChannel.class); + + private static final String FROM_DEFAULT = "Sonar"; + private static final String SUBJECT_PREFIX = "[Sonar]"; + private static final String SUBJECT_DEFAULT = "Notification"; + + private EmailMessageTemplate[] templates; + + public EmailNotificationChannel(EmailMessageTemplate[] templates) { + this.templates = templates; + } + + @Override + public Serializable createDataForPersistance(Notification notification, Integer userId) { + for (EmailMessageTemplate template : templates) { + EmailMessage email = template.format(notification); + if (email != null) { + email.setTo(userId.toString()); // TODO should be valid email@address + return email; + } + } + return null; + } + + @Override + public void deliver(Serializable notificationData) { + EmailMessage email = (EmailMessage) notificationData; + LOG.info("Email:\n{}", create(email)); + } + + /** + * Visibility has been relaxed for tests. + */ + String create(EmailMessage email) { + // TODO + String serverUrl = "http://nemo.sonarsource.org"; + String domain = "nemo.sonarsource.org"; + String listId = ""; + String from = StringUtils.defaultString(email.getFrom(), FROM_DEFAULT) + " "; + String subject = SUBJECT_PREFIX + " " + StringUtils.defaultString(email.getSubject(), SUBJECT_DEFAULT); + String permalink = null; + StringBuilder sb = new StringBuilder(); + if (StringUtils.isNotEmpty(email.getMessageId())) { + subject = "Re: " + subject; + String messageId = "<" + email.getMessageId() + "@" + domain + ">"; + appendHeader(sb, "Message-Id", messageId); + appendHeader(sb, "In-Reply-To", messageId); + appendHeader(sb, "References", messageId); + permalink = serverUrl + '/' + email.getMessageId(); + } + appendHeader(sb, "List-Id", listId); + appendHeader(sb, "List-Archive", serverUrl); + appendHeader(sb, "From", from); + appendHeader(sb, "To", email.getTo()); + appendHeader(sb, "Subject", subject); + sb.append('\n') + .append(email.getMessage()).append('\n'); + if (permalink != null) { + sb.append('\n') + .append("--\n") + .append("View it in Sonar: ").append(permalink); + } + return sb.toString(); + } + + private void appendHeader(StringBuilder sb, String header, String value) { + sb.append(header).append(": ").append(value).append('\n'); + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java new file mode 100644 index 00000000000..51af872a075 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMe.java @@ -0,0 +1,43 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import org.sonar.jpa.entity.Review; +import org.sonar.server.notifications.Notification; +import org.sonar.server.notifications.NotificationDispatcher; + +/** + * This dispatcher means: "notify me when when someone comments on review assigned to me". + * + * @since 2.10 + */ +public class CommentOnReviewAssignedToMe extends NotificationDispatcher { + + @Override + public void dispatch(Notification notification, Context context) { + if (notification instanceof CommentOnReviewNotification) { + Review review = ((CommentOnReviewNotification) notification).getReview(); + if (review.getAssigneeId() != null) { + context.addUser(review.getAssigneeId()); + } + } + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java new file mode 100644 index 00000000000..868e6286108 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMe.java @@ -0,0 +1,41 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import org.sonar.jpa.entity.Review; +import org.sonar.server.notifications.Notification; +import org.sonar.server.notifications.NotificationDispatcher; + +/** + * This dispatcher means: "notify me when when someone comments on review created by me". + * + * @since 2.10 + */ +public class CommentOnReviewCreatedByMe extends NotificationDispatcher { + + @Override + public void dispatch(Notification notification, Context context) { + if (notification instanceof CommentOnReviewNotification) { + Review review = ((CommentOnReviewNotification) notification).getReview(); + context.addUser(review.getUserId()); + } + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java new file mode 100644 index 00000000000..eb982500128 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java @@ -0,0 +1,45 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import org.sonar.server.notifications.Notification; +import org.sonar.server.notifications.email.EmailMessage; +import org.sonar.server.notifications.email.EmailMessageTemplate; + +/** + * Email template for {@link CommentOnReviewNotification}. + */ +public class CommentOnReviewEmailTemplate extends EmailMessageTemplate { + + @Override + public EmailMessage format(Notification notification) { + if (notification instanceof CommentOnReviewNotification) { + CommentOnReviewNotification event = (CommentOnReviewNotification) notification; + EmailMessage email = new EmailMessage() + .setFrom(event.getAuthor().getName()) + .setMessageId("review/" + event.getReview().getId()) + .setSubject(event.getReview().getTitle()) + .setMessage(event.getComment()); + return email; + } + return null; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java new file mode 100644 index 00000000000..48a89b36c40 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewNotification.java @@ -0,0 +1,53 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import org.sonar.api.database.model.User; +import org.sonar.jpa.entity.Review; +import org.sonar.server.notifications.Notification; + +/** + * @since 2.10 + */ +public class CommentOnReviewNotification implements Notification { + + private Review review; + private User author; + private String comment; + + public CommentOnReviewNotification(Review review, User author, String comment) { + this.review = review; + this.author = author; + this.comment = comment; + } + + public Review getReview() { + return review; + } + + public User getAuthor() { + return author; + } + + public String getComment() { + return comment; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java new file mode 100644 index 00000000000..602f4e51791 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/ReviewsNotificationManager.java @@ -0,0 +1,62 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import org.sonar.api.ServerComponent; +import org.sonar.api.database.model.User; +import org.sonar.jpa.entity.Review; +import org.sonar.jpa.session.DatabaseSessionFactory; +import org.sonar.server.notifications.NotificationManager; + +/** + * @since 2.10 + */ +public class ReviewsNotificationManager implements ServerComponent { + + private DatabaseSessionFactory sessionFactory; + private NotificationManager notificationManager; + + public ReviewsNotificationManager(DatabaseSessionFactory sessionFactory, NotificationManager notificationManager) { + this.sessionFactory = sessionFactory; + this.notificationManager = notificationManager; + } + + /** + * Visibility has been relaxed for tests. + */ + User getUserById(Integer id) { + return sessionFactory.getSession().getEntity(User.class, id); + } + + /** + * Visibility has been relaxed for tests. + */ + Review getReviewById(Long id) { + return sessionFactory.getSession().getEntity(Review.class, id); + } + + public void notifyCommentAdded(Long reviewId, Integer userId, String comment) { + Review review = getReviewById(reviewId); + User author = getUserById(userId); + CommentOnReviewNotification notification = new CommentOnReviewNotification(review, author, comment); + notificationManager.scheduleForSending(notification); + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 6afe5f7d449..160602d419e 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -40,7 +40,10 @@ import org.sonar.api.utils.TimeProfiler; import org.sonar.core.components.DefaultMetricFinder; import org.sonar.core.components.DefaultModelFinder; import org.sonar.core.components.DefaultRuleFinder; -import org.sonar.jpa.dao.*; +import org.sonar.jpa.dao.DaoFacade; +import org.sonar.jpa.dao.MeasuresDao; +import org.sonar.jpa.dao.ProfilesDao; +import org.sonar.jpa.dao.RulesDao; import org.sonar.jpa.session.DatabaseSessionFactory; import org.sonar.jpa.session.DatabaseSessionProvider; import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; @@ -52,9 +55,18 @@ import org.sonar.server.database.EmbeddedDatabaseFactory; import org.sonar.server.database.JndiDatabaseConnector; import org.sonar.server.filters.FilterExecutor; import org.sonar.server.mavendeployer.MavenRepository; +import org.sonar.server.notifications.NotificationManager; +import org.sonar.server.notifications.NotificationQueue; +import org.sonar.server.notifications.NotificationService; +import org.sonar.server.notifications.email.EmailNotificationChannel; +import org.sonar.server.notifications.reviews.CommentOnReviewAssignedToMe; +import org.sonar.server.notifications.reviews.CommentOnReviewCreatedByMe; +import org.sonar.server.notifications.reviews.CommentOnReviewEmailTemplate; +import org.sonar.server.notifications.reviews.ReviewsNotificationManager; import org.sonar.server.plugins.*; import org.sonar.server.qualitymodel.DefaultModelManager; -import org.sonar.server.rules.*; +import org.sonar.server.rules.ProfilesConsole; +import org.sonar.server.rules.RulesConsole; import org.sonar.server.startup.*; import org.sonar.server.ui.AuthenticatorFactory; import org.sonar.server.ui.CodeColorizers; @@ -179,6 +191,17 @@ public final class Platform { servicesContainer.as(Characteristics.CACHE).addComponent(RulesConsole.class); servicesContainer.as(Characteristics.CACHE).addComponent(JRubyI18n.class); + // Notifications + servicesContainer.as(Characteristics.CACHE).addComponent(NotificationQueue.class); + servicesContainer.as(Characteristics.CACHE).addComponent(NotificationService.class); + servicesContainer.as(Characteristics.CACHE).addComponent(NotificationManager.class); + servicesContainer.as(Characteristics.CACHE).addComponent(ReviewsNotificationManager.class); + // FIXME next four lines here just for tests: + servicesContainer.as(Characteristics.CACHE).addComponent(EmailNotificationChannel.class); + servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewEmailTemplate.class); + servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewAssignedToMe.class); + servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewCreatedByMe.class); + servicesContainer.start(); } diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 8c60ad0fab7..473bc679f65 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -41,6 +41,7 @@ import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.filters.Filter; import org.sonar.server.filters.FilterExecutor; import org.sonar.server.filters.FilterResult; +import org.sonar.server.notifications.reviews.ReviewsNotificationManager; import org.sonar.server.platform.Platform; import org.sonar.server.plugins.*; import org.sonar.server.rules.ProfilesConsole; @@ -308,6 +309,10 @@ public final class JRubyFacade { return i18n.message(rubyLocale, key, defaultValue, parameters); } + public ReviewsNotificationManager getReviewsNotificationManager() { + return getContainer().getComponent(ReviewsNotificationManager.class); + } + public PicoContainer getContainer() { return Platform.getInstance().getContainer(); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb index 9d820401d0d..94df7aed6d6 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb @@ -59,8 +59,10 @@ class Review < ActiveRecord::Base # - :user # - :text def create_comment(params={}) - comments.create!(params) + comment = comments.create!(params) touch + + Java::OrgSonarServerUi::JRubyFacade.getInstance().getReviewsNotificationManager().notifyCommentAdded(id.to_i, comment.user.id.to_i, comment.text.to_java) end def edit_comment(comment_id, comment_text) diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java new file mode 100644 index 00000000000..b1a00deb603 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationManagerTest.java @@ -0,0 +1,190 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.Serializable; + +public class NotificationManagerTest { + + private static Integer USER_SIMON = 1; + private static Integer USER_EVGENY = 2; + + private NotificationChannel emailChannel; + private NotificationChannel gtalkChannel; + + private NotificationDispatcher commentOnReviewAssignedToMe; + private Integer assignee; + private NotificationDispatcher commentOnReviewCreatedByMe; + private Integer creator; + + private NotificationQueue queue; + private NotificationManager manager; + + @Before + public void setUp() { + emailChannel = mock(NotificationChannel.class); + when(emailChannel.getKey()).thenReturn("email"); + doAnswer(new Answer() { + public Serializable answer(InvocationOnMock invocation) throws Throwable { + return (Serializable) invocation.getArguments()[1]; + } + }).when(emailChannel).createDataForPersistance(any(Notification.class), any(Integer.class)); + + gtalkChannel = mock(NotificationChannel.class); + when(gtalkChannel.getKey()).thenReturn("gtalk"); + doAnswer(new Answer() { + public Serializable answer(InvocationOnMock invocation) throws Throwable { + return (Serializable) invocation.getArguments()[1]; + } + }).when(gtalkChannel).createDataForPersistance(any(Notification.class), any(Integer.class)); + + commentOnReviewAssignedToMe = mock(NotificationDispatcher.class); + when(commentOnReviewAssignedToMe.getKey()).thenReturn("comment on review assigned to me"); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) throws Throwable { + ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(assignee); + return null; + } + }).when(commentOnReviewAssignedToMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class)); + + commentOnReviewCreatedByMe = mock(NotificationDispatcher.class); + when(commentOnReviewCreatedByMe.getKey()).thenReturn("comment on review created by me"); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) throws Throwable { + ((NotificationDispatcher.Context) invocation.getArguments()[1]).addUser(creator); + return null; + } + }).when(commentOnReviewCreatedByMe).dispatch(any(Notification.class), any(NotificationDispatcher.Context.class)); + + NotificationDispatcher[] dispatchers = new NotificationDispatcher[] { commentOnReviewAssignedToMe, commentOnReviewCreatedByMe }; + NotificationChannel[] channels = new NotificationChannel[] { emailChannel, gtalkChannel }; + queue = mock(NotificationQueue.class); + manager = spy(new NotificationManager(queue, dispatchers, channels)); + doReturn(false).when(manager).isEnabled(any(Integer.class), any(NotificationChannel.class), any(NotificationDispatcher.class)); + } + + /** + * Given: + * Simon wants to receive notifications by email on comments for reviews assigned to him or created by him. + * + * When: + * Freddy adds comment to review created by Simon and assigned to Simon. + * + * Then: + * Only one notification should be delivered to Simon by Email. + */ + @Test + public void scenario1() { + doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); + doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewCreatedByMe); + + Notification notification = mock(Notification.class); + creator = USER_SIMON; + assignee = USER_SIMON; + + manager.scheduleForSending(notification); + + verify(queue).add(USER_SIMON, emailChannel); + verifyNoMoreInteractions(queue); + } + + /** + * Given: + * Evgeny wants to receive notification by GTalk on comments for reviews created by him. + * Simon wants to receive notification by Email on comments for reviews assigned to him. + * + * When: + * Freddy adds comment to review created by Evgeny and assigned to Simon. + * + * Then: + * Two notifications should be delivered - one to Simon by Email and another to Evgeny by GTalk. + */ + @Test + public void scenario2() { + doReturn(true).when(manager).isEnabled(USER_EVGENY, gtalkChannel, commentOnReviewCreatedByMe); + doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); + + Notification notification = mock(Notification.class); + creator = USER_EVGENY; + assignee = USER_SIMON; + + manager.scheduleForSending(notification); + + verify(queue).add(USER_EVGENY, gtalkChannel); + verify(queue).add(USER_SIMON, emailChannel); + verifyNoMoreInteractions(queue); + } + + /** + * Given: + * Simon wants to receive notifications by Email and GTLak on comments for reviews assigned to him. + * + * When: + * Freddy adds comment to review created by Evgeny and assigned to Simon. + * + * Then: + * Two notifications should be delivered to Simon - one by Email and another by GTalk. + */ + @Test + public void scenario3() { + doReturn(true).when(manager).isEnabled(USER_SIMON, emailChannel, commentOnReviewAssignedToMe); + doReturn(true).when(manager).isEnabled(USER_SIMON, gtalkChannel, commentOnReviewAssignedToMe); + + Notification notification = mock(Notification.class); + creator = USER_EVGENY; + assignee = USER_SIMON; + + manager.scheduleForSending(notification); + + verify(queue).add(USER_SIMON, gtalkChannel); + verify(queue).add(USER_SIMON, emailChannel); + verifyNoMoreInteractions(queue); + } + + /** + * Given: + * Nobody wants to receive notifications. + * + * When: + * Freddy adds comment to review created by Evgeny and assigned to Simon. + * + * Then: + * No notifications. + */ + @Test + public void scenario4() { + Notification notification = mock(Notification.class); + creator = USER_EVGENY; + assignee = USER_SIMON; + + manager.scheduleForSending(notification); + + verifyNoMoreInteractions(queue); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java new file mode 100644 index 00000000000..7c1c0b03615 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/notifications/NotificationServiceTest.java @@ -0,0 +1,51 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications; + +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; + +public class NotificationServiceTest { + + private NotificationQueue queue; + private NotificationService service; + + @Before + public void setUp() { + queue = mock(NotificationQueue.class); + service = spy(new NotificationService(queue)); + service.setPeriod(10); + } + + @Test + public void shouldPeriodicallyProcessQueue() throws Exception { + service.start(); + + NotificationQueue.Element element = mock(NotificationQueue.Element.class); + when(queue.get()).thenReturn(element); + Thread.sleep(50); + + verify(service, atLeastOnce()).deliver(element); + + service.stop(); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java new file mode 100644 index 00000000000..4c9c3544f4d --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java @@ -0,0 +1,82 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.email; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Before; +import org.junit.Test; + +public class EmailNotificationChannelTest { + + private EmailNotificationChannel channel; + + @Before + public void setUp() { + channel = new EmailNotificationChannel(null); + } + + @Test + public void shouldCreateEmail() { + EmailMessage email = new EmailMessage() + .setMessageId("reviews/view/1") + .setFrom("Evgeny Mandrikov") + .setTo("simon.brandhof@sonarcource.com") + .setSubject("Review #3") + .setMessage("I'll take care of this violation."); + String expected = "" + + "Message-Id: \n" + + "In-Reply-To: \n" + + "References: \n" + + "List-Id: \n" + + "List-Archive: http://nemo.sonarsource.org\n" + + "From: Evgeny Mandrikov \n" + + "To: simon.brandhof@sonarcource.com\n" + + "Subject: Re: [Sonar] Review #3\n" + + "\n" + + "I'll take care of this violation.\n" + + "\n" + + "--\n" + + "View it in Sonar: http://nemo.sonarsource.org/reviews/view/1"; + String message = channel.create(email); + System.out.println(message); + assertThat(message, is(expected)); + } + + @Test + public void shouldCreateDefaultEmail() { + EmailMessage email = new EmailMessage() + .setTo("simon.brandhof@sonarcource.com") + .setMessage("Message"); + String expected = "" + + "List-Id: \n" + + "List-Archive: http://nemo.sonarsource.org\n" + + "From: Sonar \n" + + "To: simon.brandhof@sonarcource.com\n" + + "Subject: [Sonar] Notification\n" + + "\n" + + "Message\n"; + String message = channel.create(email); + System.out.println(message); + assertThat(message, is(expected)); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java new file mode 100644 index 00000000000..62c375c500c --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewAssignedToMeTest.java @@ -0,0 +1,61 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.database.model.User; +import org.sonar.jpa.entity.Review; +import org.sonar.server.notifications.NotificationDispatcher; + +public class CommentOnReviewAssignedToMeTest { + + private NotificationDispatcher.Context context; + private CommentOnReviewAssignedToMe dispatcher; + + @Before + public void setUp() { + context = mock(NotificationDispatcher.Context.class); + dispatcher = new CommentOnReviewAssignedToMe(); + } + + @Test + public void shouldDispatchToAssignee() { + CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review().setAssigneeId(1), new User(), "comment"); + dispatcher.dispatch(notification, context); + verify(context).addUser(1); + + notification = new CommentOnReviewNotification(new Review().setAssigneeId(2), new User(), "comment"); + dispatcher.dispatch(notification, context); + verify(context).addUser(2); + } + + @Test + public void shouldNotDispatchWhenNotAssigned() { + CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review(), new User(), "comment"); + dispatcher.dispatch(notification, context); + verifyNoMoreInteractions(context); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java new file mode 100644 index 00000000000..4f1c1766153 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/CommentOnReviewCreatedByMeTest.java @@ -0,0 +1,53 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.database.model.User; +import org.sonar.jpa.entity.Review; +import org.sonar.server.notifications.NotificationDispatcher; + +public class CommentOnReviewCreatedByMeTest { + + private NotificationDispatcher.Context context; + private CommentOnReviewCreatedByMe dispatcher; + + @Before + public void setUp() { + context = mock(NotificationDispatcher.Context.class); + dispatcher = new CommentOnReviewCreatedByMe(); + } + + @Test + public void shouldDispatchToCreator() { + CommentOnReviewNotification notification = new CommentOnReviewNotification(new Review().setUserId(1), new User(), "comment"); + dispatcher.dispatch(notification, context); + verify(context).addUser(1); + + notification = new CommentOnReviewNotification(new Review().setUserId(2), new User(), "comment"); + dispatcher.dispatch(notification, context); + verify(context).addUser(2); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java new file mode 100644 index 00000000000..3dd2e3e6f3b --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/notifications/reviews/ReviewsNotificationManagerTest.java @@ -0,0 +1,57 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.notifications.reviews; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.database.model.User; +import org.sonar.jpa.entity.Review; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +public class ReviewsNotificationManagerTest extends AbstractDbUnitTestCase { + + private ReviewsNotificationManager manager; + + @Before + public void setUp() { + setupData(getClass().getResourceAsStream("fixture.xml")); + manager = new ReviewsNotificationManager(getSessionFactory(), null); + } + + @Test + public void shouldGetReviewById() { + Review review = manager.getReviewById(3L); + assertThat(review.getUserId(), is(1)); + assertThat(review.getAssigneeId(), is(2)); + assertThat(review.getTitle(), is("Review #3")); + } + + @Test + public void shouldGetUserById() { + User user = manager.getUserById(1); + assertThat(user.getLogin(), is("simon")); + assertThat(user.getName(), is("Simon Brandhof")); + assertThat(user.getEmail(), is("simon.brandhof@sonarsource.com")); + } + +} diff --git a/sonar-server/src/test/resources/org/sonar/server/notifications/reviews/fixture.xml b/sonar-server/src/test/resources/org/sonar/server/notifications/reviews/fixture.xml new file mode 100644 index 00000000000..d9a64d90337 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/notifications/reviews/fixture.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3