From 5a4027c007014262d0cf9aaa11dcf43b63934bc3 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Wed, 8 Apr 2015 11:54:35 +0200 Subject: [PATCH] SONAR-6307 WS to change the parent of a quality profile --- .../server/platform/ServerComponents.java | 1 + .../server/qualityprofile/RuleActivator.java | 2 +- .../ws/QProfileChangeParentAction.java | 102 +++++++++ .../ws/QProfileIdentificationParamUtils.java | 8 +- .../QProfileChangeParentActionMediumTest.java | 197 ++++++++++++++++++ 5 files changed, 305 insertions(+), 5 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentActionMediumTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 23a95ec3bd2..202b1558515 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -390,6 +390,7 @@ class ServerComponents { pico.addSingleton(QProfileCreateAction.class); pico.addSingleton(QProfileImportersAction.class); pico.addSingleton(QProfileInheritanceAction.class); + pico.addSingleton(QProfileChangeParentAction.class); pico.addSingleton(QProfilesWs.class); pico.addSingleton(ProfilesWs.class); pico.addSingleton(RuleActivationActions.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java index 8026d90518d..e8c72779dd1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java @@ -449,7 +449,7 @@ public class RuleActivator implements ServerComponent { } } - void setParent(String key, @Nullable String parentKey) { + public void setParent(String key, @Nullable String parentKey) { DbSession dbSession = db.openSession(false); try { setParent(dbSession, key, parentKey); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentAction.java new file mode 100644 index 00000000000..9c6d01f161e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentAction.java @@ -0,0 +1,102 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.ws; + +import com.google.common.base.Preconditions; +import org.sonar.api.resources.Languages; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService.NewAction; +import org.sonar.api.server.ws.WebService.NewController; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.DbClient; +import org.sonar.server.qualityprofile.QProfileFactory; +import org.sonar.server.qualityprofile.RuleActivator; + +import static org.apache.commons.lang.StringUtils.isEmpty; + +public class QProfileChangeParentAction implements BaseQProfileWsAction { + + private static final String PARAM_PARENT_KEY = "parentKey"; + + private static final String PARAM_PARENT_NAME = "parentName"; + + private final DbClient dbClient; + + private final RuleActivator ruleActivator; + + private final QProfileFactory profileFactory; + + private final Languages languages; + + public QProfileChangeParentAction(DbClient dbClient, RuleActivator ruleActivator, QProfileFactory profileFactory, Languages languages) { + this.dbClient = dbClient; + this.ruleActivator = ruleActivator; + this.profileFactory = profileFactory; + this.languages = languages; + } + + @Override + public void define(NewController context) { + NewAction inheritance = context.createAction("change_parent") + .setSince("5.2") + .setDescription("Change a quality profile's parent.") + .setHandler(this); + + QProfileIdentificationParamUtils.defineProfileParams(inheritance, languages); + + inheritance.createParam(PARAM_PARENT_KEY) + .setDescription("The key of the new parent profile. If this parameter is set, parentName must not be set. If left empty, ") + .setExampleValue("sonar-way-java-12345"); + inheritance.createParam(PARAM_PARENT_NAME) + .setDescription("A quality profile name. If this parameter is set, profileKey must not be set and language must be set to disambiguate.") + .setExampleValue("Sonar way"); + + } + + @Override + public void handle(Request request, Response response) throws Exception { + DbSession session = dbClient.openSession(false); + try { + String profileKey = QProfileIdentificationParamUtils.getProfileKeyFromParameters(request, profileFactory, session); + String parentKey = getParentKeyFromParameters(request, profileFactory, session); + + ruleActivator.setParent(profileKey, parentKey); + + response.noContent(); + } finally { + session.close(); + } + } + + private static String getParentKeyFromParameters(Request request, QProfileFactory profileFactory, DbSession session) { + String language = request.param(QProfileIdentificationParamUtils.PARAM_LANGUAGE); + String parentName = request.param(PARAM_PARENT_NAME); + String parentKey = request.param(PARAM_PARENT_KEY); + + Preconditions.checkArgument( + (isEmpty(parentName) || isEmpty(parentKey)), "parentKey and parentName cannot be used simultaneously"); + + if (isEmpty(parentKey) && !isEmpty(parentName)) { + parentKey = QProfileIdentificationParamUtils.getProfileKeyFromLanguageAndName(language, parentName, profileFactory, session); + } + return parentKey; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileIdentificationParamUtils.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileIdentificationParamUtils.java index a577869ca12..29659c1c360 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileIdentificationParamUtils.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileIdentificationParamUtils.java @@ -32,9 +32,9 @@ import static org.apache.commons.lang.StringUtils.isEmpty; public class QProfileIdentificationParamUtils { - private static final String PARAM_LANGUAGE = "language"; - private static final String PARAM_PROFILE_NAME = "profileName"; - private static final String PARAM_PROFILE_KEY = "profileKey"; + public static final String PARAM_LANGUAGE = "language"; + public static final String PARAM_PROFILE_NAME = "profileName"; + public static final String PARAM_PROFILE_KEY = "profileKey"; private QProfileIdentificationParamUtils() { // Utility class @@ -67,7 +67,7 @@ public class QProfileIdentificationParamUtils { return profileKey; } - private static String getProfileKeyFromLanguageAndName(String language, String profileName, QProfileFactory profileFactory, DbSession session) { + public static String getProfileKeyFromLanguageAndName(String language, String profileName, QProfileFactory profileFactory, DbSession session) { QualityProfileDto profile = profileFactory.getByNameAndLanguage(session, profileName, language); if (profile == null) { throw new NotFoundException(String.format("Unable to find a profile for language '%s' with name '%s'", language, profileName)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentActionMediumTest.java new file mode 100644 index 00000000000..c95c3d9ec47 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangeParentActionMediumTest.java @@ -0,0 +1,197 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.ws; + +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rule.Severity; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; +import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.qualityprofile.QProfileName; +import org.sonar.server.qualityprofile.QProfileTesting; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class QProfileChangeParentActionMediumTest { + + // TODO Replace with DbTester + EsTester once DaoV2 is removed + @ClassRule + public static ServerTester tester = new ServerTester(); + + QProfilesWs ws; + DbClient db; + DbSession session; + WsTester wsTester; + + @Before + public void setUp() throws Exception { + tester.clearDbAndIndexes(); + db = tester.get(DbClient.class); + ws = tester.get(QProfilesWs.class); + wsTester = tester.get(WsTester.class); + session = db.openSession(false); + MockUserSession.set().setLogin("gandalf").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + } + + @After + public void after() { + session.close(); + } + + @Test + public void change_parent_with_keys() throws Exception { + QualityProfileDto parent1 = createProfile("xoo", "Parent 1"); + QualityProfileDto parent2 = createProfile("xoo", "Parent 2"); + QualityProfileDto child = createProfile("xoo", "Child"); + + RuleDto rule1 = createRule("xoo", "rule1"); + RuleDto rule2 = createRule("xoo", "rule2"); + createActiveRule(rule1, parent1); + createActiveRule(rule2, parent2); + session.commit(); + + assertThat(db.activeRuleDao().findByProfileKey(session, child.getKey())).isEmpty(); + + // 1. Set parent 1 + wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "change_parent") + .setParam(QProfileIdentificationParamUtils.PARAM_PROFILE_KEY, child.getKey().toString()) + .setParam("parentKey", parent1.getKey().toString()) + .execute(); + session.clearCache(); + + // 1. Assert ActiveRule in DAO + List activeRules1 = db.activeRuleDao().findByProfileKey(session, child.getKey()); + assertThat(activeRules1).hasSize(1); + assertThat(activeRules1.get(0).getKey().ruleKey().rule()).isEqualTo("rule1"); + + // 2. Set parent 2 + wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "change_parent") + .setParam(QProfileIdentificationParamUtils.PARAM_PROFILE_KEY, child.getKey().toString()) + .setParam("parentKey", parent2.getKey().toString()) + .execute(); + session.clearCache(); + + // 2. Assert ActiveRule in DAO + List activeRules2 = db.activeRuleDao().findByProfileKey(session, child.getKey()); + assertThat(activeRules2).hasSize(1); + assertThat(activeRules2.get(0).getKey().ruleKey().rule()).isEqualTo("rule2"); + + // 3. Remove parent + wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "change_parent") + .setParam(QProfileIdentificationParamUtils.PARAM_PROFILE_KEY, child.getKey().toString()) + .execute(); + session.clearCache(); + + // 3. Assert ActiveRule in DAO + List activeRules = db.activeRuleDao().findByProfileKey(session, child.getKey()); + assertThat(activeRules).isEmpty(); + } + + @Test + public void change_parent_with_names() throws Exception { + QualityProfileDto parent1 = createProfile("xoo", "Parent 1"); + QualityProfileDto parent2 = createProfile("xoo", "Parent 2"); + QualityProfileDto child = createProfile("xoo", "Child"); + + RuleDto rule1 = createRule("xoo", "rule1"); + RuleDto rule2 = createRule("xoo", "rule2"); + createActiveRule(rule1, parent1); + createActiveRule(rule2, parent2); + session.commit(); + + assertThat(db.activeRuleDao().findByProfileKey(session, child.getKey())).isEmpty(); + + // 1. Set parent 1 + wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "change_parent") + .setParam(QProfileIdentificationParamUtils.PARAM_LANGUAGE, "xoo") + .setParam(QProfileIdentificationParamUtils.PARAM_PROFILE_NAME, child.getName()) + .setParam("parentName", parent1.getName()) + .execute(); + session.clearCache(); + + // 1. Assert ActiveRule in DAO + List activeRules1 = db.activeRuleDao().findByProfileKey(session, child.getKey()); + assertThat(activeRules1).hasSize(1); + assertThat(activeRules1.get(0).getKey().ruleKey().rule()).isEqualTo("rule1"); + + // 2. Set parent 2 + wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "change_parent") + .setParam(QProfileIdentificationParamUtils.PARAM_LANGUAGE, "xoo") + .setParam(QProfileIdentificationParamUtils.PARAM_PROFILE_NAME, child.getName()) + .setParam("parentName", parent2.getName()) + .execute(); + session.clearCache(); + + // 2. Assert ActiveRule in DAO + List activeRules2 = db.activeRuleDao().findByProfileKey(session, child.getKey()); + assertThat(activeRules2).hasSize(1); + assertThat(activeRules2.get(0).getKey().ruleKey().rule()).isEqualTo("rule2"); + } + + @Test(expected = IllegalArgumentException.class) + public void fail_if_parent_key_and_name_both_set() throws Exception { + QualityProfileDto child = createProfile("xoo", "Child"); + session.commit(); + + assertThat(db.activeRuleDao().findByProfileKey(session, child.getKey())).isEmpty(); + + wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "change_parent") + .setParam(QProfileIdentificationParamUtils.PARAM_PROFILE_KEY, child.getKee()) + .setParam("parentName", "polop") + .setParam("parentKey", "palap") + .execute(); + session.clearCache(); + } + + private QualityProfileDto createProfile(String lang, String name) { + QualityProfileDto profile = QProfileTesting.newDto(new QProfileName(lang, name), "p" + lang + "-" + name.toLowerCase()); + db.qualityProfileDao().insert(session, profile); + return profile; + } + + private RuleDto createRule(String lang, String id) { + RuleDto rule = RuleDto.createFor(RuleKey.of("blah", id)) + .setLanguage(lang) + .setSeverity(Severity.BLOCKER) + .setStatus(RuleStatus.READY); + db.ruleDao().insert(session, rule); + return rule; + } + + private ActiveRuleDto createActiveRule(RuleDto rule, QualityProfileDto profile) { + ActiveRuleDto activeRule = ActiveRuleDto.createFor(profile, rule) + .setSeverity(rule.getSeverityString()); + db.activeRuleDao().insert(session, activeRule); + return activeRule; + } +} -- 2.39.5