public class Foo implements Language {
public static final String KEY = "foo";
- public static final String NAME = "foo";
+ public static final String NAME = "Foo";
@Override
public String getKey() {
public class Foo implements Language {
public static final String KEY = "foo";
- public static final String NAME = "foo";
+ public static final String NAME = "Foo";
@Override
public String getKey() {
import org.sonar.server.qualityprofile.BuiltInQProfileInsertImpl;
import org.sonar.server.qualityprofile.BuiltInQProfileLoader;
import org.sonar.server.qualityprofile.BuiltInQProfileUpdateImpl;
-import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification;
import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationDispatcher;
+import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationSender;
import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationTemplate;
import org.sonar.server.qualityprofile.RegisterQualityProfiles;
import org.sonar.server.rule.RegisterRules;
NotificationModule.class,
BuiltInQualityProfilesNotificationDispatcher.class,
BuiltInQualityProfilesNotificationTemplate.class,
- BuiltInQualityProfilesNotification.class,
+ BuiltInQualityProfilesNotificationSender.class,
BuiltInQProfileInsertImpl.class,
BuiltInQProfileUpdateImpl.class,
RegisterQualityProfiles.class,
package org.sonar.server.qualityprofile;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.IntStream;
import org.sonar.api.notifications.Notification;
-import org.sonar.server.notification.NotificationManager;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationSender.BUILT_IN_QUALITY_PROFILES;
public class BuiltInQualityProfilesNotification {
- static final String BUILT_IN_QUALITY_PROFILES = "built-in-quality-profiles";
+ private static final String NUMBER_OF_PROFILES = "numberOfProfiles";
+ private static final String PROFILE_NAME = ".profileName";
+ private static final String LANGUAGE = ".language";
+ private final List<Profile> profiles = new ArrayList<>();
- private final NotificationManager notificationManager;
+ public BuiltInQualityProfilesNotification addProfile(Profile profile) {
+ profiles.add(profile);
+ return this;
+ }
- public BuiltInQualityProfilesNotification(NotificationManager notificationManager) {
- this.notificationManager = notificationManager;
+ public Notification serialize() {
+ Notification notification = new Notification(BUILT_IN_QUALITY_PROFILES);
+ notification.setFieldValue(NUMBER_OF_PROFILES, String.valueOf(profiles.size()));
+ AtomicInteger count = new AtomicInteger();
+ profiles.forEach(profile -> {
+ int index = count.getAndIncrement();
+ notification.setFieldValue(index + ".profileName", profile.getProfileName());
+ notification.setFieldValue(index + ".language", profile.getLanguage());
+ });
+ return notification;
}
- void send() {
- notificationManager.scheduleForSending(
- new Notification(BUILT_IN_QUALITY_PROFILES)
- .setDefaultMessage("This is a test message from SonarQube"));
+ public static BuiltInQualityProfilesNotification parse(Notification notification) {
+ checkState(BUILT_IN_QUALITY_PROFILES.equals(notification.getType()),
+ "Expected notification of type %s but got %s", BUILT_IN_QUALITY_PROFILES, notification.getType());
+ BuiltInQualityProfilesNotification notif = new BuiltInQualityProfilesNotification();
+ String numberOfProfilesText = notification.getFieldValue(NUMBER_OF_PROFILES);
+ checkState(numberOfProfilesText != null, "Could not read the built-in quality profile notification");
+ Integer numberOfProfiles = Integer.valueOf(numberOfProfilesText);
+ IntStream.rangeClosed(0, numberOfProfiles - 1)
+ .mapToObj(index -> new Profile(
+ requireNonNull(notification.getFieldValue(index + PROFILE_NAME)),
+ requireNonNull(notification.getFieldValue(index + LANGUAGE))))
+ .forEach(notif::addProfile);
+ return notif;
+ }
+
+ public List<Profile> getProfiles() {
+ return profiles;
+ }
+
+ public static class Profile {
+ private final String profileName;
+ private final String language;
+
+ public Profile(String profileName, String language) {
+ this.profileName = profileName;
+ this.language = language;
+ }
+
+ public String getProfileName() {
+ return profileName;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
}
}
import org.sonar.server.notification.NotificationDispatcher;
import org.sonar.server.notification.email.EmailNotificationChannel;
-import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.BUILT_IN_QUALITY_PROFILES;
+import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationSender.BUILT_IN_QUALITY_PROFILES;
public class BuiltInQualityProfilesNotificationDispatcher extends NotificationDispatcher {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.qualityprofile;
+
+import java.util.List;
+import org.sonar.api.resources.Languages;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
+
+public class BuiltInQualityProfilesNotificationSender {
+
+ static final String BUILT_IN_QUALITY_PROFILES = "built-in-quality-profiles";
+
+ private final NotificationManager notificationManager;
+ private final Languages languages;
+
+ public BuiltInQualityProfilesNotificationSender(NotificationManager notificationManager, Languages languages) {
+ this.notificationManager = notificationManager;
+ this.languages = languages;
+ }
+
+ void send(List<QProfileName> changedProfiles) {
+ BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification();
+ changedProfiles.stream()
+ .map(changedProfile -> {
+ String profileName = changedProfile.getName();
+ String languageName = languages.get(changedProfile.getLanguage()).getName();
+ return new Profile(profileName, languageName);
+ })
+ .forEach(notification::addProfile);
+ notificationManager.scheduleForSending(notification.serialize());
+ }
+}
*/
package org.sonar.server.qualityprofile;
+import java.util.Comparator;
import org.sonar.api.notifications.Notification;
import org.sonar.plugins.emailnotifications.api.EmailMessage;
import org.sonar.plugins.emailnotifications.api.EmailTemplate;
+import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
-import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.BUILT_IN_QUALITY_PROFILES;
+import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationSender.BUILT_IN_QUALITY_PROFILES;
public class BuiltInQualityProfilesNotificationTemplate extends EmailTemplate {
if (!BUILT_IN_QUALITY_PROFILES.equals(notification.getType())) {
return null;
}
+
+ BuiltInQualityProfilesNotification profilesNotification = BuiltInQualityProfilesNotification.parse(notification);
+
+ StringBuilder message = new StringBuilder("Built-in quality profiles have been updated:\n");
+ profilesNotification.getProfiles().stream()
+ .sorted(Comparator.comparing(Profile::getLanguage).thenComparing(Profile::getProfileName))
+ .forEach(profile -> message.append("\"").append(profile.getProfileName()).append("\" - ").append(profile.getLanguage()).append("\n"));
+
+ message.append(
+ "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
+
// And finally return the email that will be sent
return new EmailMessage()
.setMessageId(BUILT_IN_QUALITY_PROFILES)
.setSubject("empty")
- .setMessage("This is a test message from SonarQube");
+ .setMessage(message.toString());
}
}
private final DbClient dbClient;
private final BuiltInQProfileInsert builtInQProfileInsert;
private final BuiltInQProfileUpdate builtInQProfileUpdate;
- private final BuiltInQualityProfilesNotification builtInQualityProfilesNotification;
+ private final BuiltInQualityProfilesNotificationSender builtInQualityProfilesNotification;
public RegisterQualityProfiles(BuiltInQProfileRepository builtInQProfileRepository,
DbClient dbClient, BuiltInQProfileInsert builtInQProfileInsert, BuiltInQProfileUpdate builtInQProfileUpdate,
- BuiltInQualityProfilesNotification builtInQualityProfilesNotification) {
+ BuiltInQualityProfilesNotificationSender builtInQualityProfilesNotification) {
this.builtInQProfileRepository = builtInQProfileRepository;
this.dbClient = dbClient;
this.builtInQProfileInsert = builtInQProfileInsert;
Map<QProfileName, RulesProfileDto> persistedRuleProfiles = loadPersistedProfiles(dbSession);
- List<ActiveRuleChange> changes = new ArrayList<>();
+ List<QProfileName> changedProfiles = new ArrayList<>();
builtInQProfiles.forEach(builtIn -> {
RulesProfileDto ruleProfile = persistedRuleProfiles.get(builtIn.getQProfileName());
if (ruleProfile == null) {
register(dbSession, batchDbSession, builtIn);
} else {
- changes.addAll(update(dbSession, builtIn, ruleProfile));
+ List<ActiveRuleChange> changes = update(dbSession, builtIn, ruleProfile);
+ if (!changes.isEmpty()) {
+ changedProfiles.add(builtIn.getQProfileName());
+ }
}
});
- if (!changes.isEmpty()) {
- builtInQualityProfilesNotification.send();
+ if (!changedProfiles.isEmpty()) {
+ builtInQualityProfilesNotification.send(changedProfiles);
}
}
profiler.stopDebug();
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.qualityprofile;
+
+import java.util.List;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.resources.Languages;
+import org.sonar.server.language.LanguageTesting;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
+
+import static java.util.Collections.singletonList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.assertj.core.api.Java6Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+public class BuiltInQualityProfilesNotificationSenderTest {
+
+ private NotificationManager notificationManager = mock(NotificationManager.class);
+
+ @Test
+ public void add_profile_to_notification() throws Exception {
+ String profileName = randomLowerCaseText();
+ String languageKey = randomLowerCaseText();
+ String languageName = randomLowerCaseText();
+ List<QProfileName> profileNames = singletonList(new QProfileName(languageKey, profileName));
+ Languages languages = new Languages(LanguageTesting.newLanguage(languageKey, languageName));
+
+ BuiltInQualityProfilesNotificationSender underTest = new BuiltInQualityProfilesNotificationSender(notificationManager, languages);
+ underTest.send(profileNames);
+
+ ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
+ verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture());
+ verifyNoMoreInteractions(notificationManager);
+ assertThat(BuiltInQualityProfilesNotification.parse(notificationArgumentCaptor.getValue()).getProfiles())
+ .extracting(Profile::getProfileName, Profile::getLanguage)
+ .containsExactlyInAnyOrder(tuple(profileName, languageName));
+ }
+
+ private static String randomLowerCaseText() {
+ return randomAlphanumeric(20).toLowerCase();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.qualityprofile;
+
+import org.junit.Test;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BuiltInQualityProfilesNotificationTemplateTest {
+
+ private BuiltInQualityProfilesNotificationTemplate underTest = new BuiltInQualityProfilesNotificationTemplate();
+
+ @Test
+ public void notification_contains_list_of_quality_profiles() {
+ String profileName = randomAlphanumeric(20);
+ String language = randomAlphanumeric(20);
+ BuiltInQualityProfilesNotification notification = new BuiltInQualityProfilesNotification()
+ .addProfile(new Profile(profileName, language));
+
+ EmailMessage emailMessage = underTest.format(notification.serialize());
+
+ assertThat(emailMessage.getMessage()).isEqualTo("Built-in quality profiles have been updated:\n" +
+ "\"" + profileName + "\" - " + language + "\n" +
+ "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.qualityprofile;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.notifications.Notification;
+import org.sonar.server.qualityprofile.BuiltInQualityProfilesNotification.Profile;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.server.qualityprofile.BuiltInQualityProfilesNotificationSender.BUILT_IN_QUALITY_PROFILES;
+
+public class BuiltInQualityProfilesNotificationTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void serialize_and_parse_no_profile() {
+ Notification notification = new BuiltInQualityProfilesNotification().serialize();
+
+ BuiltInQualityProfilesNotification result = BuiltInQualityProfilesNotification.parse(notification);
+
+ assertThat(result.getProfiles()).isEmpty();
+ }
+
+ @Test
+ public void serialize_and_parse_single_profile() {
+ String profileName = randomAlphanumeric(20);
+ String language = randomAlphanumeric(20);
+
+ Notification notification = new BuiltInQualityProfilesNotification().addProfile(new Profile(profileName, language)).serialize();
+ BuiltInQualityProfilesNotification result = BuiltInQualityProfilesNotification.parse(notification);
+
+ assertThat(result.getProfiles()).extracting(Profile::getProfileName, Profile::getLanguage)
+ .containsExactlyInAnyOrder(tuple(profileName, language));
+ }
+
+ @Test
+ public void serialize_and_parse_multiple_profiles() {
+ String profileName1 = randomAlphanumeric(20);
+ String language1 = randomAlphanumeric(20);
+ String profileName2 = randomAlphanumeric(20);
+ String language2 = randomAlphanumeric(20);
+
+ Notification notification = new BuiltInQualityProfilesNotification()
+ .addProfile(new Profile(profileName1, language1))
+ .addProfile(new Profile(profileName2, language2))
+ .serialize();
+ BuiltInQualityProfilesNotification result = BuiltInQualityProfilesNotification.parse(notification);
+
+ assertThat(result.getProfiles()).extracting(Profile::getProfileName, Profile::getLanguage)
+ .containsExactlyInAnyOrder(tuple(profileName1, language1), tuple(profileName2, language2));
+ }
+
+ @Test
+ public void fail_with_ISE_when_parsing_empty_notification() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Could not read the built-in quality profile notification");
+
+ BuiltInQualityProfilesNotification.parse(new Notification(BUILT_IN_QUALITY_PROFILES));
+ }
+}
package org.sonar.server.qualityprofile;
import java.util.Arrays;
+import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.RulePriority;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.apache.commons.lang.math.RandomUtils.nextLong;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, mock(RuleIndex.class), new RuleActivatorContextFactory(dbClient), typeValidations, activeRuleIndexer,
userSessionRule);
private BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer);
- private BuiltInQualityProfilesNotification builtInQualityProfilesNotification = mock(BuiltInQualityProfilesNotification.class);
+ private BuiltInQualityProfilesNotificationSender builtInQualityProfilesNotification = mock(BuiltInQualityProfilesNotificationSender.class);
private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient,
builtInQProfileInsert, builtInQProfileUpdate, builtInQualityProfilesNotification);
underTest.start();
- verify(builtInQualityProfilesNotification).send();
+ ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+ verify(builtInQualityProfilesNotification).send(captor.capture());
+ List<QProfileName> updatedProfiles = captor.<List<QProfileName>>getValue();
+ assertThat(updatedProfiles)
+ .extracting(QProfileName::getName, QProfileName::getLanguage)
+ .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
}
@Test
underTest.start();
- verify(builtInQualityProfilesNotification).send();
+ ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+ verify(builtInQualityProfilesNotification).send(captor.capture());
+ List<QProfileName> updatedProfiles = captor.<List<QProfileName>>getValue();
+ assertThat(updatedProfiles)
+ .extracting(QProfileName::getName, QProfileName::getLanguage)
+ .containsExactlyInAnyOrder(
+ tuple(dbProfile1.getName(), dbProfile1.getLanguage()),
+ tuple(dbProfile2.getName(), dbProfile2.getLanguage())
+ );
}
private void addPluginProfile(RulesProfileDto dbProfile, RuleDefinitionDto... dbRules) {
private DbClient dbClient = db.getDbClient();
private DummyBuiltInQProfileInsert insert = new DummyBuiltInQProfileInsert();
private DummyBuiltInQProfileUpdate update = new DummyBuiltInQProfileUpdate();
- private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient, insert, update, mock(BuiltInQualityProfilesNotification.class));
+ private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient, insert, update, mock(BuiltInQualityProfilesNotificationSender.class));
@Test
public void start_fails_if_BuiltInQProfileRepository_has_not_been_initialized() {
public class Foo implements Language {
public static final String KEY = "foo";
- public static final String NAME = "foo";
+ public static final String NAME = "Foo";
@Override
public String getKey() {
public class Foo implements Language {
public static final String KEY = "foo";
- public static final String NAME = "foo";
+ public static final String NAME = "Foo";
@Override
public String getKey() {
import com.sonar.orchestrator.Orchestrator;
import java.io.File;
+import java.util.List;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.junit.After;
orchestrator.restartServer();
waitUntilAllNotificationsAreDelivered(2, 10, 1_000);
- assertThat(smtpServer.getMessages())
+ List<WiserMessage> messages = smtpServer.getMessages();
+ assertThat(messages)
.extracting(this::getMimeMessage)
.extracting(this::getAllRecipients)
.containsOnly("<" + profileAdmin1.getEmail() + ">", "<" + profileAdmin2.getEmail() + ">");
- assertThat(smtpServer.getMessages())
- .extracting(this::getMimeMessage)
- .extracting(this::getContent)
- .extracting(m -> m.contains("This is a test message from SonarQube"))
- .containsOnly(true);
+ assertThat(messages.get(0).getMimeMessage().getContent().toString())
+ .containsSequence(
+ "Built-in quality profiles have been updated:",
+ "\"Basic\" - Foo",
+ "This is a good time to review your quality profiles and update them to benefit from the latest evolutions.")
+ .isEqualTo(messages.get(1).getMimeMessage().getContent().toString());
}
private MimeMessage getMimeMessage(WiserMessage msg) {