diff options
author | Lukasz Jarocki <lukasz.jarocki@sonarsource.com> | 2022-02-08 13:06:37 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-02-18 15:48:04 +0000 |
commit | 90d9a31aa2feb59ce8546fede2721892473ec993 (patch) | |
tree | d654a9a53c47f08770e2f063a25ef0b5ff20d5d0 /server/sonar-webserver-auth/src | |
parent | 0c8ff1bac53c2dfa89e7be014b64b411f24434c0 (diff) | |
download | sonarqube-90d9a31aa2feb59ce8546fede2721892473ec993.tar.gz sonarqube-90d9a31aa2feb59ce8546fede2721892473ec993.zip |
SONAR-15919 added filtering rule set change events based on languages user subscribed to
Diffstat (limited to 'server/sonar-webserver-auth/src')
33 files changed, 0 insertions, 4933 deletions
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotification.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotification.java deleted file mode 100644 index df8d85c816a..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotification.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.sonar.api.notifications.Notification; - -public class BuiltInQPChangeNotification extends Notification { - static final String TYPE = "built-in-quality-profiles"; - - public BuiltInQPChangeNotification() { - super(TYPE); - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationBuilder.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationBuilder.java deleted file mode 100644 index 7de478f2e9b..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationBuilder.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.IntStream; -import org.sonar.api.notifications.Notification; - -import static com.google.common.base.Preconditions.checkState; -import static java.lang.Integer.parseInt; -import static java.lang.Long.parseLong; -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; - -public class BuiltInQPChangeNotificationBuilder { - - private static final String NUMBER_OF_PROFILES = "numberOfProfiles"; - private static final String PROFILE_NAME = ".profileName"; - private static final String LANGUAGE_KEY = ".languageKey"; - private static final String LANGUAGE_NAME = ".languageName"; - private static final String NEW_RULES = ".newRules"; - private static final String UPDATED_RULES = ".updatedRules"; - private static final String REMOVED_RULES = ".removedRules"; - private static final String START_DATE = ".startDate"; - private static final String END_DATE = ".endDate"; - - private final List<Profile> profiles = new ArrayList<>(); - - public BuiltInQPChangeNotificationBuilder addProfile(Profile profile) { - profiles.add(profile); - return this; - } - - public BuiltInQPChangeNotification build() { - BuiltInQPChangeNotification notification = new BuiltInQPChangeNotification(); - notification.setFieldValue(NUMBER_OF_PROFILES, String.valueOf(profiles.size())); - AtomicInteger count = new AtomicInteger(); - profiles.forEach(profile -> { - int index = count.getAndIncrement(); - notification.setFieldValue(index + PROFILE_NAME, profile.getProfileName()); - notification.setFieldValue(index + LANGUAGE_KEY, profile.getLanguageKey()); - notification.setFieldValue(index + LANGUAGE_NAME, profile.getLanguageName()); - notification.setFieldValue(index + NEW_RULES, String.valueOf(profile.getNewRules())); - notification.setFieldValue(index + UPDATED_RULES, String.valueOf(profile.getUpdatedRules())); - notification.setFieldValue(index + REMOVED_RULES, String.valueOf(profile.getRemovedRules())); - notification.setFieldValue(index + START_DATE, String.valueOf(profile.getStartDate())); - notification.setFieldValue(index + END_DATE, String.valueOf(profile.getEndDate())); - }); - return notification; - } - - public static BuiltInQPChangeNotificationBuilder parse(Notification notification) { - checkState(BuiltInQPChangeNotification.TYPE.equals(notification.getType()), - "Expected notification of type %s but got %s", BuiltInQPChangeNotification.TYPE, notification.getType()); - BuiltInQPChangeNotificationBuilder notif = new BuiltInQPChangeNotificationBuilder(); - 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 -> Profile.newBuilder() - .setProfileName(getNonNullFieldValue(notification, index + PROFILE_NAME)) - .setLanguageKey(getNonNullFieldValue(notification, index + LANGUAGE_KEY)) - .setLanguageName(getNonNullFieldValue(notification, index + LANGUAGE_NAME)) - .setNewRules(parseInt(getNonNullFieldValue(notification, index + NEW_RULES))) - .setUpdatedRules(parseInt(getNonNullFieldValue(notification, index + UPDATED_RULES))) - .setRemovedRules(parseInt(getNonNullFieldValue(notification, index + REMOVED_RULES))) - .setStartDate(parseLong(getNonNullFieldValue(notification, index + START_DATE))) - .setEndDate(parseLong(getNonNullFieldValue(notification, index + END_DATE))) - .build()) - .forEach(notif::addProfile); - return notif; - } - - private static String getNonNullFieldValue(Notification notification, String key) { - String value = notification.getFieldValue(key); - return requireNonNull(value, format("Notification field '%s' is null", key)); - } - - public List<Profile> getProfiles() { - return profiles; - } - - public static class Profile { - private final String profileName; - private final String languageKey; - private final String languageName; - private final int newRules; - private final int updatedRules; - private final int removedRules; - private final long startDate; - private final long endDate; - - public Profile(Builder builder) { - this.profileName = builder.profileName; - this.languageKey = builder.languageKey; - this.languageName = builder.languageName; - this.newRules = builder.newRules; - this.updatedRules = builder.updatedRules; - this.removedRules = builder.removedRules; - this.startDate = builder.startDate; - this.endDate = builder.endDate; - } - - public String getProfileName() { - return profileName; - } - - public String getLanguageKey() { - return languageKey; - } - - public String getLanguageName() { - return languageName; - } - - public int getNewRules() { - return newRules; - } - - public int getUpdatedRules() { - return updatedRules; - } - - public int getRemovedRules() { - return removedRules; - } - - public long getStartDate() { - return startDate; - } - - public long getEndDate() { - return endDate; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public static class Builder { - private String profileName; - private String languageKey; - private String languageName; - private int newRules; - private int updatedRules; - private int removedRules; - private long startDate; - private long endDate; - - private Builder() { - } - - public Builder setLanguageKey(String languageKey) { - this.languageKey = requireNonNull(languageKey, "languageKEy should not be null"); - return this; - } - - public Builder setLanguageName(String languageName) { - this.languageName = requireNonNull(languageName, "languageName should not be null"); - return this; - } - - public Builder setProfileName(String profileName) { - this.profileName = requireNonNull(profileName, "profileName should not be null"); - return this; - } - - public Builder setNewRules(int newRules) { - checkState(newRules >= 0, "newRules should not be negative"); - this.newRules = newRules; - return this; - } - - public Builder setUpdatedRules(int updatedRules) { - checkState(updatedRules >= 0, "updatedRules should not be negative"); - this.updatedRules = updatedRules; - return this; - } - - public Builder setRemovedRules(int removedRules) { - checkState(removedRules >= 0, "removedRules should not be negative"); - this.removedRules = removedRules; - return this; - } - - public Builder setStartDate(long startDate) { - this.startDate = startDate; - return this; - } - - public Builder setEndDate(long endDate) { - this.endDate = endDate; - return this; - } - - public Profile build() { - return new Profile(this); - } - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationHandler.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationHandler.java deleted file mode 100644 index 5402ead1042..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Collection; -import java.util.Optional; -import java.util.Set; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.server.notification.EmailNotificationHandler; -import org.sonar.server.notification.NotificationDispatcherMetadata; -import org.sonar.server.notification.email.EmailNotificationChannel; -import org.sonar.server.notification.email.EmailNotificationChannel.EmailDeliveryRequest; - -import static org.sonar.core.util.stream.MoreCollectors.toSet; - -public class BuiltInQPChangeNotificationHandler extends EmailNotificationHandler<BuiltInQPChangeNotification> { - private final DbClient dbClient; - - public BuiltInQPChangeNotificationHandler(DbClient dbClient, EmailNotificationChannel emailNotificationChannel) { - super(emailNotificationChannel); - this.dbClient = dbClient; - } - - @Override - public Optional<NotificationDispatcherMetadata> getMetadata() { - return Optional.empty(); - } - - @Override - public Class<BuiltInQPChangeNotification> getNotificationClass() { - return BuiltInQPChangeNotification.class; - } - - @Override - public Set<EmailDeliveryRequest> toEmailDeliveryRequests(Collection<BuiltInQPChangeNotification> notifications) { - try (DbSession session = dbClient.openSession(false)) { - return dbClient.authorizationDao() - .selectQualityProfileAdministratorLogins(session) - .stream() - .flatMap(t -> notifications.stream().map(notification -> new EmailDeliveryRequest(t.getEmail(), notification))) - .collect(toSet()); - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTemplate.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTemplate.java deleted file mode 100644 index bd1096664a2..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTemplate.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Comparator; -import java.util.Date; -import javax.annotation.CheckForNull; -import org.sonar.api.notifications.Notification; -import org.sonar.api.platform.Server; -import org.sonar.server.issue.notification.EmailMessage; -import org.sonar.server.issue.notification.EmailTemplate; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.sonar.api.utils.DateUtils.formatDate; -import static org.sonar.server.qualityprofile.BuiltInQPChangeNotificationBuilder.Profile; -import static org.sonar.server.qualityprofile.BuiltInQPChangeNotificationBuilder.parse; - -public class BuiltInQPChangeNotificationTemplate implements EmailTemplate { - - private final Server server; - - public BuiltInQPChangeNotificationTemplate(Server server) { - this.server = server; - } - - @Override - @CheckForNull - public EmailMessage format(Notification notification) { - if (!BuiltInQPChangeNotification.TYPE.equals(notification.getType())) { - return null; - } - - BuiltInQPChangeNotificationBuilder profilesNotification = parse(notification); - StringBuilder message = new StringBuilder("The following built-in profiles have been updated:\n\n"); - profilesNotification.getProfiles().stream() - .sorted(Comparator.comparing(Profile::getLanguageName).thenComparing(Profile::getProfileName)) - .forEach(profile -> { - message.append("\"") - .append(profile.getProfileName()) - .append("\" - ") - .append(profile.getLanguageName()) - .append(": ") - .append(server.getPublicRootUrl()).append("/profiles/changelog?language=") - .append(profile.getLanguageKey()) - .append("&name=") - .append(encode(profile.getProfileName())) - .append("&since=") - .append(formatDate(new Date(profile.getStartDate()))) - .append("&to=") - .append(formatDate(new Date(profile.getEndDate()))) - .append("\n"); - int newRules = profile.getNewRules(); - if (newRules > 0) { - message.append(" ").append(newRules).append(" new rule") - .append(plural(newRules)) - .append('\n'); - } - int updatedRules = profile.getUpdatedRules(); - if (updatedRules > 0) { - message.append(" ").append(updatedRules).append(" rule") - .append(updatedRules > 1 ? "s have been updated" : " has been updated") - .append("\n"); - } - int removedRules = profile.getRemovedRules(); - if (removedRules > 0) { - message.append(" ").append(removedRules).append(" rule") - .append(plural(removedRules)) - .append(" removed\n"); - } - message.append("\n"); - }); - - message.append("This is a good time to review your quality profiles and update them to benefit from the latest evolutions: "); - message.append(server.getPublicRootUrl()).append("/profiles"); - - // And finally return the email that will be sent - return new EmailMessage() - .setMessageId(BuiltInQPChangeNotification.TYPE) - .setSubject("Built-in quality profiles have been updated") - .setPlainTextMessage(message.toString()); - } - - private static String plural(int count) { - return count > 1 ? "s" : ""; - } - - public String encode(String text) { - try { - return URLEncoder.encode(text, UTF_8.name()); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(String.format("Cannot encode %s", text), e); - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java deleted file mode 100644 index 866c7dc0801..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; - -import static org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule; -import static org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.OverriddenParam; - -/** - * Represent a Quality Profile as computed from {@link BuiltInQualityProfilesDefinition} provided by installed plugins. - */ -@Immutable -public final class BuiltInQProfile { - private final QProfileName qProfileName; - private final boolean isDefault; - private final List<ActiveRule> activeRules; - - private BuiltInQProfile(Builder builder) { - this.qProfileName = new QProfileName(builder.language, builder.name); - this.isDefault = builder.declaredDefault || builder.computedDefault; - this.activeRules = ImmutableList.copyOf(builder.activeRules); - } - - public String getName() { - return qProfileName.getName(); - } - - public String getLanguage() { - return qProfileName.getLanguage(); - } - - public QProfileName getQProfileName() { - return qProfileName; - } - - public boolean isDefault() { - return isDefault; - } - - public List<ActiveRule> getActiveRules() { - return activeRules; - } - - static final class ActiveRule { - private final String ruleUuid; - private final RuleKey ruleKey; - private final String severity; - private final List<OverriddenParam> params; - - ActiveRule(String ruleUuid, BuiltInActiveRule builtIn) { - this(ruleUuid, RuleKey.of(builtIn.repoKey(), builtIn.ruleKey()), builtIn.overriddenSeverity(), builtIn.overriddenParams()); - } - - ActiveRule(String ruleUuid, RuleKey ruleKey, @Nullable String severity, List<OverriddenParam> params) { - this.ruleUuid = ruleUuid; - this.ruleKey = ruleKey; - this.severity = severity; - this.params = params; - } - - public String getRuleUuid() { - return ruleUuid; - } - - public RuleKey getRuleKey() { - return ruleKey; - } - - @CheckForNull - public String getSeverity() { - return severity; - } - - public List<OverriddenParam> getParams() { - return params; - } - } - - static final class Builder { - private String language; - private String name; - private boolean declaredDefault; - private boolean computedDefault; - private final List<ActiveRule> activeRules = new ArrayList<>(); - - public Builder setLanguage(String language) { - this.language = language; - return this; - } - - Builder setName(String name) { - this.name = name; - return this; - } - - String getName() { - return name; - } - - Builder setDeclaredDefault(boolean declaredDefault) { - this.declaredDefault = declaredDefault; - return this; - } - - boolean isDeclaredDefault() { - return declaredDefault; - } - - Builder setComputedDefault(boolean flag) { - computedDefault = flag; - return this; - } - - Builder addRule(ActiveRule activeRule) { - this.activeRules.add(activeRule); - return this; - } - - BuiltInQProfile build() { - return new BuiltInQProfile(this); - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsert.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsert.java deleted file mode 100644 index 7cfd2d28faf..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsert.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.sonar.db.DbSession; - -public interface BuiltInQProfileInsert { - /** - * Persist a new built-in profile - * Db sessions are committed and Elasticsearch indices are updated - */ - void create(DbSession batchSession, BuiltInQProfile builtInQProfile); -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java deleted file mode 100644 index d4aad4ffbbf..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.base.Splitter; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.api.server.rule.RuleParamType; -import org.sonar.api.utils.System2; -import org.sonar.core.util.UuidFactory; -import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.DefaultQProfileDto; -import org.sonar.db.qualityprofile.OrgQProfileDto; -import org.sonar.db.qualityprofile.RulesProfileDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; -import org.sonar.server.rule.ServerRuleFinder; -import org.sonar.server.util.TypeValidations; - -import static com.google.common.base.MoreObjects.firstNonNull; -import static com.google.common.collect.Lists.newArrayList; -import static java.util.Collections.emptySet; -import static java.util.Objects.requireNonNull; - -public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert { - private final DbClient dbClient; - private final ServerRuleFinder ruleFinder; - private final System2 system2; - private final UuidFactory uuidFactory; - private final TypeValidations typeValidations; - private final ActiveRuleIndexer activeRuleIndexer; - private RuleRepository ruleRepository; - - public BuiltInQProfileInsertImpl(DbClient dbClient, ServerRuleFinder ruleFinder, System2 system2, UuidFactory uuidFactory, - TypeValidations typeValidations, ActiveRuleIndexer activeRuleIndexer) { - this.dbClient = dbClient; - this.ruleFinder = ruleFinder; - this.system2 = system2; - this.uuidFactory = uuidFactory; - this.typeValidations = typeValidations; - this.activeRuleIndexer = activeRuleIndexer; - } - - @Override - public void create(DbSession batchDbSession, BuiltInQProfile builtInQProfile) { - initRuleRepository(batchDbSession); - - Date now = new Date(system2.now()); - RulesProfileDto ruleProfile = insertRulesProfile(batchDbSession, builtInQProfile, now); - - List<ActiveRuleChange> changes = builtInQProfile.getActiveRules().stream() - .map(activeRule -> insertActiveRule(batchDbSession, ruleProfile, activeRule, now.getTime())) - .collect(MoreCollectors.toList()); - - changes.forEach(change -> dbClient.qProfileChangeDao().insert(batchDbSession, change.toDto(null))); - - createDefaultAndOrgQProfiles(batchDbSession, builtInQProfile, ruleProfile); - - activeRuleIndexer.commitAndIndex(batchDbSession, changes); - } - - - private void createDefaultAndOrgQProfiles(DbSession batchDbSession, BuiltInQProfile builtIn, RulesProfileDto rulesProfileDto) { - Optional<String> qProfileUuid = dbClient.defaultQProfileDao().selectDefaultQProfileUuid(batchDbSession, builtIn.getLanguage()); - - OrgQProfileDto dto = new OrgQProfileDto() - .setRulesProfileUuid(rulesProfileDto.getUuid()) - .setUuid(uuidFactory.create()); - - if (builtIn.isDefault() && qProfileUuid.isEmpty()) { - DefaultQProfileDto defaultQProfileDto = new DefaultQProfileDto() - .setQProfileUuid(dto.getUuid()) - .setLanguage(builtIn.getLanguage()); - dbClient.defaultQProfileDao().insert(batchDbSession, defaultQProfileDto); - } - - dbClient.qualityProfileDao().insert(batchDbSession, dto); - } - - private void initRuleRepository(DbSession dbSession) { - if (ruleRepository == null) { - ruleRepository = new RuleRepository(dbClient, dbSession, ruleFinder); - } - } - - private RulesProfileDto insertRulesProfile(DbSession dbSession, BuiltInQProfile builtIn, Date now) { - RulesProfileDto dto = new RulesProfileDto() - .setUuid(uuidFactory.create()) - .setName(builtIn.getName()) - .setLanguage(builtIn.getLanguage()) - .setIsBuiltIn(true) - .setRulesUpdatedAtAsDate(now); - dbClient.qualityProfileDao().insert(dbSession, dto); - return dto; - } - - private ActiveRuleChange insertActiveRule(DbSession batchDbSession, RulesProfileDto rulesProfileDto, BuiltInQProfile.ActiveRule activeRule, long now) { - RuleKey ruleKey = activeRule.getRuleKey(); - RuleDefinitionDto ruleDefinitionDto = ruleRepository.getDefinition(ruleKey) - .orElseThrow(() -> new IllegalStateException("RuleDefinition not found for key " + ruleKey)); - - ActiveRuleDto dto = new ActiveRuleDto(); - dto.setProfileUuid(rulesProfileDto.getUuid()); - dto.setRuleUuid(ruleDefinitionDto.getUuid()); - dto.setKey(ActiveRuleKey.of(rulesProfileDto, ruleDefinitionDto.getKey())); - dto.setSeverity(firstNonNull(activeRule.getSeverity(), ruleDefinitionDto.getSeverityString())); - dto.setUpdatedAt(now); - dto.setCreatedAt(now); - dbClient.activeRuleDao().insert(batchDbSession, dto); - - List<ActiveRuleParamDto> paramDtos = insertActiveRuleParams(batchDbSession, activeRule, dto); - - ActiveRuleChange change = new ActiveRuleChange(ActiveRuleChange.Type.ACTIVATED, dto, ruleDefinitionDto); - change.setSeverity(dto.getSeverityString()); - paramDtos.forEach(paramDto -> change.setParameter(paramDto.getKey(), paramDto.getValue())); - return change; - } - - private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, BuiltInQProfile.ActiveRule activeRule, ActiveRuleDto activeRuleDto) { - Map<String, String> valuesByParamKey = activeRule.getParams().stream() - .collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue)); - List<ActiveRuleParamDto> rules = ruleRepository.getRuleParams(activeRule.getRuleKey()).stream() - .map(param -> createParamDto(param, Optional.ofNullable(valuesByParamKey.get(param.getName())).orElse(param.getDefaultValue()))) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - rules.forEach(paramDto -> dbClient.activeRuleDao().insertParam(session, activeRuleDto, paramDto)); - return rules; - } - - @CheckForNull - private ActiveRuleParamDto createParamDto(RuleParamDto param, @Nullable String value) { - if (value == null) { - return null; - } - ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(param); - paramDto.setValue(validateParam(param, value)); - return paramDto; - } - - private String validateParam(RuleParamDto ruleParam, String value) { - RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType()); - if (ruleParamType.multiple()) { - List<String> values = newArrayList(Splitter.on(",").split(value)); - typeValidations.validate(values, ruleParamType.type(), ruleParamType.values()); - } else { - typeValidations.validate(value, ruleParamType.type(), ruleParamType.values()); - } - return value; - } - - private static class RuleRepository { - private final Map<RuleKey, Set<RuleParamDto>> params; - private final ServerRuleFinder ruleFinder; - - private RuleRepository(DbClient dbClient, DbSession session, ServerRuleFinder ruleFinder) { - this.ruleFinder = ruleFinder; - this.params = new HashMap<>(); - - for (RuleParamDto ruleParam : dbClient.ruleDao().selectAllRuleParams(session)) { - Optional<RuleKey> ruleKey = ruleFinder.findDtoByUuid(ruleParam.getRuleUuid()) - .map(r -> RuleKey.of(r.getRepositoryKey(), r.getRuleKey())); - - if (ruleKey.isPresent()) { - params.computeIfAbsent(ruleKey.get(), r -> new HashSet<>()).add(ruleParam); - } - } - } - - private Optional<RuleDefinitionDto> getDefinition(RuleKey ruleKey) { - return ruleFinder.findDtoByKey(requireNonNull(ruleKey, "RuleKey can't be null")); - } - - private Set<RuleParamDto> getRuleParams(RuleKey ruleKey) { - return params.getOrDefault(requireNonNull(ruleKey, "RuleKey can't be null"), emptySet()); - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileLoader.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileLoader.java deleted file mode 100644 index b97571648de..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileLoader.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.picocontainer.Startable; - -/** - * Startable added to {@link org.sonar.server.platform.platformlevel.PlatformLevelStartup} responsible for initializing - * {@link BuiltInQProfileRepository}. - */ -public class BuiltInQProfileLoader implements Startable { - private final BuiltInQProfileRepository builtInQProfileRepository; - - public BuiltInQProfileLoader(BuiltInQProfileRepository builtInQProfileRepository) { - this.builtInQProfileRepository = builtInQProfileRepository; - } - - @Override - public void start() { - builtInQProfileRepository.initialize(); - } - - @Override - public void stop() { - // nothing to do - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepository.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepository.java deleted file mode 100644 index 72db5b95b49..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepository.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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; - -public interface BuiltInQProfileRepository { - /** - * Initializes the Repository. - * - * This method is intended to be called from a startup task - * (see {@link org.sonar.server.platform.platformlevel.PlatformLevelStartup}). - * - * @throws IllegalStateException if called more then once - */ - void initialize(); - - /** - * @return an immutable list - * - * @throws IllegalStateException if {@link #initialize()} has not been called - */ - List<BuiltInQProfile> get(); -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java deleted file mode 100644 index 57ca9b115ae..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.collect.ImmutableList; -import com.google.common.collect.Multimap; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nullable; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.api.utils.log.Profiler; -import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.rule.DeprecatedRuleKeyDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.server.rule.ServerRuleFinder; - -import static com.google.common.base.Preconditions.checkState; - -public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository { - private static final Logger LOGGER = Loggers.get(BuiltInQProfileRepositoryImpl.class); - private static final String DEFAULT_PROFILE_NAME = "Sonar way"; - - private final DbClient dbClient; - private final ServerRuleFinder ruleFinder; - private final Languages languages; - private final List<BuiltInQualityProfilesDefinition> definitions; - private List<BuiltInQProfile> qProfiles; - - /** - * Requires for pico container when no {@link BuiltInQualityProfilesDefinition} is defined at all - */ - public BuiltInQProfileRepositoryImpl(DbClient dbClient, ServerRuleFinder ruleFinder, Languages languages) { - this(dbClient, ruleFinder, languages, new BuiltInQualityProfilesDefinition[0]); - } - - public BuiltInQProfileRepositoryImpl(DbClient dbClient, ServerRuleFinder ruleFinder, Languages languages, BuiltInQualityProfilesDefinition... definitions) { - this.dbClient = dbClient; - this.ruleFinder = ruleFinder; - this.languages = languages; - this.definitions = ImmutableList.copyOf(definitions); - } - - @Override - public void initialize() { - checkState(qProfiles == null, "initialize must be called only once"); - - Profiler profiler = Profiler.create(LOGGER).startInfo("Load quality profiles"); - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - for (BuiltInQualityProfilesDefinition definition : definitions) { - definition.define(context); - } - Map<String, Map<String, BuiltInQualityProfile>> rulesProfilesByLanguage = validateAndClean(context); - this.qProfiles = toFlatList(rulesProfilesByLanguage); - ensureAllLanguagesHaveAtLeastOneBuiltInQP(); - profiler.stopDebug(); - } - - @Override - public List<BuiltInQProfile> get() { - checkState(qProfiles != null, "initialize must be called first"); - - return qProfiles; - } - - private void ensureAllLanguagesHaveAtLeastOneBuiltInQP() { - Set<String> languagesWithBuiltInQProfiles = qProfiles.stream().map(BuiltInQProfile::getLanguage).collect(Collectors.toSet()); - Set<String> languagesWithoutBuiltInQProfiles = Arrays.stream(languages.all()) - .map(Language::getKey) - .filter(key -> !languagesWithBuiltInQProfiles.contains(key)) - .collect(Collectors.toSet()); - - checkState(languagesWithoutBuiltInQProfiles.isEmpty(), "The following languages have no built-in quality profiles: %s", - String.join("", languagesWithoutBuiltInQProfiles)); - } - - private Map<String, Map<String, BuiltInQualityProfile>> validateAndClean(BuiltInQualityProfilesDefinition.Context context) { - Map<String, Map<String, BuiltInQualityProfile>> profilesByLanguageAndName = context.profilesByLanguageAndName(); - profilesByLanguageAndName.entrySet() - .removeIf(entry -> { - String language = entry.getKey(); - if (languages.get(language) == null) { - LOGGER.info("Language {} is not installed, related quality profiles are ignored", language); - return true; - } - return false; - }); - - return profilesByLanguageAndName; - } - - private List<BuiltInQProfile> toFlatList(Map<String, Map<String, BuiltInQualityProfile>> rulesProfilesByLanguage) { - if (rulesProfilesByLanguage.isEmpty()) { - return Collections.emptyList(); - } - Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = loadRuleDefinitionsByRuleKey(); - Map<String, List<BuiltInQProfile.Builder>> buildersByLanguage = rulesProfilesByLanguage - .entrySet() - .stream() - .collect(MoreCollectors.uniqueIndex( - Map.Entry::getKey, - rulesProfilesByLanguageAndName -> toQualityProfileBuilders(rulesProfilesByLanguageAndName, rulesByRuleKey))); - return buildersByLanguage - .entrySet() - .stream() - .filter(BuiltInQProfileRepositoryImpl::ensureAtMostOneDeclaredDefault) - .map(entry -> toQualityProfiles(entry.getValue())) - .flatMap(Collection::stream) - .collect(MoreCollectors.toList()); - } - - private Map<RuleKey, RuleDefinitionDto> loadRuleDefinitionsByRuleKey() { - try (DbSession dbSession = dbClient.openSession(false)) { - Collection<RuleDefinitionDto> ruleDefinitions = ruleFinder.findAll(); - Multimap<String, DeprecatedRuleKeyDto> deprecatedRuleKeysByRuleId = dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbSession).stream() - .collect(MoreCollectors.index(DeprecatedRuleKeyDto::getRuleUuid)); - Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = new HashMap<>(); - for (RuleDefinitionDto ruleDefinition : ruleDefinitions) { - rulesByRuleKey.put(ruleDefinition.getKey(), ruleDefinition); - deprecatedRuleKeysByRuleId.get(ruleDefinition.getUuid()).forEach(t -> rulesByRuleKey.put(RuleKey.of(t.getOldRepositoryKey(), t.getOldRuleKey()), ruleDefinition)); - } - return rulesByRuleKey; - } - } - - /** - * Creates {@link BuiltInQProfile.Builder} for each unique quality profile name for a given language. - * Builders will have the following properties populated: - * <ul> - * <li>{@link BuiltInQProfile.Builder#language language}: key of the method's parameter</li> - * <li>{@link BuiltInQProfile.Builder#name name}: {@link RulesProfile#getName()}</li> - * <li>{@link BuiltInQProfile.Builder#declaredDefault declaredDefault}: {@code true} if at least one RulesProfile - * with a given name has {@link RulesProfile#getDefaultProfile()} is {@code true}</li> - * <li>{@link BuiltInQProfile.Builder#activeRules activeRules}: the concatenate of the active rules of all - * RulesProfile with a given name</li> - * </ul> - */ - private static List<BuiltInQProfile.Builder> toQualityProfileBuilders(Map.Entry<String, Map<String, BuiltInQualityProfile>> rulesProfilesByLanguageAndName, - Map<RuleKey, RuleDefinitionDto> rulesByRuleKey) { - String language = rulesProfilesByLanguageAndName.getKey(); - // use a LinkedHashMap to keep order of insertion of RulesProfiles - Map<String, BuiltInQProfile.Builder> qualityProfileBuildersByName = new LinkedHashMap<>(); - for (BuiltInQualityProfile builtInProfile : rulesProfilesByLanguageAndName.getValue().values()) { - qualityProfileBuildersByName.compute( - builtInProfile.name(), - (name, existingBuilder) -> updateOrCreateBuilder(language, existingBuilder, builtInProfile, rulesByRuleKey)); - } - return ImmutableList.copyOf(qualityProfileBuildersByName.values()); - } - - /** - * Fails if more than one {@link BuiltInQProfile.Builder#declaredDefault} is {@code true}, otherwise returns {@code true}. - */ - private static boolean ensureAtMostOneDeclaredDefault(Map.Entry<String, List<BuiltInQProfile.Builder>> entry) { - Set<String> declaredDefaultProfileNames = entry.getValue().stream() - .filter(BuiltInQProfile.Builder::isDeclaredDefault) - .map(BuiltInQProfile.Builder::getName) - .collect(MoreCollectors.toSet()); - checkState(declaredDefaultProfileNames.size() <= 1, "Several Quality profiles are flagged as default for the language %s: %s", entry.getKey(), declaredDefaultProfileNames); - return true; - } - - private static BuiltInQProfile.Builder updateOrCreateBuilder(String language, @Nullable BuiltInQProfile.Builder existingBuilder, BuiltInQualityProfile builtInProfile, - Map<RuleKey, RuleDefinitionDto> rulesByRuleKey) { - BuiltInQProfile.Builder builder = createOrReuseBuilder(existingBuilder, language, builtInProfile); - builder.setDeclaredDefault(builtInProfile.isDefault()); - builtInProfile.rules().forEach(builtInActiveRule -> { - RuleKey ruleKey = RuleKey.of(builtInActiveRule.repoKey(), builtInActiveRule.ruleKey()); - RuleDefinitionDto ruleDefinition = rulesByRuleKey.get(ruleKey); - checkState(ruleDefinition != null, "Rule with key '%s' not found", ruleKey); - builder.addRule(new BuiltInQProfile.ActiveRule(ruleDefinition.getUuid(), ruleDefinition.getKey(), - builtInActiveRule.overriddenSeverity(), builtInActiveRule.overriddenParams())); - }); - return builder; - } - - private static BuiltInQProfile.Builder createOrReuseBuilder(@Nullable BuiltInQProfile.Builder existingBuilder, String language, BuiltInQualityProfile builtInProfile) { - if (existingBuilder == null) { - return new BuiltInQProfile.Builder() - .setLanguage(language) - .setName(builtInProfile.name()); - } - return existingBuilder; - } - - private static List<BuiltInQProfile> toQualityProfiles(List<BuiltInQProfile.Builder> builders) { - if (builders.stream().noneMatch(BuiltInQProfile.Builder::isDeclaredDefault)) { - Optional<BuiltInQProfile.Builder> sonarWayProfile = builders.stream().filter(builder -> builder.getName().equals(DEFAULT_PROFILE_NAME)).findFirst(); - if (sonarWayProfile.isPresent()) { - sonarWayProfile.get().setComputedDefault(true); - } else { - builders.iterator().next().setComputedDefault(true); - } - } - return builders.stream() - .map(BuiltInQProfile.Builder::build) - .collect(MoreCollectors.toList(builders.size())); - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdate.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdate.java deleted file mode 100644 index 44403a027e7..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdate.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.db.DbSession; -import org.sonar.db.qualityprofile.RulesProfileDto; - -public interface BuiltInQProfileUpdate { - /** - * Persist an existing built-in profile. - * Db session is committed and Elasticsearch indices are updated. - */ - List<ActiveRuleChange> update(DbSession dbSession, BuiltInQProfile builtInQProfile, RulesProfileDto ruleProfile); -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java deleted file mode 100644 index 9f0f059f6b5..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.RulesProfileDto; -import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; - -import static org.sonar.core.util.stream.MoreCollectors.toSet; - -public class BuiltInQProfileUpdateImpl implements BuiltInQProfileUpdate { - - private final DbClient dbClient; - private final RuleActivator ruleActivator; - private final ActiveRuleIndexer activeRuleIndexer; - private final QualityProfileChangeEventService qualityProfileChangeEventService; - - public BuiltInQProfileUpdateImpl(DbClient dbClient, RuleActivator ruleActivator, ActiveRuleIndexer activeRuleIndexer, - QualityProfileChangeEventService qualityProfileChangeEventService) { - this.dbClient = dbClient; - this.ruleActivator = ruleActivator; - this.activeRuleIndexer = activeRuleIndexer; - this.qualityProfileChangeEventService = qualityProfileChangeEventService; - } - - public List<ActiveRuleChange> update(DbSession dbSession, BuiltInQProfile builtInDefinition, RulesProfileDto initialRuleProfile) { - // Keep reference to all the activated rules before update - Set<String> deactivatedRuleUuids = dbClient.activeRuleDao().selectByRuleProfile(dbSession, initialRuleProfile) - .stream() - .map(ActiveRuleDto::getRuleUuid) - .collect(MoreCollectors.toHashSet()); - - // all rules, including those which are removed from built-in profile - Set<String> ruleUuids = Stream.concat( - deactivatedRuleUuids.stream(), - builtInDefinition.getActiveRules().stream().map(BuiltInQProfile.ActiveRule::getRuleUuid)) - .collect(toSet()); - - Collection<RuleActivation> activations = new ArrayList<>(); - for (BuiltInQProfile.ActiveRule ar : builtInDefinition.getActiveRules()) { - RuleActivation activation = convert(ar); - activations.add(activation); - deactivatedRuleUuids.remove(activation.getRuleUuid()); - } - - RuleActivationContext context = ruleActivator.createContextForBuiltInProfile(dbSession, initialRuleProfile, ruleUuids); - List<ActiveRuleChange> changes = new ArrayList<>(); - - changes.addAll(ruleActivator.activate(dbSession, activations, context)); - - // these rules are no longer part of the built-in profile - deactivatedRuleUuids.forEach(ruleUuid -> changes.addAll(ruleActivator.deactivate(dbSession, context, ruleUuid, false))); - - if (!changes.isEmpty()) { - qualityProfileChangeEventService.distributeRuleChangeEvent(context.getProfiles(), changes, initialRuleProfile.getLanguage()); - } - - activeRuleIndexer.commitAndIndex(dbSession, changes); - return changes; - } - - private static RuleActivation convert(BuiltInQProfile.ActiveRule ar) { - Map<String, String> params = ar.getParams().stream() - .collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue)); - return RuleActivation.create(ar.getRuleUuid(), ar.getSeverity(), params); - } - -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesUpdateListener.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesUpdateListener.java deleted file mode 100644 index 0e9a2ae021f..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesUpdateListener.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.collect.Multimap; -import java.util.Collection; -import org.sonar.api.config.Configuration; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; -import org.sonar.server.notification.NotificationManager; -import org.sonar.server.qualityprofile.BuiltInQPChangeNotificationBuilder.Profile; - -import static org.sonar.core.config.CorePropertyDefinitions.DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES; -import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED; -import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED; -import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED; - -public class BuiltInQualityProfilesUpdateListener { - - private final NotificationManager notificationManager; - private final Languages languages; - private final Configuration config; - - public BuiltInQualityProfilesUpdateListener(NotificationManager notificationManager, Languages languages, Configuration config) { - this.notificationManager = notificationManager; - this.languages = languages; - this.config = config; - } - - void onChange(Multimap<QProfileName, ActiveRuleChange> changedProfiles, long startDate, long endDate) { - if (config.getBoolean(DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES).orElse(false)) { - return; - } - - BuiltInQPChangeNotificationBuilder builder = new BuiltInQPChangeNotificationBuilder(); - changedProfiles.keySet().stream() - .map(changedProfile -> { - String profileName = changedProfile.getName(); - Language language = languages.get(changedProfile.getLanguage()); - Collection<ActiveRuleChange> activeRuleChanges = changedProfiles.get(changedProfile); - int newRules = (int) activeRuleChanges.stream().map(ActiveRuleChange::getType).filter(ACTIVATED::equals).count(); - int updatedRules = (int) activeRuleChanges.stream().map(ActiveRuleChange::getType).filter(UPDATED::equals).count(); - int removedRules = (int) activeRuleChanges.stream().map(ActiveRuleChange::getType).filter(DEACTIVATED::equals).count(); - return Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(language.getKey()) - .setLanguageName(language.getName()) - .setNewRules(newRules) - .setUpdatedRules(updatedRules) - .setRemovedRules(removedRules) - .setStartDate(startDate) - .setEndDate(endDate) - .build(); - }) - .forEach(builder::addProfile); - - notificationManager.scheduleForSending(builder.build()); - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DescendantProfilesSupplier.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DescendantProfilesSupplier.java deleted file mode 100644 index 10127dc0c8e..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DescendantProfilesSupplier.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Collection; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.QProfileDto; - -@FunctionalInterface -public interface DescendantProfilesSupplier { - - Result get(Collection<QProfileDto> profiles, Collection<String> ruleUuids); - - final class Result { - private final Collection<QProfileDto> profiles; - private final Collection<ActiveRuleDto> activeRules; - private final Collection<ActiveRuleParamDto> activeRuleParams; - - public Result(Collection<QProfileDto> profiles, Collection<ActiveRuleDto> activeRules, Collection<ActiveRuleParamDto> activeRuleParams) { - this.profiles = profiles; - this.activeRules = activeRules; - this.activeRuleParams = activeRuleParams; - } - - public Collection<QProfileDto> getProfiles() { - return profiles; - } - - public Collection<ActiveRuleDto> getActiveRules() { - return activeRules; - } - - public Collection<ActiveRuleParamDto> getActiveRuleParams() { - return activeRuleParams; - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java deleted file mode 100644 index a6a3b847913..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.sonar.api.server.ServerSide; -import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; -import org.sonar.process.cluster.hz.HazelcastMember; - -@ServerSide -public class DistributedRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor { - - private HazelcastMember hazelcastMember; - - public DistributedRuleActivatorEventsDistributor(HazelcastMember hazelcastMember) { - this.hazelcastMember = hazelcastMember; - } - - @Override - public void subscribe(RuleActivationListener listener) { - hazelcastMember.subscribeRuleActivationTopic(listener); - } - - @Override - public void pushEvent(RuleSetChangeEvent event) { - hazelcastMember.publishEvent(event); - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QProfileName.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QProfileName.java deleted file mode 100644 index ad910ce3f0f..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QProfileName.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 javax.annotation.Nullable; - -public class QProfileName { - private final String lang; - private final String name; - - public QProfileName(String lang, String name) { - this.lang = lang; - this.name = name; - } - - public String getLanguage() { - return lang; - } - - public String getName() { - return name; - } - - public static QProfileName createFor(String lang, String name){ - return new QProfileName(lang, name); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - QProfileName that = (QProfileName) o; - if (!lang.equals(that.lang)) { - return false; - } - return name.equals(that.name); - } - - @Override - public int hashCode() { - int result = lang.hashCode(); - result = 31 * result + name.hashCode(); - return result; - } - - @Override - public String toString() { - return String.format("%s/%s", lang, name); - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java deleted file mode 100644 index 008afff27f5..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Collection; -import java.util.List; -import java.util.Optional; -import org.sonar.db.project.ProjectDto; -import org.sonar.db.qualityprofile.QProfileDto; - -public interface QualityProfileChangeEventService { - - void publishRuleActivationToSonarLintClients(ProjectDto project, Optional<QProfileDto> activatedProfile, Optional<QProfileDto> deactivatedProfile); - - void distributeRuleChangeEvent(Collection<QProfileDto> profiles, List<ActiveRuleChange> activeRuleChanges, String language); -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java deleted file mode 100644 index 22c96e6ff4a..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import org.jetbrains.annotations.NotNull; -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.ParamChange; -import org.sonar.core.util.RuleChange; -import org.sonar.core.util.RuleSetChangeEvent; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.project.ProjectDto; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.ProjectQprofileAssociationDto; -import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.rule.RuleDto; -import org.sonar.server.rule.index.RuleIndex; -import org.sonar.server.rule.index.RuleQuery; - -@ServerSide -public class QualityProfileChangeEventServiceImpl implements QualityProfileChangeEventService { - - private final DbClient dbClient; - private final RuleIndex ruleIndex; - private final RuleActivatorEventsDistributor eventsDistributor; - - public QualityProfileChangeEventServiceImpl(DbClient dbClient, RuleIndex ruleIndex, RuleActivatorEventsDistributor eventsDistributor) { - this.dbClient = dbClient; - this.ruleIndex = ruleIndex; - this.eventsDistributor = eventsDistributor; - } - - @Override - public void publishRuleActivationToSonarLintClients(ProjectDto project, Optional<QProfileDto> activatedProfile, Optional<QProfileDto> deactivatedProfile) { - List<RuleChange> activatedRules = new ArrayList<>(); - List<RuleChange> deactivatedRules = new ArrayList<>(); - - try (DbSession dbSession = dbClient.openSession(false)) { - - if (activatedProfile.isPresent()) { - RuleQuery query = new RuleQuery().setQProfile(activatedProfile.get()).setActivation(true).setIncludeExternal(true); - // .setLanguages() ? - Iterator<String> searchIdResult = ruleIndex.searchAll(query); - List<String> uuids = new ArrayList<>(); - while (searchIdResult.hasNext()) { - uuids.add(searchIdResult.next()); - } - - List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, uuids); - Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, uuids) - .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid)); - - for (RuleDto ruleDto : ruleDtos) { - RuleChange ruleChange = toRuleChange(ruleDto, paramsByRuleUuid); - activatedRules.add(ruleChange); - } - } - - if (deactivatedProfile.isPresent()) { - RuleQuery query = new RuleQuery().setQProfile(deactivatedProfile.get()).setActivation(true).setIncludeExternal(true); - // .setLanguages() ? - Iterator<String> searchIdResult = ruleIndex.searchAll(query); - List<String> uuids = new ArrayList<>(); - while (searchIdResult.hasNext()) { - uuids.add(searchIdResult.next()); - } - - List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, uuids); - Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, uuids) - .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid)); - - for (RuleDto ruleDto : ruleDtos) { - RuleChange ruleChange = toRuleChange(ruleDto, paramsByRuleUuid); - deactivatedRules.add(ruleChange); - } - } - - } - - RuleSetChangeEvent event = new RuleSetChangeEvent(new String[]{project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0])); - eventsDistributor.pushEvent(event); - } - - @NotNull - private RuleChange toRuleChange(RuleDto ruleDto, Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid) { - RuleChange ruleChange = new RuleChange(); - ruleChange.setKey(ruleDto.getRuleKey()); - ruleChange.setLanguage(ruleDto.getLanguage()); - ruleChange.setSeverity(ruleDto.getSeverityString()); - - List<ParamChange> paramChanges = new ArrayList<>(); - List<ActiveRuleParamDto> activeRuleParamDtos = paramsByRuleUuid.getOrDefault(ruleDto.getUuid(), new ArrayList<>()); - for (ActiveRuleParamDto activeRuleParam : activeRuleParamDtos) { - paramChanges.add(new ParamChange(activeRuleParam.getKey(), activeRuleParam.getValue())); - } - ruleChange.setParams(paramChanges.toArray(new ParamChange[0])); - - String templateUuid = ruleDto.getTemplateUuid(); - if (templateUuid != null && !"".equals(templateUuid)) { - try (DbSession dbSession = dbClient.openSession(false)) { - RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession) - .orElseThrow(() -> new IllegalStateException(String.format("unknow Template Rule '%s'", templateUuid))); - ruleChange.setTemplateKey(templateRule.getRuleKey()); - } - } - - return ruleChange; - } - - public void distributeRuleChangeEvent(Collection<QProfileDto> profiles, List<ActiveRuleChange> activeRuleChanges, String language) { - if (activeRuleChanges.isEmpty()) { - return; - } - - Set<RuleChange> activatedRules = new HashSet<>(); - Set<RuleChange> deactivatedRules = new HashSet<>(); - - for (ActiveRuleChange arc : activeRuleChanges) { - - RuleChange ruleChange = new RuleChange(); - ruleChange.setKey(arc.getActiveRule().getRuleKey().rule()); - ruleChange.setSeverity(arc.getSeverity()); - ruleChange.setLanguage(language); - - Optional<String> templateKey = templateKey(arc); - templateKey.ifPresent(ruleChange::setTemplateKey); - - // params - List<ParamChange> paramChanges = new ArrayList<>(); - for (Map.Entry<String, String> entry : arc.getParameters().entrySet()) { - paramChanges.add(new ParamChange(entry.getKey(), entry.getValue())); - } - ruleChange.setParams(paramChanges.toArray(new ParamChange[0])); - - switch (arc.getType()) { - case ACTIVATED: - case UPDATED: - activatedRules.add(ruleChange); - break; - case DEACTIVATED: - deactivatedRules.add(ruleChange); - break; - } - } - - Set<String> projectKeys = getProjectKeys(profiles); - - RuleSetChangeEvent event = new RuleSetChangeEvent(projectKeys.toArray(new String[0]), activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0])); - eventsDistributor.pushEvent(event); - - } - - private Optional<String> templateKey(ActiveRuleChange arc) { - try (DbSession dbSession = dbClient.openSession(false)) { - String ruleUuid = arc.getRuleUuid(); - RuleDto rule = dbClient.ruleDao().selectByUuid(ruleUuid, dbSession).orElseThrow(() -> new IllegalStateException("unknow rule")); - String templateUuid = rule.getTemplateUuid(); - if (templateUuid != null && !"".equals(templateUuid)) { - RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession) - .orElseThrow(() -> new IllegalStateException(String.format("unknow Template Rule '%s'", templateUuid))); - return Optional.of(templateRule.getRuleKey()); - } - } - return Optional.empty(); - } - - private Set<String> getProjectKeys(Collection<QProfileDto> profiles) { - Set<String> projectKeys = new HashSet<>(); - try (DbSession dbSession = dbClient.openSession(false)) { - for (QProfileDto profileDto : profiles) { - List<ProjectQprofileAssociationDto> associationDtos = dbClient.qualityProfileDao().selectSelectedProjects(dbSession, profileDto, null); - for (ProjectQprofileAssociationDto associationDto : associationDtos) { - projectKeys.add(associationDto.getProjectKey()); - } - } - return projectKeys; - } - } - -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivationContext.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivationContext.java deleted file mode 100644 index 458926e98bd..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivationContext.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import javax.annotation.CheckForNull; -import org.sonar.api.rule.RuleKey; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.qualityprofile.RulesProfileDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleParamDto; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static java.util.Objects.requireNonNull; -import static org.sonar.core.util.stream.MoreCollectors.index; -import static org.sonar.core.util.stream.MoreCollectors.toArrayList; -import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; -import static org.sonar.server.exceptions.BadRequestException.checkRequest; - -/** - * Cache of the data required to activate/deactivate - * multiple rules on a Quality profile, including - * the rule definitions, the rule parameters, the tree - * of profiles hierarchy and its related active rules. - */ -class RuleActivationContext { - - private final long date; - - // The profile that is initially targeted by the operation - private final RulesProfileDto baseRulesProfile; - - private final Map<String, QProfileDto> profilesByUuid = new HashMap<>(); - private final ListMultimap<String, QProfileDto> profilesByParentUuid = ArrayListMultimap.create(); - - // The rules/active rules involved in the group of activations/de-activations - private final Map<String, RuleWrapper> rulesByUuid = new HashMap<>(); - private final Map<ActiveRuleKey, ActiveRuleWrapper> activeRulesByKey = new HashMap<>(); - - // Cursors used to move in the rules and in the tree of profiles. - - private RulesProfileDto currentRulesProfile; - // Cardinality is zero-to-many when cursor is on a built-in rules profile, - // otherwise it's always one, and only one (cursor on descendants or on non-built-in base profile). - private Collection<QProfileDto> currentProfiles; - private RuleWrapper currentRule; - private ActiveRuleWrapper currentActiveRule; - private ActiveRuleWrapper currentParentActiveRule; - - private boolean descendantsLoaded = false; - private final DescendantProfilesSupplier descendantProfilesSupplier; - - private RuleActivationContext(Builder builder) { - this.date = builder.date; - this.descendantProfilesSupplier = builder.descendantProfilesSupplier; - - ListMultimap<String, RuleParamDto> paramsByRuleId = builder.ruleParams.stream().collect(index(RuleParamDto::getRuleUuid)); - for (RuleDefinitionDto rule : builder.rules) { - RuleWrapper wrapper = new RuleWrapper(rule, paramsByRuleId.get(rule.getUuid())); - rulesByUuid.put(rule.getUuid(), wrapper); - } - - this.baseRulesProfile = builder.baseRulesProfile; - register(builder.profiles); - register(builder.activeRules, builder.activeRuleParams); - } - - private void register(Collection<QProfileDto> profiles) { - for (QProfileDto profile : profiles) { - profilesByUuid.put(profile.getKee(), profile); - if (profile.getParentKee() != null) { - profilesByParentUuid.put(profile.getParentKee(), profile); - } - } - } - - private void register(Collection<ActiveRuleDto> activeRules, Collection<ActiveRuleParamDto> activeRuleParams) { - ListMultimap<String, ActiveRuleParamDto> paramsByActiveRuleUuid = activeRuleParams.stream().collect(index(ActiveRuleParamDto::getActiveRuleUuid)); - for (ActiveRuleDto activeRule : activeRules) { - ActiveRuleWrapper wrapper = new ActiveRuleWrapper(activeRule, paramsByActiveRuleUuid.get(activeRule.getUuid())); - this.activeRulesByKey.put(activeRule.getKey(), wrapper); - } - } - - long getDate() { - return date; - } - - /** - * The rule currently selected. - */ - RuleWrapper getRule() { - checkState(currentRule != null, "Rule has not been set yet"); - return currentRule; - } - - @CheckForNull - String getRequestedParamValue(RuleActivation request, String key) { - if (currentRule.rule.isCustomRule()) { - return null; - } - return request.getParameter(key); - } - - boolean hasRequestedParamValue(RuleActivation request, String key) { - return request.hasParameter(key); - } - - /** - * The rules profile being selected. - */ - RulesProfileDto getRulesProfile() { - checkState(currentRulesProfile != null, "Rule profile has not been set yet"); - return currentRulesProfile; - } - - /** - * The active rule related to the selected profile and rule. - * @return null if the selected rule is not activated on the selected profile. - * @see #getRulesProfile() - * @see #getRule() - */ - @CheckForNull - ActiveRuleWrapper getActiveRule() { - return currentActiveRule; - } - - /** - * The active rule related to the rule and the parent of the selected profile. - * @return null if the selected rule is not activated on the parent profile. - * @see #getRule() - */ - @CheckForNull - ActiveRuleWrapper getParentActiveRule() { - return currentParentActiveRule; - } - - /** - * Whether the profile cursor is on the base profile or not. - */ - boolean isCascading() { - return currentRulesProfile != null && !currentRulesProfile.getUuid().equals(baseRulesProfile.getUuid()); - } - - /** - * The profiles being selected. Can be zero or many if {@link #getRulesProfile()} is built-in. - * Else the collection always contains a single profile. - */ - Collection<QProfileDto> getProfiles() { - checkState(currentProfiles != null, "Profiles have not been set yet"); - return currentProfiles; - } - - /** - * The children of {@link #getProfiles()} - */ - Collection<QProfileDto> getChildProfiles() { - loadDescendants(); - return getProfiles().stream() - .flatMap(p -> profilesByParentUuid.get(p.getKee()).stream()) - .collect(Collectors.toList()); - } - - private void loadDescendants() { - if (descendantsLoaded) { - return; - } - Collection<QProfileDto> baseProfiles = profilesByUuid.values().stream() - .filter(p -> p.getRulesProfileUuid().equals(baseRulesProfile.getUuid())) - .collect(toArrayList(profilesByUuid.size())); - DescendantProfilesSupplier.Result result = descendantProfilesSupplier.get(baseProfiles, rulesByUuid.keySet()); - register(result.getProfiles()); - register(result.getActiveRules(), result.getActiveRuleParams()); - descendantsLoaded = true; - } - - /** - * Move the cursor to the given rule and back to the base profile. - */ - public void reset(String ruleUuid) { - doSwitch(this.baseRulesProfile, ruleUuid); - } - - /** - * Moves cursor to a child profile - */ - void selectChild(QProfileDto to) { - checkState(!to.isBuiltIn()); - QProfileDto qp = requireNonNull(this.profilesByUuid.get(to.getKee()), () -> "No profile with uuid " + to.getKee()); - - RulesProfileDto ruleProfile = RulesProfileDto.from(qp); - doSwitch(ruleProfile, getRule().get().getUuid()); - } - - private void doSwitch(RulesProfileDto ruleProfile, String ruleUuid) { - this.currentRule = rulesByUuid.get(ruleUuid); - checkRequest(this.currentRule != null, "Rule with UUID %s not found", ruleUuid); - RuleKey ruleKey = currentRule.get().getKey(); - - this.currentRulesProfile = ruleProfile; - this.currentProfiles = profilesByUuid.values().stream() - .filter(p -> p.getRulesProfileUuid().equals(ruleProfile.getUuid())) - .collect(Collectors.toList()); - this.currentActiveRule = this.activeRulesByKey.get(ActiveRuleKey.of(ruleProfile, ruleKey)); - this.currentParentActiveRule = this.currentProfiles.stream() - .map(QProfileDto::getParentKee) - .filter(Objects::nonNull) - .map(profilesByUuid::get) - .filter(Objects::nonNull) - .findFirst() - .map(profile -> activeRulesByKey.get(ActiveRuleKey.of(profile, ruleKey))) - .orElse(null); - } - - static final class Builder { - private long date = System.currentTimeMillis(); - private RulesProfileDto baseRulesProfile; - private Collection<RuleDefinitionDto> rules; - private Collection<RuleParamDto> ruleParams; - private Collection<QProfileDto> profiles; - private Collection<ActiveRuleDto> activeRules; - private Collection<ActiveRuleParamDto> activeRuleParams; - private DescendantProfilesSupplier descendantProfilesSupplier; - - Builder setDate(long l) { - this.date = l; - return this; - } - - Builder setBaseProfile(RulesProfileDto p) { - this.baseRulesProfile = p; - return this; - } - - Builder setRules(Collection<RuleDefinitionDto> rules) { - this.rules = rules; - return this; - } - - Builder setRuleParams(Collection<RuleParamDto> ruleParams) { - this.ruleParams = ruleParams; - return this; - } - - /** - * All the profiles involved in the activation workflow, including the - * parent profile, even if it's not updated. - */ - Builder setProfiles(Collection<QProfileDto> profiles) { - this.profiles = profiles; - return this; - } - - Builder setActiveRules(Collection<ActiveRuleDto> activeRules) { - this.activeRules = activeRules; - return this; - } - - Builder setActiveRuleParams(Collection<ActiveRuleParamDto> activeRuleParams) { - this.activeRuleParams = activeRuleParams; - return this; - } - - Builder setDescendantProfilesSupplier(DescendantProfilesSupplier d) { - this.descendantProfilesSupplier = d; - return this; - } - - RuleActivationContext build() { - checkArgument(date > 0, "date is not set"); - requireNonNull(baseRulesProfile, "baseRulesProfile is null"); - requireNonNull(rules, "rules is null"); - requireNonNull(ruleParams, "ruleParams is null"); - requireNonNull(profiles, "profiles is null"); - requireNonNull(activeRules, "activeRules is null"); - requireNonNull(activeRuleParams, "activeRuleParams is null"); - requireNonNull(descendantProfilesSupplier, "descendantProfilesSupplier is null"); - return new RuleActivationContext(this); - } - } - - static final class RuleWrapper { - private final RuleDefinitionDto rule; - private final Map<String, RuleParamDto> paramsByKey; - - private RuleWrapper(RuleDefinitionDto rule, Collection<RuleParamDto> params) { - this.rule = rule; - this.paramsByKey = params.stream().collect(uniqueIndex(RuleParamDto::getName)); - } - - RuleDefinitionDto get() { - return rule; - } - - Collection<RuleParamDto> getParams() { - return paramsByKey.values(); - } - - @CheckForNull - RuleParamDto getParam(String key) { - return paramsByKey.get(key); - } - - @CheckForNull - String getParamDefaultValue(String key) { - RuleParamDto param = getParam(key); - return param != null ? param.getDefaultValue() : null; - } - } - - static final class ActiveRuleWrapper { - private final ActiveRuleDto activeRule; - private final Map<String, ActiveRuleParamDto> paramsByKey; - - private ActiveRuleWrapper(ActiveRuleDto activeRule, Collection<ActiveRuleParamDto> params) { - this.activeRule = activeRule; - this.paramsByKey = params.stream().collect(uniqueIndex(ActiveRuleParamDto::getKey)); - } - - ActiveRuleDto get() { - return activeRule; - } - - Collection<ActiveRuleParamDto> getParams() { - return paramsByKey.values(); - } - - @CheckForNull - ActiveRuleParamDto getParam(String key) { - return paramsByKey.get(key); - } - - @CheckForNull - String getParamValue(String key) { - ActiveRuleParamDto param = paramsByKey.get(key); - return param != null ? param.getValue() : null; - } - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java deleted file mode 100644 index 713ae6ce9ab..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.base.Splitter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.rule.RuleStatus; -import org.sonar.api.server.ServerSide; -import org.sonar.api.server.rule.RuleParamType; -import org.sonar.api.utils.System2; -import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.qualityprofile.ActiveRuleDao; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.OrgQProfileDto; -import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.qualityprofile.RulesProfileDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.qualityprofile.RuleActivationContext.ActiveRuleWrapper; -import org.sonar.server.qualityprofile.RuleActivationContext.RuleWrapper; -import org.sonar.server.user.UserSession; -import org.sonar.server.util.TypeValidations; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.stream.Collectors.toList; -import static org.sonar.server.exceptions.BadRequestException.checkRequest; - -/** - * Activation and deactivation of rules in Quality profiles - */ -@ServerSide -public class RuleActivator { - - private final System2 system2; - private final DbClient db; - private final TypeValidations typeValidations; - private final UserSession userSession; - - public RuleActivator(System2 system2, DbClient db, TypeValidations typeValidations, UserSession userSession) { - this.system2 = system2; - this.db = db; - this.typeValidations = typeValidations; - this.userSession = userSession; - } - - - public List<ActiveRuleChange> activate(DbSession dbSession, Collection<RuleActivation> activations, RuleActivationContext context) { - return activations.stream().map(a -> activate(dbSession, a, context)) - .flatMap(List::stream) - .collect(toList()); - } - - public List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation, RuleActivationContext context) { - context.reset(activation.getRuleUuid()); - List<ActiveRuleChange> activeRuleChanges = doActivate(dbSession, activation, context); - return activeRuleChanges; - } - - private List<ActiveRuleChange> doActivate(DbSession dbSession, RuleActivation activation, RuleActivationContext context) { - RuleDefinitionDto rule = context.getRule().get(); - checkRequest(RuleStatus.REMOVED != rule.getStatus(), "Rule was removed: %s", rule.getKey()); - checkRequest(!rule.isTemplate(), "Rule template can't be activated on a Quality profile: %s", rule.getKey()); - checkRequest(context.getRulesProfile().getLanguage().equals(rule.getLanguage()), - "%s rule %s cannot be activated on %s profile %s", rule.getLanguage(), rule.getKey(), context.getRulesProfile().getLanguage(),context.getRulesProfile().getName()); - List<ActiveRuleChange> changes = new ArrayList<>(); - ActiveRuleChange change; - boolean stopCascading = false; - - ActiveRuleWrapper activeRule = context.getActiveRule(); - ActiveRuleKey activeRuleKey = ActiveRuleKey.of(context.getRulesProfile(), rule.getKey()); - if (activeRule == null) { - if (activation.isReset()) { - // ignore reset when rule is not activated - return changes; - } - // new activation - change = new ActiveRuleChange(ActiveRuleChange.Type.ACTIVATED, activeRuleKey, rule); - applySeverityAndParamToChange(activation, context, change); - if (context.isCascading() || isSameAsParent(change, context)) { - change.setInheritance(ActiveRuleInheritance.INHERITED); - } - } else { - // already activated - if (context.isCascading() && activeRule.get().doesOverride()) { - // propagating to descendants, but child profile already overrides rule -> stop propagation - return changes; - } - change = new ActiveRuleChange(ActiveRuleChange.Type.UPDATED, activeRuleKey, rule); - if (context.isCascading() && activeRule.get().getInheritance() == null) { - // activate on child, then on parent -> mark child as overriding parent - change.setInheritance(ActiveRuleInheritance.OVERRIDES); - change.setSeverity(activeRule.get().getSeverityString()); - for (ActiveRuleParamDto activeParam : activeRule.getParams()) { - change.setParameter(activeParam.getKey(), activeParam.getValue()); - } - stopCascading = true; - } else { - applySeverityAndParamToChange(activation, context, change); - if (!context.isCascading() && context.getParentActiveRule() != null) { - // override rule which is already declared on parents - change.setInheritance(isSameAsParent(change, context) ? ActiveRuleInheritance.INHERITED : ActiveRuleInheritance.OVERRIDES); - } - } - if (isSame(change, activeRule)) { - change = null; - stopCascading = true; - } - } - - if (change != null) { - changes.add(change); - persist(change, context, dbSession); - } - - if (!changes.isEmpty()) { - updateProfileDates(dbSession, context); - } - - if (!stopCascading) { - changes.addAll(propagateActivationToDescendants(dbSession, activation, context)); - } - - return changes; - } - - private void updateProfileDates(DbSession dbSession, RuleActivationContext context) { - RulesProfileDto ruleProfile = context.getRulesProfile(); - ruleProfile.setRulesUpdatedAtAsDate(new Date(context.getDate())); - db.qualityProfileDao().update(dbSession, ruleProfile); - - if (userSession.isLoggedIn()) { - context.getProfiles().forEach(p -> db.qualityProfileDao().update(dbSession, OrgQProfileDto.from(p).setUserUpdatedAt(context.getDate()))); - } - } - - /** - * Update severity and params - */ - private void applySeverityAndParamToChange(RuleActivation request, RuleActivationContext context, ActiveRuleChange change) { - RuleWrapper rule = context.getRule(); - ActiveRuleWrapper activeRule = context.getActiveRule(); - ActiveRuleWrapper parentActiveRule = context.getParentActiveRule(); - - if (request.isReset()) { - applySeverityAndParamsWhenResetRequested(change, rule, parentActiveRule); - } else if (context.getRulesProfile().isBuiltIn()) { - applySeverityAndParamsWhenBuiltInProfile(request, context, change, rule); - } else { - applySeverityAndParamsWhenNonBuiltInProfile(request, context, change, rule, activeRule, parentActiveRule); - } - } - - private void applySeverityAndParamsWhenResetRequested(ActiveRuleChange change, RuleWrapper rule, @Nullable ActiveRuleWrapper parentActiveRule) { - String severity = firstNonNull( - parentActiveRule != null ? parentActiveRule.get().getSeverityString() : null, - rule.get().getSeverityString()); - change.setSeverity(severity); - - for (RuleParamDto ruleParamDto : rule.getParams()) { - String paramKey = ruleParamDto.getName(); - // load params from parent profile, else from default values - String paramValue = firstNonNull( - parentActiveRule != null ? parentActiveRule.getParamValue(paramKey) : null, - rule.getParamDefaultValue(paramKey)); - - change.setParameter(paramKey, validateParam(ruleParamDto, paramValue)); - } - } - - private void applySeverityAndParamsWhenBuiltInProfile(RuleActivation request, RuleActivationContext context, ActiveRuleChange change, - RuleWrapper rule) { - // for builtin quality profiles, the severity from profile, when null use the default severity of the rule - String severity = firstNonNull(request.getSeverity(), rule.get().getSeverityString()); - change.setSeverity(severity); - - for (RuleParamDto ruleParamDto : rule.getParams()) { - String paramKey = ruleParamDto.getName(); - // use the value defined in the profile definition, else the rule default value - String paramValue = firstNonNull( - context.getRequestedParamValue(request, paramKey), - rule.getParamDefaultValue(paramKey)); - change.setParameter(paramKey, validateParam(ruleParamDto, paramValue)); - } - } - - /** - * 1. apply requested severity and param - * 2. if rule activated and overridden - apply user value - * 3. apply parent value - * 4. apply defaults - */ - private void applySeverityAndParamsWhenNonBuiltInProfile(RuleActivation request, RuleActivationContext context, ActiveRuleChange change, - RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule, @Nullable ActiveRuleWrapper parentActiveRule) { - String severity = getSeverityForNonBuiltInProfile(request, rule, activeRule, parentActiveRule); - change.setSeverity(severity); - - for (RuleParamDto ruleParamDto : rule.getParams()) { - String paramKey = ruleParamDto.getName(); - String parentValue = parentActiveRule != null ? parentActiveRule.getParamValue(paramKey) : null; - String paramValue; - if (context.hasRequestedParamValue(request, paramKey)) { - // If the request contains the parameter then we're using either value from request, or parent value, or default value - paramValue = firstNonNull( - context.getRequestedParamValue(request, paramKey), - parentValue, - rule.getParamDefaultValue(paramKey)); - } else if (activeRule != null) { - // If the request doesn't contain the parameter, then we're using either user value from db, or parent value if rule inherited, or default - // value - paramValue = firstNonNull( - activeRule.get().doesOverride() ? activeRule.getParamValue(paramKey) : null, - parentValue == null ? activeRule.getParamValue(paramKey) : parentValue, - rule.getParamDefaultValue(paramKey)); - } else { - paramValue = firstNonNull( - parentValue, - rule.getParamDefaultValue(paramKey)); - } - change.setParameter(paramKey, validateParam(ruleParamDto, paramValue)); - } - } - - private static String getSeverityForNonBuiltInProfile(RuleActivation request, RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule, - @Nullable ActiveRuleWrapper parentActiveRule) { - String severity; - if (activeRule != null) { - ActiveRuleDto activeRuleDto = activeRule.get(); - // load severity from request, else keep existing one (if overridden), else from parent if rule inherited, else from default - severity = firstNonNull( - request.getSeverity(), - activeRuleDto.doesOverride() ? activeRuleDto.getSeverityString() : null, - parentActiveRule != null ? parentActiveRule.get().getSeverityString() : activeRuleDto.getSeverityString(), - rule.get().getSeverityString()); - } else { - // load severity from request, else from parent, else from default - severity = firstNonNull( - request.getSeverity(), - parentActiveRule != null ? parentActiveRule.get().getSeverityString() : null, - rule.get().getSeverityString()); - } - return severity; - } - - private List<ActiveRuleChange> propagateActivationToDescendants(DbSession dbSession, RuleActivation activation, RuleActivationContext context) { - List<ActiveRuleChange> changes = new ArrayList<>(); - - // get all inherited profiles - context.getChildProfiles().forEach(child -> { - context.selectChild(child); - changes.addAll(doActivate(dbSession, activation, context)); - }); - return changes; - } - - private void persist(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) { - ActiveRuleDto activeRule = null; - if (change.getType() == ActiveRuleChange.Type.ACTIVATED) { - activeRule = doInsert(change, context, dbSession); - } else if (change.getType() == ActiveRuleChange.Type.DEACTIVATED) { - ActiveRuleDao dao = db.activeRuleDao(); - activeRule = dao.delete(dbSession, change.getKey()).orElse(null); - - } else if (change.getType() == ActiveRuleChange.Type.UPDATED) { - activeRule = doUpdate(change, context, dbSession); - } - change.setActiveRule(activeRule); - db.qProfileChangeDao().insert(dbSession, change.toDto(userSession.getUuid())); - } - - private ActiveRuleDto doInsert(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) { - ActiveRuleDao dao = db.activeRuleDao(); - RuleWrapper rule = context.getRule(); - - ActiveRuleDto activeRule = new ActiveRuleDto(); - activeRule.setProfileUuid(context.getRulesProfile().getUuid()); - activeRule.setRuleUuid(rule.get().getUuid()); - activeRule.setKey(ActiveRuleKey.of(context.getRulesProfile(), rule.get().getKey())); - String severity = change.getSeverity(); - if (severity != null) { - activeRule.setSeverity(severity); - } - ActiveRuleInheritance inheritance = change.getInheritance(); - if (inheritance != null) { - activeRule.setInheritance(inheritance.name()); - } - activeRule.setUpdatedAt(system2.now()); - activeRule.setCreatedAt(system2.now()); - dao.insert(dbSession, activeRule); - for (Map.Entry<String, String> param : change.getParameters().entrySet()) { - if (param.getValue() != null) { - ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(rule.getParam(param.getKey())); - paramDto.setValue(param.getValue()); - dao.insertParam(dbSession, activeRule, paramDto); - } - } - return activeRule; - } - - private ActiveRuleDto doUpdate(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) { - ActiveRuleWrapper activeRule = context.getActiveRule(); - if (activeRule == null) { - return null; - } - ActiveRuleDao dao = db.activeRuleDao(); - String severity = change.getSeverity(); - if (severity != null) { - activeRule.get().setSeverity(severity); - } - ActiveRuleInheritance inheritance = change.getInheritance(); - if (inheritance != null) { - activeRule.get().setInheritance(inheritance.name()); - } - activeRule.get().setUpdatedAt(system2.now()); - dao.update(dbSession, activeRule.get()); - - for (Map.Entry<String, String> param : change.getParameters().entrySet()) { - ActiveRuleParamDto activeRuleParamDto = activeRule.getParam(param.getKey()); - if (activeRuleParamDto == null) { - // did not exist - if (param.getValue() != null) { - activeRuleParamDto = ActiveRuleParamDto.createFor(context.getRule().getParam(param.getKey())); - activeRuleParamDto.setValue(param.getValue()); - dao.insertParam(dbSession, activeRule.get(), activeRuleParamDto); - } - } else { - if (param.getValue() != null) { - activeRuleParamDto.setValue(param.getValue()); - dao.updateParam(dbSession, activeRuleParamDto); - } else { - dao.deleteParam(dbSession, activeRuleParamDto); - } - } - } - return activeRule.get(); - } - - public List<ActiveRuleChange> deactivate(DbSession dbSession, RuleActivationContext context, String ruleUuid, boolean force) { - context.reset(ruleUuid); - List<ActiveRuleChange> activeRuleChanges = doDeactivate(dbSession, context, force); - return activeRuleChanges; - } - - private List<ActiveRuleChange> doDeactivate(DbSession dbSession, RuleActivationContext context, boolean force) { - List<ActiveRuleChange> changes = new ArrayList<>(); - ActiveRuleWrapper activeRule = context.getActiveRule(); - if (activeRule == null) { - return changes; - } - - ActiveRuleChange change; - checkRequest(force || context.isCascading() || activeRule.get().getInheritance() == null, "Cannot deactivate inherited rule '%s'", context.getRule().get().getKey()); - change = new ActiveRuleChange(ActiveRuleChange.Type.DEACTIVATED, activeRule.get(), context.getRule().get()); - changes.add(change); - persist(change, context, dbSession); - - // get all inherited profiles (they are not built-in by design) - context.getChildProfiles().forEach(child -> { - context.selectChild(child); - changes.addAll(doDeactivate(dbSession, context, force)); - }); - - if (!changes.isEmpty()) { - updateProfileDates(dbSession, context); - } - - return changes; - } - - @CheckForNull - private String validateParam(RuleParamDto ruleParam, @Nullable String value) { - if (value != null) { - RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType()); - if (ruleParamType.multiple()) { - List<String> values = Splitter.on(",").splitToList(value); - typeValidations.validate(values, ruleParamType.type(), ruleParamType.values()); - } else { - typeValidations.validate(value, ruleParamType.type(), ruleParamType.values()); - } - } - return value; - } - - public RuleActivationContext createContextForBuiltInProfile(DbSession dbSession, RulesProfileDto builtInProfile, Collection<String> ruleUuids) { - checkArgument(builtInProfile.isBuiltIn(), "Rules profile with UUID %s is not built-in", builtInProfile.getUuid()); - - RuleActivationContext.Builder builder = new RuleActivationContext.Builder(); - builder.setDescendantProfilesSupplier(createDescendantProfilesSupplier(dbSession)); - - // load rules - completeWithRules(dbSession, builder, ruleUuids); - - // load org profiles. Their parents are null by nature. - List<QProfileDto> profiles = db.qualityProfileDao().selectQProfilesByRuleProfile(dbSession, builtInProfile); - builder.setProfiles(profiles); - builder.setBaseProfile(builtInProfile); - - // load active rules - Collection<String> ruleProfileUuids = Stream - .concat(Stream.of(builtInProfile.getUuid()), profiles.stream().map(QProfileDto::getRulesProfileUuid)) - .collect(MoreCollectors.toHashSet(profiles.size() + 1)); - completeWithActiveRules(dbSession, builder, ruleUuids, ruleProfileUuids); - return builder.build(); - } - - public RuleActivationContext createContextForUserProfile(DbSession dbSession, QProfileDto profile, Collection<String> ruleUuids) { - checkArgument(!profile.isBuiltIn(), "Profile with UUID %s is built-in", profile.getKee()); - RuleActivationContext.Builder builder = new RuleActivationContext.Builder(); - builder.setDescendantProfilesSupplier(createDescendantProfilesSupplier(dbSession)); - - // load rules - completeWithRules(dbSession, builder, ruleUuids); - - // load profiles - List<QProfileDto> profiles = new ArrayList<>(); - profiles.add(profile); - if (profile.getParentKee() != null) { - profiles.add(db.qualityProfileDao().selectByUuid(dbSession, profile.getParentKee())); - } - builder.setProfiles(profiles); - builder.setBaseProfile(RulesProfileDto.from(profile)); - - // load active rules - Collection<String> ruleProfileUuids = profiles.stream() - .map(QProfileDto::getRulesProfileUuid) - .collect(MoreCollectors.toHashSet(profiles.size())); - completeWithActiveRules(dbSession, builder, ruleUuids, ruleProfileUuids); - - return builder.build(); - } - - DescendantProfilesSupplier createDescendantProfilesSupplier(DbSession dbSession) { - return (parents, ruleUuids) -> { - Collection<QProfileDto> profiles = db.qualityProfileDao().selectDescendants(dbSession, parents); - Set<String> ruleProfileUuids = profiles.stream() - .map(QProfileDto::getRulesProfileUuid) - .collect(MoreCollectors.toHashSet()); - Collection<ActiveRuleDto> activeRules = db.activeRuleDao().selectByRulesAndRuleProfileUuids(dbSession, ruleUuids, ruleProfileUuids); - List<String> activeRuleUuids = activeRules.stream().map(ActiveRuleDto::getUuid).collect(MoreCollectors.toArrayList(activeRules.size())); - List<ActiveRuleParamDto> activeRuleParams = db.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, activeRuleUuids); - return new DescendantProfilesSupplier.Result(profiles, activeRules, activeRuleParams); - }; - } - - private void completeWithRules(DbSession dbSession, RuleActivationContext.Builder builder, Collection<String> ruleUuids) { - List<RuleDefinitionDto> rules = db.ruleDao().selectDefinitionByUuids(dbSession, ruleUuids); - builder.setRules(rules); - builder.setRuleParams(db.ruleDao().selectRuleParamsByRuleUuids(dbSession, ruleUuids)); - } - - private void completeWithActiveRules(DbSession dbSession, RuleActivationContext.Builder builder, Collection<String> ruleUuids, Collection<String> ruleProfileUuids) { - Collection<ActiveRuleDto> activeRules = db.activeRuleDao().selectByRulesAndRuleProfileUuids(dbSession, ruleUuids, ruleProfileUuids); - builder.setActiveRules(activeRules); - List<String> activeRuleUuids = activeRules.stream().map(ActiveRuleDto::getUuid).collect(MoreCollectors.toArrayList(activeRules.size())); - builder.setActiveRuleParams(db.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, activeRuleUuids)); - } - - private static boolean isSame(ActiveRuleChange change, ActiveRuleWrapper activeRule) { - ActiveRuleInheritance inheritance = change.getInheritance(); - if (inheritance != null && !inheritance.name().equals(activeRule.get().getInheritance())) { - return false; - } - String severity = change.getSeverity(); - if (severity != null && !severity.equals(activeRule.get().getSeverityString())) { - return false; - } - for (Map.Entry<String, String> changeParam : change.getParameters().entrySet()) { - String activeParamValue = activeRule.getParamValue(changeParam.getKey()); - if (changeParam.getValue() == null && activeParamValue != null) { - return false; - } - if (changeParam.getValue() != null && (activeParamValue == null || !StringUtils.equals(changeParam.getValue(), activeParamValue))) { - return false; - } - } - return true; - } - - /** - * True if trying to override an inherited rule but with exactly the same values - */ - private static boolean isSameAsParent(ActiveRuleChange change, RuleActivationContext context) { - ActiveRuleWrapper parentActiveRule = context.getParentActiveRule(); - if (parentActiveRule == null) { - return false; - } - if (!StringUtils.equals(change.getSeverity(), parentActiveRule.get().getSeverityString())) { - return false; - } - for (Map.Entry<String, String> entry : change.getParameters().entrySet()) { - if (entry.getValue() != null && !entry.getValue().equals(parentActiveRule.getParamValue(entry.getKey()))) { - return false; - } - } - return true; - } - - @CheckForNull - private static String firstNonNull(String... strings) { - for (String s : strings) { - if (s != null) { - return s; - } - } - return null; - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java deleted file mode 100644 index d41f1a5abd1..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; - -public interface RuleActivatorEventsDistributor { - - void subscribe(RuleActivationListener listener); - - void pushEvent(RuleSetChangeEvent event); -}
\ No newline at end of file diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java deleted file mode 100644 index 43fc97da319..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.ArrayList; -import java.util.List; -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; - -@ServerSide -public class StandaloneRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor { - - private List<RuleActivationListener> listeners = new ArrayList<>(); - - @Override - public void subscribe(RuleActivationListener listener) { - listeners.add(listener); - } - - @Override - public void pushEvent(RuleSetChangeEvent event) { - listeners.forEach(l -> l.listen(event)); - } -} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/package-info.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/package-info.java deleted file mode 100644 index 0b1a410c6a3..00000000000 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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. - */ -@ParametersAreNonnullByDefault -package org.sonar.server.qualityprofile; - -import javax.annotation.ParametersAreNonnullByDefault; - diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationHandlerTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationHandlerTest.java deleted file mode 100644 index c178ab5ffcc..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationHandlerTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Random; -import java.util.Set; -import java.util.stream.IntStream; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.EmailSubscriberDto; -import org.sonar.db.permission.AuthorizationDao; -import org.sonar.server.notification.email.EmailNotificationChannel; - -import static java.util.Collections.emptySet; -import static java.util.stream.Collectors.toSet; -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; - -public class BuiltInQPChangeNotificationHandlerTest { - private DbClient dbClient = mock(DbClient.class); - private DbSession dbSession = mock(DbSession.class); - private AuthorizationDao authorizationDao = mock(AuthorizationDao.class); - private EmailNotificationChannel emailNotificationChannel = mock(EmailNotificationChannel.class); - - private BuiltInQPChangeNotificationHandler underTest = new BuiltInQPChangeNotificationHandler(dbClient, emailNotificationChannel); - - @Before - public void wire_mocks() { - when(dbClient.openSession(false)).thenReturn(dbSession); - when(dbClient.authorizationDao()).thenReturn(authorizationDao); - } - - @Test - public void getMetadata_returns_empty() { - assertThat(underTest.getMetadata()).isEmpty(); - } - - @Test - public void getNotificationClass_is_BuiltInQPChangeNotification() { - assertThat(underTest.getNotificationClass()).isEqualTo(BuiltInQPChangeNotification.class); - } - - @Test - public void deliver_has_no_effect_if_emailNotificationChannel_is_disabled() { - when(emailNotificationChannel.isActivated()).thenReturn(false); - Set<BuiltInQPChangeNotification> notifications = IntStream.range(0, 1 + new Random().nextInt(10)) - .mapToObj(i -> mock(BuiltInQPChangeNotification.class)) - .collect(toSet()); - - int deliver = underTest.deliver(notifications); - - assertThat(deliver).isZero(); - verify(emailNotificationChannel).isActivated(); - verifyNoMoreInteractions(emailNotificationChannel); - verifyZeroInteractions(dbClient); - notifications.forEach(Mockito::verifyZeroInteractions); - } - - @Test - public void deliver_has_no_effect_if_there_is_no_global_administer_email_subscriber() { - when(emailNotificationChannel.isActivated()).thenReturn(true); - Set<BuiltInQPChangeNotification> notifications = IntStream.range(0, 1 + new Random().nextInt(10)) - .mapToObj(i -> mock(BuiltInQPChangeNotification.class)) - .collect(toSet()); - when(authorizationDao.selectQualityProfileAdministratorLogins(dbSession)) - .thenReturn(emptySet()); - - int deliver = underTest.deliver(notifications); - - assertThat(deliver).isZero(); - verify(emailNotificationChannel).isActivated(); - verifyNoMoreInteractions(emailNotificationChannel); - verify(dbClient).openSession(false); - verify(dbClient).authorizationDao(); - verifyNoMoreInteractions(dbClient); - verify(authorizationDao).selectQualityProfileAdministratorLogins(dbSession); - verifyNoMoreInteractions(authorizationDao); - notifications.forEach(Mockito::verifyZeroInteractions); - } - - @Test - public void deliver_create_emailRequest_for_each_notification_and_for_each_global_administer_email_subscriber() { - when(emailNotificationChannel.isActivated()).thenReturn(true); - Set<BuiltInQPChangeNotification> notifications = IntStream.range(0, 1 + new Random().nextInt(10)) - .mapToObj(i -> mock(BuiltInQPChangeNotification.class)) - .collect(toSet()); - Set<EmailSubscriberDto> emailSubscribers = IntStream.range(0, 1 + new Random().nextInt(10)) - .mapToObj(i -> EmailSubscriberDto.create("login_" + i, true, "login_" + i + "@foo")) - .collect(toSet()); - when(authorizationDao.selectQualityProfileAdministratorLogins(dbSession)) - .thenReturn(emailSubscribers); - Set<EmailNotificationChannel.EmailDeliveryRequest> expectedRequests = notifications.stream() - .flatMap(notification -> emailSubscribers.stream().map(subscriber -> new EmailNotificationChannel.EmailDeliveryRequest(subscriber.getEmail(), notification))) - .collect(toSet()); - int deliveries = new Random().nextInt(expectedRequests.size()); - when(emailNotificationChannel.deliverAll(expectedRequests)).thenReturn(deliveries); - - int deliver = underTest.deliver(notifications); - - assertThat(deliver).isEqualTo(deliveries); - verify(emailNotificationChannel).isActivated(); - verify(emailNotificationChannel).deliverAll(expectedRequests); - verifyNoMoreInteractions(emailNotificationChannel); - verify(dbClient).openSession(false); - verify(dbClient).authorizationDao(); - verifyNoMoreInteractions(dbClient); - verify(authorizationDao).selectQualityProfileAdministratorLogins(dbSession); - verifyNoMoreInteractions(authorizationDao); - notifications.forEach(Mockito::verifyZeroInteractions); - } - -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTemplateTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTemplateTest.java deleted file mode 100644 index 6cf1581ff45..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTemplateTest.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Date; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.platform.Server; -import org.sonar.server.issue.notification.EmailMessage; -import org.sonar.server.qualityprofile.BuiltInQPChangeNotificationBuilder.Profile; - -import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.sonar.api.utils.DateUtils.formatDate; - -public class BuiltInQPChangeNotificationTemplateTest { - - private Server server = mock(Server.class); - - private BuiltInQPChangeNotificationTemplate underTest = new BuiltInQPChangeNotificationTemplate(server); - - @Before - public void setUp() { - when(server.getPublicRootUrl()).thenReturn("http://" + randomAlphanumeric(10)); - } - - @Test - public void notification_contains_a_subject() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setNewRules(2) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertThat(emailMessage.getSubject()).isEqualTo("Built-in quality profiles have been updated"); - } - - @Test - public void notification_contains_count_of_new_rules() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setNewRules(2) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertMessage(emailMessage, "\n 2 new rules\n"); - } - - @Test - public void notification_contains_count_of_updated_rules() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setUpdatedRules(2) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertMessage(emailMessage, "\n 2 rules have been updated\n"); - } - - @Test - public void notification_contains_count_of_removed_rules() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setRemovedRules(2) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertMessage(emailMessage, "\n 2 rules removed\n"); - } - - @Test - public void notification_supports_grammar_for_single_rule_added_removed_or_updated() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setNewRules(1) - .setUpdatedRules(1) - .setRemovedRules(1) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertThat(emailMessage.getMessage()) - .contains("\n 1 new rule\n") - .contains("\n 1 rule has been updated\n") - .contains("\n 1 rule removed\n"); - } - - @Test - public void notification_contains_list_of_new_updated_and_removed_rules() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setNewRules(2) - .setUpdatedRules(3) - .setRemovedRules(4) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertMessage(emailMessage, - "\n" + - " 2 new rules\n" + - " 3 rules have been updated\n" + - " 4 rules removed\n"); - } - - @Test - public void notification_contains_many_profiles() { - String profileName1 = "profile1_" + randomAlphanumeric(20); - String languageKey1 = "langkey1_" + randomAlphanumeric(20); - String languageName1 = "langName1_" + randomAlphanumeric(20); - String profileName2 = "profile2_" + randomAlphanumeric(20); - String languageKey2 = "langkey2_" + randomAlphanumeric(20); - String languageName2 = "langName2_" + randomAlphanumeric(20); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName1) - .setLanguageKey(languageKey1) - .setLanguageName(languageName1) - .setNewRules(2) - .build()) - .addProfile(Profile.newBuilder() - .setProfileName(profileName2) - .setLanguageKey(languageKey2) - .setLanguageName(languageName2) - .setNewRules(13) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertThat(emailMessage.getMessage()).containsSubsequence("The following built-in profiles have been updated:\n", - profileTitleText(profileName1, languageKey1, languageName1), - " 2 new rules\n", - profileTitleText(profileName2, languageKey2, languageName2), - " 13 new rules\n", - "This is a good time to review your quality profiles and update them to benefit from the latest evolutions: " + server.getPublicRootUrl() + "/profiles"); - } - - @Test - public void notification_contains_profiles_sorted_by_language_then_by_profile_name() { - String languageKey1 = "langkey1_" + randomAlphanumeric(20); - String languageName1 = "langName1_" + randomAlphanumeric(20); - String languageKey2 = "langKey2_" + randomAlphanumeric(20); - String languageName2 = "langName2_" + randomAlphanumeric(20); - String profileName1 = "profile1_" + randomAlphanumeric(20); - String profileName2 = "profile2_" + randomAlphanumeric(20); - String profileName3 = "profile3_" + randomAlphanumeric(20); - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder().setProfileName(profileName3).setLanguageKey(languageKey2).setLanguageName(languageName2).build()) - .addProfile(Profile.newBuilder().setProfileName(profileName2).setLanguageKey(languageKey1).setLanguageName(languageName1).build()) - .addProfile(Profile.newBuilder().setProfileName(profileName1).setLanguageKey(languageKey2).setLanguageName(languageName2).build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertThat(emailMessage.getMessage()).containsSubsequence( - "\"" + profileName2 + "\" - " + languageName1, - "\"" + profileName1 + "\" - " + languageName2, - "\"" + profileName3 + "\" - " + languageName2); - } - - @Test - public void notification_contains_encoded_profile_name() { - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName("Sonar Way") - .setLanguageKey("java") - .setLanguageName(newLanguageName()) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertThat(emailMessage.getMessage()).contains(server.getPublicRootUrl() + "/profiles/changelog?language=java&name=Sonar+Way"); - } - - @Test - public void notification_contains_from_and_to_date() { - String profileName = newProfileName(); - String languageKey = newLanguageKey(); - String languageName = newLanguageName(); - long startDate = 1_000_000_000_000L; - long endDate = startDate + 1_100_000_000_000L; - BuiltInQPChangeNotificationBuilder notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setStartDate(startDate) - .setEndDate(endDate) - .build()); - - EmailMessage emailMessage = underTest.format(notification.build()); - - assertMessage(emailMessage, - profileTitleText(profileName, languageKey, languageName, formatDate(new Date(startDate)), formatDate(new Date(endDate)))); - } - - private void assertMessage(EmailMessage emailMessage, String expectedProfileDetails) { - assertThat(emailMessage.getMessage()) - .containsSubsequence( - "The following built-in profiles have been updated:\n\n", - expectedProfileDetails, - "\nThis is a good time to review your quality profiles and update them to benefit from the latest evolutions: " + server.getPublicRootUrl() + "/profiles"); - } - - private String profileTitleText(String profileName, String languageKey, String languageName) { - return "\"" + profileName + "\" - " + languageName + ": " + server.getPublicRootUrl() + "/profiles/changelog?language=" + languageKey + "&name=" + profileName; - } - - private String profileTitleText(String profileName, String languageKey, String languageName, String startDate, String endDate) { - return "\"" + profileName + "\" - " + languageName + ": " + server.getPublicRootUrl() + "/profiles/changelog?language=" + languageKey + "&name=" + profileName + - "&since=" + startDate + "&to=" + endDate + "\n"; - } - - private static String newProfileName() { - return "profileName_" + randomAlphanumeric(20); - } - - private static String newLanguageName() { - return "languageName_" + randomAlphanumeric(20); - } - - private static String newLanguageKey() { - return "languageKey_" + randomAlphanumeric(20); - } -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTest.java deleted file mode 100644 index 6341e3acb28..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQPChangeNotificationTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Random; -import org.junit.Test; -import org.sonar.api.notifications.Notification; -import org.sonar.server.qualityprofile.BuiltInQPChangeNotificationBuilder.Profile; - -import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.tuple; - -public class BuiltInQPChangeNotificationTest { - - private static final Random RANDOM = new Random(); - - - @Test - public void serialize_and_parse_no_profile() { - Notification notification = new BuiltInQPChangeNotificationBuilder().build(); - - BuiltInQPChangeNotificationBuilder result = BuiltInQPChangeNotificationBuilder.parse(notification); - - assertThat(result.getProfiles()).isEmpty(); - } - - @Test - public void serialize_and_parse_single_profile() { - String profileName = randomAlphanumeric(20); - String languageKey = randomAlphanumeric(20); - String languageName = randomAlphanumeric(20); - int newRules = RANDOM.nextInt(5000); - int updatedRules = RANDOM.nextInt(5000); - int removedRules = RANDOM.nextInt(5000); - long startDate = RANDOM.nextInt(5000); - long endDate = startDate + RANDOM.nextInt(5000); - - BuiltInQPChangeNotification notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setNewRules(newRules) - .setUpdatedRules(updatedRules) - .setRemovedRules(removedRules) - .setStartDate(startDate) - .setEndDate(endDate) - .build()) - .build(); - BuiltInQPChangeNotificationBuilder result = BuiltInQPChangeNotificationBuilder.parse(notification); - - assertThat(result.getProfiles()) - .extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName, Profile::getNewRules, Profile::getUpdatedRules, Profile::getRemovedRules, - Profile::getStartDate, Profile::getEndDate) - .containsExactlyInAnyOrder(tuple(profileName, languageKey, languageName, newRules, updatedRules, removedRules, startDate, endDate)); - } - - @Test - public void serialize_and_parse_multiple_profiles() { - String profileName1 = randomAlphanumeric(20); - String languageKey1 = randomAlphanumeric(20); - String languageName1 = randomAlphanumeric(20); - String profileName2 = randomAlphanumeric(20); - String languageKey2 = randomAlphanumeric(20); - String languageName2 = randomAlphanumeric(20); - - BuiltInQPChangeNotification notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName1) - .setLanguageKey(languageKey1) - .setLanguageName(languageName1) - .build()) - .addProfile(Profile.newBuilder() - .setProfileName(profileName2) - .setLanguageKey(languageKey2) - .setLanguageName(languageName2) - .build()) - .build(); - BuiltInQPChangeNotificationBuilder result = BuiltInQPChangeNotificationBuilder.parse(notification); - - assertThat(result.getProfiles()).extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName) - .containsExactlyInAnyOrder(tuple(profileName1, languageKey1, languageName1), tuple(profileName2, languageKey2, languageName2)); - } - - @Test - public void serialize_and_parse_max_values() { - String profileName = randomAlphanumeric(20); - String languageKey = randomAlphanumeric(20); - String languageName = randomAlphanumeric(20); - int newRules = Integer.MAX_VALUE; - int updatedRules = Integer.MAX_VALUE; - int removedRules = Integer.MAX_VALUE; - long startDate = Long.MAX_VALUE; - long endDate = Long.MAX_VALUE; - - BuiltInQPChangeNotification notification = new BuiltInQPChangeNotificationBuilder() - .addProfile(Profile.newBuilder() - .setProfileName(profileName) - .setLanguageKey(languageKey) - .setLanguageName(languageName) - .setNewRules(newRules) - .setUpdatedRules(updatedRules) - .setRemovedRules(removedRules) - .setStartDate(startDate) - .setEndDate(endDate) - .build()) - .build(); - BuiltInQPChangeNotificationBuilder result = BuiltInQPChangeNotificationBuilder.parse(notification); - - assertThat(result.getProfiles()) - .extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName, Profile::getNewRules, Profile::getUpdatedRules, Profile::getRemovedRules, - Profile::getStartDate, Profile::getEndDate) - .containsExactlyInAnyOrder(tuple(profileName, languageKey, languageName, newRules, updatedRules, removedRules, startDate, endDate)); - } - - @Test - public void fail_with_ISE_when_parsing_empty_notification() { - assertThatThrownBy(() -> BuiltInQPChangeNotificationBuilder.parse(new Notification(BuiltInQPChangeNotification.TYPE))) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Could not read the built-in quality profile notification"); - } -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java deleted file mode 100644 index cfa001ff1b8..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Arrays; -import java.util.List; -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.PropertyType; -import org.sonar.api.impl.utils.AlwaysIncreasingSystem2; -import org.sonar.api.rule.Severity; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; -import org.sonar.api.utils.System2; -import org.sonar.core.util.SequenceUuidFactory; -import org.sonar.core.util.UuidFactory; -import org.sonar.db.DbSession; -import org.sonar.db.DbTester; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.QProfileChangeDto; -import org.sonar.db.qualityprofile.QProfileChangeQuery; -import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; -import org.sonar.server.rule.DefaultRuleFinder; -import org.sonar.server.rule.ServerRuleFinder; -import org.sonar.server.util.StringTypeValidation; -import org.sonar.server.util.TypeValidations; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -public class BuiltInQProfileInsertImplTest { - - @Rule - public BuiltInQProfileRepositoryRule builtInQProfileRepository = new BuiltInQProfileRepositoryRule(); - @Rule - public DbTester db = DbTester.create(); - - private final System2 system2 = new AlwaysIncreasingSystem2(); - private final UuidFactory uuidFactory = new SequenceUuidFactory(); - private final TypeValidations typeValidations = new TypeValidations(singletonList(new StringTypeValidation())); - private final DbSession dbSession = db.getSession(); - private final DbSession batchDbSession = db.getDbClient().openSession(true); - private final ServerRuleFinder ruleFinder = new DefaultRuleFinder(db.getDbClient()); - private final ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class); - private final BuiltInQProfileInsertImpl underTest = new BuiltInQProfileInsertImpl(db.getDbClient(), ruleFinder, system2, uuidFactory, typeValidations, activeRuleIndexer); - - @After - public void tearDown() { - batchDbSession.close(); - } - - @Test - public void insert_active_rules_and_changelog() { - RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); - RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo"); - - newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); - newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); - newQp.done(); - - BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name"), rule1, rule2); - call(builtIn); - - verifyTableSize("rules_profiles", 1); - verifyTableSize("org_qprofiles", 1); - verifyTableSize("active_rules", 2); - verifyTableSize("active_rule_parameters", 0); - verifyTableSize("qprofile_changes", 2); - verifyTableSize("default_qprofiles", 0); - - QProfileDto profile = verifyProfileInDb(builtIn); - verifyActiveRuleInDb(profile, rule1, Severity.CRITICAL); - verifyActiveRuleInDb(profile, rule2, Severity.MAJOR); - } - - @Test - public void insert_default_qp() { - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - context.createBuiltInQualityProfile("the name", "xoo") - .setDefault(true) - .done(); - - BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); - call(builtIn); - - verifyTableSize("rules_profiles", 1); - verifyTableSize("org_qprofiles", 1); - verifyTableSize("active_rules", 0); - verifyTableSize("active_rule_parameters", 0); - verifyTableSize("qprofile_changes", 0); - verifyTableSize("default_qprofiles", 1); - - verifyProfileInDb(builtIn); - } - - @Test - public void insert_active_rules_with_params() { - RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); - RuleParamDto param1 = db.rules().insertRuleParam(rule1, p -> p.setType(PropertyType.STRING.name())); - RuleParamDto param2 = db.rules().insertRuleParam(rule1, p -> p.setType(PropertyType.STRING.name())); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo"); - - newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); - newQp.done(); - - BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name"), rule1); - call(builtIn); - - verifyTableSize("rules_profiles", 1); - verifyTableSize("org_qprofiles", 1); - verifyTableSize("active_rules", 1); - verifyTableSize("active_rule_parameters", 2); - verifyTableSize("qprofile_changes", 1); - - QProfileDto profile = verifyProfileInDb(builtIn); - verifyActiveRuleInDb(profile, rule1, Severity.CRITICAL, param1, param2); - } - - @Test - public void flag_profile_as_default_if_declared_as_default_by_api() { - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo").setDefault(true); - newQp.done(); - - BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); - - call(builtIn); - - QProfileDto profile = verifyProfileInDb(builtIn); - QProfileDto defaultProfile = db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, "xoo"); - assertThat(defaultProfile.getKee()).isEqualTo(profile.getKee()); - } - - @Test - public void existing_default_profile_must_not_be_changed() { - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo").setDefault(true); - newQp.done(); - BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); - - QProfileDto currentDefault = db.qualityProfiles().insert(p -> p.setLanguage("xoo")); - db.qualityProfiles().setAsDefault(currentDefault); - - call(builtIn); - - QProfileDto defaultProfile = db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, "xoo"); - assertThat(defaultProfile.getKee()).isEqualTo(currentDefault.getKee()); - verifyTableSize("rules_profiles", 2); - } - - @Test - public void dont_flag_profile_as_default_if_not_declared_as_default_by_api() { - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo").setDefault(false); - newQp.done(); - BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); - - call(builtIn); - - QProfileDto defaultProfile = db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, "xoo"); - assertThat(defaultProfile).isNull(); - } - - // TODO test lot of active_rules, params, orgas - - private void verifyActiveRuleInDb(QProfileDto profile, RuleDefinitionDto rule, String expectedSeverity, RuleParamDto... paramDtos) { - ActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByKey(dbSession, ActiveRuleKey.of(profile, rule.getKey())).get(); - assertThat(activeRule.getUuid()).isNotNull(); - assertThat(activeRule.getInheritance()).isNull(); - assertThat(activeRule.doesOverride()).isFalse(); - assertThat(activeRule.getRuleUuid()).isEqualTo(rule.getUuid()); - assertThat(activeRule.getProfileUuid()).isEqualTo(profile.getRulesProfileUuid()); - assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity); - assertThat(activeRule.getCreatedAt()).isPositive(); - assertThat(activeRule.getUpdatedAt()).isPositive(); - - List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(dbSession, activeRule.getUuid()); - assertThat(params).extracting(ActiveRuleParamDto::getKey).containsOnly(Arrays.stream(paramDtos).map(RuleParamDto::getName).toArray(String[]::new)); - - QProfileChangeQuery changeQuery = new QProfileChangeQuery(profile.getKee()); - QProfileChangeDto change = db.getDbClient().qProfileChangeDao().selectByQuery(dbSession, changeQuery).stream() - .filter(c -> c.getDataAsMap().get("ruleUuid").equals(rule.getUuid())) - .findFirst() - .get(); - assertThat(change.getChangeType()).isEqualTo(ActiveRuleChange.Type.ACTIVATED.name()); - assertThat(change.getCreatedAt()).isPositive(); - assertThat(change.getUuid()).isNotEmpty(); - assertThat(change.getUserUuid()).isNull(); - assertThat(change.getRulesProfileUuid()).isEqualTo(profile.getRulesProfileUuid()); - assertThat(change.getDataAsMap()).containsEntry("severity", expectedSeverity); - } - - private QProfileDto verifyProfileInDb(BuiltInQProfile builtIn) { - QProfileDto profileOnOrg1 = db.getDbClient().qualityProfileDao().selectByNameAndLanguage(dbSession, builtIn.getName(), builtIn.getLanguage()); - assertThat(profileOnOrg1.getLanguage()).isEqualTo(builtIn.getLanguage()); - assertThat(profileOnOrg1.getName()).isEqualTo(builtIn.getName()); - assertThat(profileOnOrg1.getParentKee()).isNull(); - assertThat(profileOnOrg1.getLastUsed()).isNull(); - assertThat(profileOnOrg1.getUserUpdatedAt()).isNull(); - assertThat(profileOnOrg1.getRulesUpdatedAt()).isNotEmpty(); - assertThat(profileOnOrg1.getKee()).isNotEqualTo(profileOnOrg1.getRulesProfileUuid()); - assertThat(profileOnOrg1.getRulesProfileUuid()).isNotNull(); - return profileOnOrg1; - } - - private void verifyTableSize(String table, int expectedSize) { - assertThat(db.countRowsOfTable(dbSession, table)).as("table " + table).isEqualTo(expectedSize); - } - - private void call(BuiltInQProfile builtIn) { - underTest.create(dbSession, builtIn); - dbSession.commit(); - batchDbSession.commit(); - } - -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileLoaderTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileLoaderTest.java deleted file mode 100644 index 6e2ab84fd31..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileLoaderTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 static org.assertj.core.api.Assertions.assertThat; - -public class BuiltInQProfileLoaderTest { - @Rule - public BuiltInQProfileRepositoryRule builtInQProfileRepositoryRule = new BuiltInQProfileRepositoryRule(); - - private BuiltInQProfileLoader underTest = new BuiltInQProfileLoader(builtInQProfileRepositoryRule); - - @Test - public void start_initializes_DefinedQProfileRepository() { - underTest.start(); - - assertThat(builtInQProfileRepositoryRule.isInitialized()).isTrue(); - } -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java deleted file mode 100644 index 4672e429f8f..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Rule; -import org.junit.Test; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.db.DbClient; -import org.sonar.db.DbTester; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.server.language.LanguageTesting; -import org.sonar.server.rule.DefaultRuleFinder; -import org.sonar.server.rule.ServerRuleFinder; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.groups.Tuple.tuple; -import static org.mockito.Mockito.mock; -import static org.sonar.db.rule.RuleTesting.EXTERNAL_XOO; -import static org.sonar.server.qualityprofile.BuiltInQProfile.ActiveRule; - -public class BuiltInQProfileRepositoryImplTest { - private static final Language FOO_LANGUAGE = LanguageTesting.newLanguage("foo", "foo", "foo"); - private static final String SONAR_WAY_QP_NAME = "Sonar way"; - - @Rule - public DbTester db = DbTester.create(); - - private final DbClient dbClient = db.getDbClient(); - private final ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient); - - @Test - public void create_qprofile_with_rule() { - RuleDefinitionDto rule1 = db.rules().insert(); - RuleDefinitionDto rule2 = db.rules().insert(); - db.rules().insert(); - DummyProfileDefinition definition = new DummyProfileDefinition("foo", "foo", false, asList(rule1.getKey(), rule2.getKey())); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definition); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getName) - .containsExactlyInAnyOrder("foo"); - assertThat(underTest.get().get(0).getActiveRules()) - .extracting(ActiveRule::getRuleUuid, ActiveRule::getRuleKey) - .containsExactlyInAnyOrder( - tuple(rule1.getUuid(), rule1.getKey()), - tuple(rule2.getUuid(), rule2.getKey())); - } - - @Test - public void make_single_profile_of_a_language_default_even_if_not_flagged_as_so() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", "foo1", false)); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getLanguage, BuiltInQProfile::isDefault) - .containsExactly(tuple(FOO_LANGUAGE.getKey(), true)); - } - - @Test - public void make_single_profile_of_a_language_default_even_if_flagged_as_so() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", "foo1", true)); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getLanguage, BuiltInQProfile::isDefault) - .containsExactly(tuple(FOO_LANGUAGE.getKey(), true)); - } - - @Test - public void make_first_profile_of_a_language_default_when_none_flagged_as_so() { - DummyProfileDefinition[] definitions = new DummyProfileDefinition[] {new DummyProfileDefinition("foo", "foo1", false), new DummyProfileDefinition("foo", "foo2", false)}; - String firstName = definitions[0].getName(); - String secondName = definitions[1].getName(); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definitions); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getName, BuiltInQProfile::isDefault) - .containsExactlyInAnyOrder(tuple(firstName, true), tuple(secondName, false)); - } - - @Test - public void create_profile_Sonar_Way_as_default_if_none_other_is_defined_default_for_a_given_language() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl( - dbClient, ruleFinder, new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", "doh", false), new DummyProfileDefinition("foo", "boo", false), - new DummyProfileDefinition("foo", SONAR_WAY_QP_NAME, false), new DummyProfileDefinition("foo", "goo", false)); - - underTest.initialize(); - - assertThat(underTest.get()) - .filteredOn(b -> FOO_LANGUAGE.getKey().equals(b.getLanguage())) - .filteredOn(BuiltInQProfile::isDefault) - .extracting(BuiltInQProfile::getName) - .containsExactly(SONAR_WAY_QP_NAME); - } - - @Test - public void do_not_create_Sonar_Way_as_default_if_other_profile_is_defined_as_default() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl( - dbClient, ruleFinder, new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", SONAR_WAY_QP_NAME, false), new DummyProfileDefinition("foo", "goo", true)); - - underTest.initialize(); - - assertThat(underTest.get()) - .filteredOn(b -> FOO_LANGUAGE.getKey().equals(b.getLanguage())) - .filteredOn(BuiltInQProfile::isDefault) - .extracting(BuiltInQProfile::getName) - .containsExactly("goo"); - } - - @Test - public void match_Sonar_Way_default_with_case_sensitivity() { - String sonarWayInOtherCase = SONAR_WAY_QP_NAME.toUpperCase(); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl( - dbClient, ruleFinder, new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", "goo", false), new DummyProfileDefinition("foo", sonarWayInOtherCase, false)); - - underTest.initialize(); - - assertThat(underTest.get()) - .filteredOn(b -> FOO_LANGUAGE.getKey().equals(b.getLanguage())) - .filteredOn(BuiltInQProfile::isDefault) - .extracting(BuiltInQProfile::getName) - .containsExactly("goo"); - } - - @Test - public void create_no_BuiltInQProfile_when_all_definitions_apply_to_non_defined_languages() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages(), - new DummyProfileDefinition("foo", "P1", false)); - - underTest.initialize(); - - assertThat(underTest.get()).isEmpty(); - } - - @Test - public void create_qprofile_with_deprecated_rule() { - RuleDefinitionDto rule1 = db.rules().insert(); - db.rules().insertDeprecatedKey(d -> d.setRuleUuid(rule1.getUuid()).setOldRepositoryKey("oldRepo").setOldRuleKey("oldKey")); - RuleDefinitionDto rule2 = db.rules().insert(); - DummyProfileDefinition definition = new DummyProfileDefinition("foo", "foo", false, - asList(RuleKey.of("oldRepo", "oldKey"), rule2.getKey())); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definition); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getName) - .containsExactlyInAnyOrder("foo"); - assertThat(underTest.get().get(0).getActiveRules()) - .extracting(ActiveRule::getRuleUuid, ActiveRule::getRuleKey) - .containsExactlyInAnyOrder( - tuple(rule1.getUuid(), rule1.getKey()), - tuple(rule2.getUuid(), rule2.getKey())); - } - - @Test - public void fail_with_ISE_when_rule_does_not_exist() { - DummyProfileDefinition[] definitions = new DummyProfileDefinition[] {new DummyProfileDefinition("foo", "foo", false, singletonList(EXTERNAL_XOO))}; - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definitions); - - - } - - @Test - public void fail_with_ISE_when_two_profiles_with_different_name_are_default_for_the_same_language() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", "foo1", true), new DummyProfileDefinition("foo", "foo2", true)); - - assertThatThrownBy(underTest::initialize) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Several Quality profiles are flagged as default for the language foo: [foo1, foo2]"); - } - - @Test - public void get_throws_ISE_if_called_before_initialize() { - BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages()); - - assertThatThrownBy(underTest::get) - .isInstanceOf(IllegalStateException.class) - .hasMessage("initialize must be called first"); - } - - @Test - public void initialize_throws_ISE_if_called_twice() { - BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages()); - underTest.initialize(); - - assertThatThrownBy(underTest::initialize) - .isInstanceOf(IllegalStateException.class) - .hasMessage("initialize must be called only once"); - } - - @Test - public void initialize_throws_ISE_if_language_has_no_builtin_qp() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages(FOO_LANGUAGE)); - - assertThatThrownBy(underTest::initialize) - .isInstanceOf(IllegalStateException.class) - .hasMessage("The following languages have no built-in quality profiles: foo"); - } - - private static final class DummyProfileDefinition implements BuiltInQualityProfilesDefinition { - private final String language; - private final String name; - private final boolean defaultProfile; - private final List<RuleKey> activeRuleKeys; - - private DummyProfileDefinition(String language, String name, boolean defaultProfile, List<RuleKey> activeRuleKeys) { - this.language = language; - this.name = name; - this.defaultProfile = defaultProfile; - this.activeRuleKeys = activeRuleKeys; - } - - private DummyProfileDefinition(String language, String name, boolean defaultProfile) { - this(language, name, defaultProfile, emptyList()); - } - - @Override - public void define(Context context) { - NewBuiltInQualityProfile builtInQualityProfile = context.createBuiltInQualityProfile(name, language); - activeRuleKeys.forEach(activeRuleKey -> builtInQualityProfile.activateRule(activeRuleKey.repository(), activeRuleKey.rule())); - builtInQualityProfile.setDefault(defaultProfile); - builtInQualityProfile.done(); - } - - String getName() { - return name; - } - - } - -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java deleted file mode 100644 index 024de98b39f..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java +++ /dev/null @@ -1,554 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import javax.annotation.Nullable; -import org.assertj.core.groups.Tuple; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; -import org.sonar.api.utils.System2; -import org.sonar.db.DbSession; -import org.sonar.db.DbTester; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.OrgActiveRuleDto; -import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.qualityprofile.RulesProfileDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; -import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.util.IntegerTypeValidation; -import org.sonar.server.util.StringTypeValidation; -import org.sonar.server.util.TypeValidations; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.groups.Tuple.tuple; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.sonar.api.rules.RulePriority.BLOCKER; -import static org.sonar.api.rules.RulePriority.CRITICAL; -import static org.sonar.api.rules.RulePriority.MAJOR; -import static org.sonar.api.rules.RulePriority.MINOR; -import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto; -import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED; - -public class BuiltInQProfileUpdateImplTest { - - private static final long NOW = 1_000; - private static final long PAST = NOW - 100; - - @Rule - public BuiltInQProfileRepositoryRule builtInProfileRepository = new BuiltInQProfileRepositoryRule(); - @Rule - public DbTester db = DbTester.create(); - @Rule - public UserSessionRule userSession = UserSessionRule.standalone(); - private System2 system2 = new TestSystem2().setNow(NOW); - private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class); - private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation())); - private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class); - private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession); - - private BuiltInQProfileUpdateImpl underTest = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivator, activeRuleIndexer, - qualityProfileChangeEventService); - - private RulesProfileDto persistedProfile; - - @Before - public void setUp() { - persistedProfile = newRuleProfileDto(rp -> rp - .setIsBuiltIn(true) - .setLanguage("xoo") - .setRulesUpdatedAt(null)); - db.getDbClient().qualityProfileDao().insert(db.getSession(), persistedProfile); - db.commit(); - } - - @Test - public void activate_new_rules() { - RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); - RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); - newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); - newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2); - - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(2); - assertThatRuleIsNewlyActivated(activeRules, rule1, CRITICAL); - assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR); - assertThatProfileIsMarkedAsUpdated(persistedProfile); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void already_activated_rule_is_updated_in_case_of_differences() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule); - - activateRuleInDb(persistedProfile, rule, BLOCKER); - - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(1); - assertThatRuleIsUpdated(activeRules, rule, CRITICAL); - assertThatProfileIsMarkedAsUpdated(persistedProfile); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void already_activated_rule_is_not_touched_if_no_differences() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule); - - activateRuleInDb(persistedProfile, rule, CRITICAL); - - underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(1); - assertThatRuleIsUntouched(activeRules, rule, CRITICAL); - assertThatProfileIsNotMarkedAsUpdated(persistedProfile); - verifyNoInteractions(qualityProfileChangeEventService); - } - - @Test - public void deactivate_rule_that_is_not_in_built_in_definition_anymore() { - RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); - RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); - newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2); - - // built-in definition contains only rule2 - // so rule1 must be deactivated - activateRuleInDb(persistedProfile, rule1, CRITICAL); - - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(1); - assertThatRuleIsDeactivated(activeRules, rule1); - assertThatProfileIsMarkedAsUpdated(persistedProfile); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void activate_deactivate_and_update_three_rules_at_the_same_time() { - RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); - RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - RuleDefinitionDto rule3 = db.rules().insert(r -> r.setLanguage("xoo")); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); - newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); - newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2); - - // rule1 must be updated (blocker to critical) - // rule2 must be activated - // rule3 must be deactivated - activateRuleInDb(persistedProfile, rule1, BLOCKER); - activateRuleInDb(persistedProfile, rule3, BLOCKER); - - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(2); - assertThatRuleIsUpdated(activeRules, rule1, CRITICAL); - assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR); - assertThatRuleIsDeactivated(activeRules, rule3); - assertThatProfileIsMarkedAsUpdated(persistedProfile); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - // SONAR-10473 - @Test - public void activate_rule_on_built_in_profile_resets_severity_to_default_if_not_overridden() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setSeverity(Severity.MAJOR).setLanguage("xoo")); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule); - underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThatRuleIsNewlyActivated(activeRules, rule, MAJOR); - - // emulate an upgrade of analyzer that changes the default severity of the rule - rule.setSeverity(Severity.MINOR); - db.rules().update(rule); - - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile); - activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThatRuleIsNewlyActivated(activeRules, rule, MINOR); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void activate_rule_on_built_in_profile_resets_params_to_default_if_not_overridden() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10")); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", rule.getLanguage()); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(newQp.language(), newQp.name()), rule); - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile); - - List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(1); - assertThatRuleHasParams(db, activeRules.get(0), tuple("min", "10")); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - - // emulate an upgrade of analyzer that changes the default value of parameter min - ruleParam.setDefaultValue("20"); - db.getDbClient().ruleDao().updateRuleParam(db.getSession(), rule, ruleParam); - - changes = underTest.update(db.getSession(), builtIn, persistedProfile); - activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile); - assertThat(activeRules).hasSize(1); - assertThatRuleHasParams(db, activeRules.get(0), tuple("min", "20")); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void propagate_activation_to_descendant_profiles() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - - QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - QProfileDto childProfile = createChildProfile(profile); - QProfileDto grandchildProfile = createChildProfile(childProfile); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage()); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule); - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile)); - - assertThat(changes).hasSize(3); - assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap()); - assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap()); - assertThatRuleIsActivated(grandchildProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap()); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - // SONAR-14559 - @Test - public void propagate_rule_update_to_descendant_active_rule() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)); - - QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - activateRuleInDb(RulesProfileDto.from(parentProfile), rule, RulePriority.valueOf(Severity.MINOR), null); - - QProfileDto childProfile = createChildProfile(parentProfile); - activateRuleInDb(RulesProfileDto.from(childProfile), rule, RulePriority.valueOf(Severity.MINOR), INHERITED); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(parentProfile.getName(), parentProfile.getLanguage()); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(parentProfile.getLanguage(), parentProfile.getName()), rule); - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(parentProfile)); - - assertThat(changes).hasSize(2); - - List<ActiveRuleDto> parentActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(parentProfile)); - assertThatRuleIsUpdated(parentActiveRules, rule, RulePriority.BLOCKER, null); - - List<ActiveRuleDto> childActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(childProfile)); - assertThatRuleIsUpdated(childActiveRules, rule, RulePriority.BLOCKER, INHERITED); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void propagate_rule_param_update_to_descendant_active_rule_params() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)); - RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10")); - - QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule, - RulePriority.valueOf(Severity.MINOR), null); - activateRuleParamInDb(parentActiveRuleDto, ruleParam, "20"); - - QProfileDto childProfile = createChildProfile(parentProfile); - ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule, - RulePriority.valueOf(Severity.MINOR), INHERITED); - activateRuleParamInDb(childActiveRuleDto, ruleParam, "20"); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(parentProfile.getName(), parentProfile.getLanguage()); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(parentProfile.getLanguage(), parentProfile.getName()), rule); - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(parentProfile)); - - assertThat(changes).hasSize(2); - - assertThatRuleHasParams(db, parentActiveRuleDto, tuple("min", "10")); - assertThatRuleHasParams(db, childActiveRuleDto, tuple("min", "10")); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - } - - @Test - public void do_not_load_descendants_if_no_changes() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - - QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - QProfileDto childProfile = createChildProfile(profile); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage()); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - - // first run - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule); - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile)); - assertThat(changes).hasSize(2).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.ACTIVATED); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - - // second run, without any input changes - RuleActivator ruleActivatorWithoutDescendants = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession) { - @Override - DescendantProfilesSupplier createDescendantProfilesSupplier(DbSession dbSession) { - return (profiles, ruleIds) -> { - throw new IllegalStateException("BOOM - descendants should not be loaded"); - }; - } - }; - changes = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivatorWithoutDescendants, activeRuleIndexer, qualityProfileChangeEventService) - .update(db.getSession(), builtIn, RulesProfileDto.from(profile)); - assertThat(changes).isEmpty(); - verifyNoMoreInteractions(qualityProfileChangeEventService); - } - - @Test - public void propagate_deactivation_to_descendant_profiles() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - - QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - QProfileDto childProfile = createChildProfile(profile); - QProfileDto grandChildProfile = createChildProfile(childProfile); - - BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage()); - newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()); - newQp.done(); - - // first run to activate the rule - BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule); - List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile)); - assertThat(changes).hasSize(3).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.ACTIVATED); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - - // second run to deactivate the rule - context = new BuiltInQualityProfilesDefinition.Context(); - NewBuiltInQualityProfile updatedQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage()); - updatedQp.done(); - builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule); - changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile)); - assertThat(changes).hasSize(3).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.DEACTIVATED); - verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage())); - - assertThatRuleIsDeactivated(profile, rule); - assertThatRuleIsDeactivated(childProfile, rule); - assertThatRuleIsDeactivated(grandChildProfile, rule); - } - - private QProfileDto createChildProfile(QProfileDto parent) { - return db.qualityProfiles().insert(p -> p - .setLanguage(parent.getLanguage()) - .setParentKee(parent.getKee()) - .setName("Child of " + parent.getName())) - .setIsBuiltIn(false); - } - - private void assertThatRuleIsActivated(QProfileDto profile, RuleDefinitionDto rule, @Nullable List<ActiveRuleChange> changes, - String expectedSeverity, @Nullable ActiveRuleInheritance expectedInheritance, Map<String, String> expectedParams) { - OrgActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile) - .stream() - .filter(ar -> ar.getRuleKey().equals(rule.getKey())) - .findFirst() - .orElseThrow(IllegalStateException::new); - - assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity); - assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance != null ? expectedInheritance.name() : null); - - List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid()); - assertThat(params).hasSize(expectedParams.size()); - - if (changes != null) { - ActiveRuleChange change = changes.stream() - .filter(c -> c.getActiveRule().getUuid().equals(activeRule.getUuid())) - .findFirst().orElseThrow(IllegalStateException::new); - assertThat(change.getInheritance()).isEqualTo(expectedInheritance); - assertThat(change.getSeverity()).isEqualTo(expectedSeverity); - assertThat(change.getType()).isEqualTo(ActiveRuleChange.Type.ACTIVATED); - } - } - - private static void assertThatRuleHasParams(DbTester db, ActiveRuleDto activeRule, Tuple... expectedParams) { - assertThat(db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid())) - .extracting(ActiveRuleParamDto::getKey, ActiveRuleParamDto::getValue) - .containsExactlyInAnyOrder(expectedParams); - } - - private static void assertThatRuleIsNewlyActivated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) { - ActiveRuleDto activeRule = findRule(activeRules, rule).get(); - - assertThat(activeRule.getInheritance()).isNull(); - assertThat(activeRule.getSeverityString()).isEqualTo(severity.name()); - assertThat(activeRule.getCreatedAt()).isEqualTo(NOW); - assertThat(activeRule.getUpdatedAt()).isEqualTo(NOW); - } - - private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance expectedInheritance) { - ActiveRuleDto activeRule = findRule(activeRules, rule).get(); - - if (expectedInheritance != null) { - assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance.name()); - } else { - assertThat(activeRule.getInheritance()).isNull(); - } - assertThat(activeRule.getSeverityString()).isEqualTo(severity.name()); - assertThat(activeRule.getCreatedAt()).isEqualTo(PAST); - assertThat(activeRule.getUpdatedAt()).isEqualTo(NOW); - } - - private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) { - assertThatRuleIsUpdated(activeRules, rule, severity, null); - } - - private static void assertThatRuleIsUpdated(ActiveRuleDto activeRules, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance expectedInheritance) { - assertThatRuleIsUpdated(singletonList(activeRules), rule, severity, expectedInheritance); - } - - private static void assertThatRuleIsUntouched(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) { - ActiveRuleDto activeRule = findRule(activeRules, rule).get(); - - assertThat(activeRule.getInheritance()).isNull(); - assertThat(activeRule.getSeverityString()).isEqualTo(severity.name()); - assertThat(activeRule.getCreatedAt()).isEqualTo(PAST); - assertThat(activeRule.getUpdatedAt()).isEqualTo(PAST); - } - - private void assertThatRuleIsDeactivated(QProfileDto profile, RuleDefinitionDto rule) { - Collection<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRulesAndRuleProfileUuids( - db.getSession(), singletonList(rule.getUuid()), singletonList(profile.getRulesProfileUuid())); - assertThat(activeRules).isEmpty(); - } - - private static void assertThatRuleIsDeactivated(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule) { - assertThat(findRule(activeRules, rule)).isEmpty(); - } - - private void assertThatProfileIsMarkedAsUpdated(RulesProfileDto dto) { - RulesProfileDto reloaded = db.getDbClient().qualityProfileDao().selectBuiltInRuleProfiles(db.getSession()) - .stream() - .filter(p -> p.getUuid().equals(dto.getUuid())) - .findFirst() - .get(); - assertThat(reloaded.getRulesUpdatedAt()).isNotEmpty(); - } - - private void assertThatProfileIsNotMarkedAsUpdated(RulesProfileDto dto) { - RulesProfileDto reloaded = db.getDbClient().qualityProfileDao().selectBuiltInRuleProfiles(db.getSession()) - .stream() - .filter(p -> p.getUuid().equals(dto.getUuid())) - .findFirst() - .get(); - assertThat(reloaded.getRulesUpdatedAt()).isNull(); - } - - private static Optional<ActiveRuleDto> findRule(List<ActiveRuleDto> activeRules, RuleDefinitionDto rule) { - return activeRules.stream() - .filter(ar -> ar.getRuleKey().equals(rule.getKey())) - .findFirst(); - } - - private ActiveRuleDto activateRuleInDb(RulesProfileDto profile, RuleDefinitionDto rule, RulePriority severity) { - return activateRuleInDb(profile, rule, severity, null); - } - - private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance inheritance) { - ActiveRuleDto dto = new ActiveRuleDto() - .setKey(ActiveRuleKey.of(ruleProfile, RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()))) - .setProfileUuid(ruleProfile.getUuid()) - .setSeverity(severity.name()) - .setRuleUuid(rule.getUuid()) - .setInheritance(inheritance != null ? inheritance.name() : null) - .setCreatedAt(PAST) - .setUpdatedAt(PAST); - db.getDbClient().activeRuleDao().insert(db.getSession(), dto); - db.commit(); - return dto; - } - - private void activateRuleParamInDb(ActiveRuleDto activeRuleDto, RuleParamDto ruleParamDto, String value) { - ActiveRuleParamDto dto = new ActiveRuleParamDto() - .setActiveRuleUuid(activeRuleDto.getUuid()) - .setRulesParameterUuid(ruleParamDto.getUuid()) - .setKey(ruleParamDto.getName()) - .setValue(value); - db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRuleDto, dto); - db.commit(); - } -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesUpdateListenerTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesUpdateListenerTest.java deleted file mode 100644 index 2f6fc1719d1..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQualityProfilesUpdateListenerTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import org.assertj.core.groups.Tuple; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.sonar.api.config.internal.MapSettings; -import org.sonar.api.notifications.Notification; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; -import org.sonar.core.util.Uuids; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.server.notification.NotificationManager; -import org.sonar.server.qualityprofile.BuiltInQPChangeNotificationBuilder.Profile; - -import static java.util.Arrays.asList; -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.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.sonar.core.config.CorePropertyDefinitions.DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES; -import static org.sonar.server.language.LanguageTesting.newLanguage; -import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED; -import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED; -import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED; - -public class BuiltInQualityProfilesUpdateListenerTest { - - private NotificationManager notificationManager = mock(NotificationManager.class); - private MapSettings settings = new MapSettings(); - - @Test - public void add_profile_to_notification_for_added_rules() { - enableNotificationInGlobalSettings(); - Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create(); - Languages languages = new Languages(); - Tuple expectedTuple = addProfile(profiles, languages, ACTIVATED); - - BuiltInQualityProfilesUpdateListener underTest = new BuiltInQualityProfilesUpdateListener(notificationManager, languages, settings.asConfig()); - underTest.onChange(profiles, 0, 1); - - ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); - verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); - verifyNoMoreInteractions(notificationManager); - assertThat(BuiltInQPChangeNotificationBuilder.parse(notificationArgumentCaptor.getValue()).getProfiles()) - .extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName, Profile::getNewRules) - .containsExactlyInAnyOrder(expectedTuple); - } - - @Test - public void add_profile_to_notification_for_updated_rules() { - enableNotificationInGlobalSettings(); - Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create(); - Languages languages = new Languages(); - Tuple expectedTuple = addProfile(profiles, languages, UPDATED); - - BuiltInQualityProfilesUpdateListener underTest = new BuiltInQualityProfilesUpdateListener(notificationManager, languages, settings.asConfig()); - underTest.onChange(profiles, 0, 1); - - ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); - verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); - verifyNoMoreInteractions(notificationManager); - assertThat(BuiltInQPChangeNotificationBuilder.parse(notificationArgumentCaptor.getValue()).getProfiles()) - .extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName, Profile::getUpdatedRules) - .containsExactlyInAnyOrder(expectedTuple); - } - - @Test - public void add_profile_to_notification_for_removed_rules() { - enableNotificationInGlobalSettings(); - Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create(); - Languages languages = new Languages(); - Tuple expectedTuple = addProfile(profiles, languages, DEACTIVATED); - - BuiltInQualityProfilesUpdateListener underTest = new BuiltInQualityProfilesUpdateListener(notificationManager, languages, settings.asConfig()); - underTest.onChange(profiles, 0, 1); - - ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); - verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); - verifyNoMoreInteractions(notificationManager); - assertThat(BuiltInQPChangeNotificationBuilder.parse(notificationArgumentCaptor.getValue()).getProfiles()) - .extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName, Profile::getRemovedRules) - .containsExactlyInAnyOrder(expectedTuple); - } - - @Test - public void add_multiple_profiles_to_notification() { - enableNotificationInGlobalSettings(); - Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create(); - Languages languages = new Languages(); - Tuple expectedTuple1 = addProfile(profiles, languages, ACTIVATED); - Tuple expectedTuple2 = addProfile(profiles, languages, ACTIVATED); - - BuiltInQualityProfilesUpdateListener underTest = new BuiltInQualityProfilesUpdateListener(notificationManager, languages, settings.asConfig()); - underTest.onChange(profiles, 0, 1); - - ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); - verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); - verifyNoMoreInteractions(notificationManager); - assertThat(BuiltInQPChangeNotificationBuilder.parse(notificationArgumentCaptor.getValue()).getProfiles()) - .extracting(Profile::getProfileName, Profile::getLanguageKey, Profile::getLanguageName, Profile::getNewRules) - .containsExactlyInAnyOrder(expectedTuple1, expectedTuple2); - } - - @Test - public void add_start_and_end_dates_to_notification() { - enableNotificationInGlobalSettings(); - Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create(); - Languages languages = new Languages(); - addProfile(profiles, languages, ACTIVATED); - long startDate = 10_000_000_000L; - long endDate = 15_000_000_000L; - - BuiltInQualityProfilesUpdateListener underTest = new BuiltInQualityProfilesUpdateListener(notificationManager, languages, settings.asConfig()); - underTest.onChange(profiles, startDate, endDate); - - ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); - verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); - verifyNoMoreInteractions(notificationManager); - assertThat(BuiltInQPChangeNotificationBuilder.parse(notificationArgumentCaptor.getValue()).getProfiles()) - .extracting(Profile::getStartDate, Profile::getEndDate) - .containsExactlyInAnyOrder(tuple(startDate, endDate)); - } - - @Test - public void avoid_notification_if_configured_in_settings() { - settings.setProperty(DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES, true); - Multimap<QProfileName, ActiveRuleChange> profiles = ArrayListMultimap.create(); - Languages languages = new Languages(); - addProfile(profiles, languages, ACTIVATED); - - BuiltInQualityProfilesUpdateListener underTest = new BuiltInQualityProfilesUpdateListener(notificationManager, languages, settings.asConfig()); - underTest.onChange(profiles, 0, 1); - - verifyZeroInteractions(notificationManager); - } - - private Tuple addProfile(Multimap<QProfileName, ActiveRuleChange> profiles, Languages languages, ActiveRuleChange.Type type) { - String profileName = randomLowerCaseText(); - String ruleUuid1 = Uuids.createFast(); - String ruleUuid2 = Uuids.createFast(); - Language language = newLanguage(randomLowerCaseText(), randomLowerCaseText()); - languages.add(language); - profiles.putAll(new QProfileName(language.getKey(), profileName), - asList(new ActiveRuleChange( - type, - ActiveRuleKey.parse("qp:repo:rule1"), new RuleDefinitionDto().setUuid(ruleUuid1)), - new ActiveRuleChange(type, ActiveRuleKey.parse("qp:repo:rule2"), new RuleDefinitionDto().setUuid(ruleUuid2)))); - return tuple(profileName, language.getKey(), language.getName(), 2); - } - - private static String randomLowerCaseText() { - return randomAlphanumeric(20).toLowerCase(); - } - - private void enableNotificationInGlobalSettings() { - settings.setProperty(DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES, false); - } -} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java deleted file mode 100644 index cdcd4fa41e2..00000000000 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.collect.ImmutableMap; -import java.util.List; -import javax.annotation.Nullable; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.utils.System2; -import org.sonar.db.DbSession; -import org.sonar.db.DbTester; -import org.sonar.db.qualityprofile.ActiveRuleDto; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.qualityprofile.ActiveRuleParamDto; -import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.qualityprofile.RulesProfileDto; -import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.qualityprofile.DescendantProfilesSupplier.Result; -import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.util.IntegerTypeValidation; -import org.sonar.server.util.StringTypeValidation; -import org.sonar.server.util.TypeValidations; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.mock; -import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED; -import static org.sonar.server.qualityprofile.ActiveRuleInheritance.OVERRIDES; - -/** - * Class org.sonar.server.qualityprofile.RuleActivator is mostly covered in - * org.sonar.server.qualityprofile.BuiltInQProfileUpdateImplTest - */ -public class RuleActivatorTest { - @Rule - public final DbTester db = DbTester.create(); - - @Rule - public final UserSessionRule userSession = UserSessionRule.standalone(); - - private static final long NOW = 1_000; - private static final long PAST = NOW - 100; - private final System2 system2 = new TestSystem2().setNow(NOW); - private final TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation())); - - private final QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class); - private final RuleActivator underTest = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession); - - @Test - public void reset_overridden_active_rule() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)); - RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10")); - - QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule, - RulePriority.valueOf(Severity.BLOCKER), null); - ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10"); - - QProfileDto childProfile = createChildProfile(parentProfile); - ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule, - RulePriority.valueOf(Severity.MINOR), OVERRIDES); - ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "15"); - - DbSession session = db.getSession(); - RuleActivation resetRequest = RuleActivation.createReset(rule.getUuid()); - RuleActivationContext context = new RuleActivationContext.Builder() - .setProfiles(asList(parentProfile, childProfile)) - .setBaseProfile(RulesProfileDto.from(childProfile)) - .setDate(NOW) - .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList())) - .setRules(singletonList(rule)) - .setRuleParams(singletonList(ruleParam)) - .setActiveRules(asList(parentActiveRuleDto, childActiveRuleDto)) - .setActiveRuleParams(asList(parentActiveRuleParam, childActiveRuleParam)) - .build(); - - List<ActiveRuleChange> result = underTest.activate(session, resetRequest, context); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getParameters()).containsEntry("min", "10"); - assertThat(result.get(0).getSeverity()).isEqualTo(Severity.BLOCKER); - assertThat(result.get(0).getInheritance()).isEqualTo(ActiveRuleInheritance.INHERITED); - } - - @Test - public void request_new_severity_and_param_for_child_rule() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)); - RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10")); - - QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule, - RulePriority.valueOf(Severity.BLOCKER), null); - ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10"); - - QProfileDto childProfile = createChildProfile(parentProfile); - ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule, - RulePriority.valueOf(Severity.BLOCKER), INHERITED); - ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "10"); - - DbSession session = db.getSession(); - RuleActivation resetRequest = RuleActivation.create(rule.getUuid(), Severity.MINOR, ImmutableMap.of("min", "15")); - RuleActivationContext context = new RuleActivationContext.Builder() - .setProfiles(asList(parentProfile, childProfile)) - .setBaseProfile(RulesProfileDto.from(childProfile)) - .setDate(NOW) - .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList())) - .setRules(singletonList(rule)) - .setRuleParams(singletonList(ruleParam)) - .setActiveRules(asList(parentActiveRuleDto, childActiveRuleDto)) - .setActiveRuleParams(asList(parentActiveRuleParam, childActiveRuleParam)) - .build(); - - List<ActiveRuleChange> result = underTest.activate(session, resetRequest, context); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getParameters()).containsEntry("min", "15"); - assertThat(result.get(0).getSeverity()).isEqualTo(Severity.MINOR); - assertThat(result.get(0).getInheritance()).isEqualTo(OVERRIDES); - } - - @Test - public void set_severity_and_param_for_child_rule_when_activating() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)); - RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10")); - - QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true)); - - QProfileDto childProfile = createChildProfile(parentProfile); - - DbSession session = db.getSession(); - RuleActivation resetRequest = RuleActivation.create(rule.getUuid()); - RuleActivationContext context = new RuleActivationContext.Builder() - .setProfiles(asList(parentProfile, childProfile)) - .setBaseProfile(RulesProfileDto.from(childProfile)) - .setDate(NOW) - .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList())) - .setRules(singletonList(rule)) - .setRuleParams(singletonList(ruleParam)) - .setActiveRules(emptyList()) - .setActiveRuleParams(emptyList()) - .build(); - - List<ActiveRuleChange> result = underTest.activate(session, resetRequest, context); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getParameters()).containsEntry("min", "10"); - assertThat(result.get(0).getSeverity()).isEqualTo(Severity.BLOCKER); - assertThat(result.get(0).getInheritance()).isNull(); - } - - @Test - public void fail_if_rule_language_doesnt_match_qp() { - RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo") - .setRepositoryKey("repo") - .setRuleKey("rule") - .setSeverity(Severity.BLOCKER)); - QProfileDto qp = db.qualityProfiles().insert(p -> p.setLanguage("xoo2").setKee("qp").setIsBuiltIn(true)); - - DbSession session = db.getSession(); - RuleActivation resetRequest = RuleActivation.create(rule.getUuid()); - RuleActivationContext context = new RuleActivationContext.Builder() - .setProfiles(singletonList(qp)) - .setBaseProfile(RulesProfileDto.from(qp)) - .setDate(NOW) - .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList())) - .setRules(singletonList(rule)) - .setRuleParams(emptyList()) - .setActiveRules(emptyList()) - .setActiveRuleParams(emptyList()) - .build(); - - - assertThrows("xoo rule repo:rule cannot be activated on xoo2 profile qp", BadRequestException.class, - () -> underTest.activate(session, resetRequest, context)); - } - - - private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDefinitionDto rule, RulePriority severity, @Nullable ActiveRuleInheritance inheritance) { - ActiveRuleDto dto = new ActiveRuleDto() - .setKey(ActiveRuleKey.of(ruleProfile, RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()))) - .setProfileUuid(ruleProfile.getUuid()) - .setSeverity(severity.name()) - .setRuleUuid(rule.getUuid()) - .setInheritance(inheritance != null ? inheritance.name() : null) - .setCreatedAt(PAST) - .setUpdatedAt(PAST); - db.getDbClient().activeRuleDao().insert(db.getSession(), dto); - db.commit(); - return dto; - } - - private ActiveRuleParamDto activateRuleParamInDb(ActiveRuleDto activeRuleDto, RuleParamDto ruleParamDto, String value) { - ActiveRuleParamDto dto = new ActiveRuleParamDto() - .setActiveRuleUuid(activeRuleDto.getUuid()) - .setRulesParameterUuid(ruleParamDto.getUuid()) - .setKey(ruleParamDto.getName()) - .setValue(value); - db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRuleDto, dto); - db.commit(); - return dto; - } - - private QProfileDto createChildProfile(QProfileDto parent) { - return db.qualityProfiles().insert(p -> p - .setLanguage(parent.getLanguage()) - .setParentKee(parent.getKee()) - .setName("Child of " + parent.getName())) - .setIsBuiltIn(false); - } -} diff --git a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java deleted file mode 100644 index c7e2b2a9a01..00000000000 --- a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.junit.rules.ExternalResource; -import org.sonar.api.resources.Language; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.db.rule.RuleDefinitionDto; - -import static com.google.common.base.Preconditions.checkState; - -public class BuiltInQProfileRepositoryRule extends ExternalResource implements BuiltInQProfileRepository { - private boolean initializeCalled = false; - private List<BuiltInQProfile> profiles = new ArrayList<>(); - - @Override - protected void before() { - this.initializeCalled = false; - this.profiles.clear(); - } - - @Override - public void initialize() { - checkState(!initializeCalled, "initialize must be called only once"); - this.initializeCalled = true; - } - - @Override - public List<BuiltInQProfile> get() { - checkState(initializeCalled, "initialize must be called first"); - - return ImmutableList.copyOf(profiles); - } - - public boolean isInitialized() { - return initializeCalled; - } - - public BuiltInQProfile add(Language language, String profileName) { - return add(language, profileName, false); - } - - public BuiltInQProfile add(Language language, String profileName, boolean isDefault) { - return add(language, profileName, isDefault, new BuiltInQProfile.ActiveRule[0]); - } - - public BuiltInQProfile add(Language language, String profileName, boolean isDefault, BuiltInQProfile.ActiveRule... rules) { - BuiltInQProfile builtIn = create(language, profileName, isDefault, rules); - profiles.add(builtIn); - return builtIn; - } - - public BuiltInQProfile create(Language language, String profileName, boolean isDefault, BuiltInQProfile.ActiveRule... rules) { - BuiltInQProfile.Builder builder = new BuiltInQProfile.Builder() - .setLanguage(language.getKey()) - .setName(profileName) - .setDeclaredDefault(isDefault); - Arrays.stream(rules).forEach(builder::addRule); - return builder.build(); - } - - public BuiltInQProfile create(BuiltInQualityProfilesDefinition.BuiltInQualityProfile api, RuleDefinitionDto... rules) { - BuiltInQProfile.Builder builder = new BuiltInQProfile.Builder() - .setLanguage(api.language()) - .setName(api.name()) - .setDeclaredDefault(api.isDefault()); - Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = Arrays.stream(rules) - .collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getKey)); - api.rules().forEach(rule -> { - RuleKey ruleKey = RuleKey.of(rule.repoKey(), rule.ruleKey()); - RuleDefinitionDto ruleDefinition = rulesByRuleKey.get(ruleKey); - Preconditions.checkState(ruleDefinition != null, "Rule '%s' not found", ruleKey); - builder.addRule(new BuiltInQProfile.ActiveRule(ruleDefinition.getUuid(), rule)); - }); - return builder - .build(); - } -} |