From 67e2278d37142eed525696714b0b585d03daefe0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Mon, 30 Mar 2015 18:40:05 +0200 Subject: [PATCH] SONAR-6300 WS to select the default quality profile for a language --- .../server/platform/ServerComponents.java | 1 + .../qualityprofile/QProfileFactory.java | 2 +- .../ws/QProfileSetDefaultAction.java | 115 +++++++++++ .../ws/QProfileSetDefaultActionTest.java | 181 ++++++++++++++++++ 4 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultActionTest.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 3742afb7567..eef1ee23140 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 @@ -380,6 +380,7 @@ class ServerComponents { pico.addSingleton(BuiltInProfiles.class); pico.addSingleton(QProfileRestoreBuiltInAction.class); pico.addSingleton(QProfileSearchAction.class); + pico.addSingleton(QProfileSetDefaultAction.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/QProfileFactory.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java index ccc8f8f2d52..c6d99c1eb42 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java @@ -138,7 +138,7 @@ public class QProfileFactory implements ServerComponent { return db.qualityProfileDao().getDefaultProfile(language, session); } - void setDefault(String profileKey) { + public void setDefault(String profileKey) { DbSession dbSession = db.openSession(false); try { setDefault(dbSession, profileKey); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultAction.java new file mode 100644 index 00000000000..ec88a6c165c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultAction.java @@ -0,0 +1,115 @@ +/* + * 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.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import org.sonar.api.resources.Language; +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; +import org.sonar.api.server.ws.WebService.NewAction; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.qualityprofile.QProfile; +import org.sonar.server.qualityprofile.QProfileFactory; +import org.sonar.server.qualityprofile.QProfileLookup; +import org.sonar.server.user.UserSession; + +import java.util.Arrays; + +import static org.apache.commons.lang.StringUtils.isEmpty; + +public class QProfileSetDefaultAction implements BaseQProfileWsAction { + + private static final String PARAM_LANGUAGE = "language"; + private static final String PARAM_PROFILE_NAME = "profileName"; + private static final String PARAM_PROFILE_KEY = "profileKey"; + + private final Languages languages; + + private final QProfileLookup profileLookup; + + private final QProfileFactory profileFactory; + + public QProfileSetDefaultAction(Languages languages, QProfileLookup profileLookup, QProfileFactory profileFactory) { + this.languages = languages; + this.profileLookup = profileLookup; + this.profileFactory = profileFactory; + } + + @Override + public void handle(Request request, Response response) throws Exception { + UserSession.get().checkLoggedIn().checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + String language = request.param(PARAM_LANGUAGE); + String profileName = request.param(PARAM_PROFILE_NAME); + String profileKey = request.param(PARAM_PROFILE_KEY); + + Preconditions.checkArgument( + (!isEmpty(language) && !isEmpty(profileName)) ^ !isEmpty(profileKey), "Either profileKey or profileName + language must be set"); + + if(profileKey == null) { + profileKey = getProfileKeyFromLanguageAndName(language, profileName); + } + + profileFactory.setDefault(profileKey); + + response.noContent(); + } + + private String getProfileKeyFromLanguageAndName(String language, String profileName) { + QProfile profile = profileLookup.profile(profileName, language); + if (profile == null) { + throw new NotFoundException(String.format("Unable to find a profile for language '%s' with name '%s'", language, profileName)); + } + return profile.key(); + } + + @Override + public void define(WebService.NewController controller) { + NewAction setDefault = controller.createAction("set_default") + .setSince("5.2") + .setDescription("Select the default profile for a given language.") + .setHandler(this) + .setResponseExample(getClass().getResource("example-search.json")); + + setDefault.createParam(PARAM_LANGUAGE) + .setDescription("The key of a language supported by the platform. If specified, profileName must be set to select the default profile for the selected language.") + .setExampleValue("js") + .setPossibleValues(Collections2.transform(Arrays.asList(languages.all()), new Function() { + @Override + public String apply(Language input) { + return input.getKey(); + } + })); + + setDefault.createParam(PARAM_PROFILE_NAME) + .setDescription("The name of a quality profile. If specified, language must be set. The matching profile will be used as default for the selected language.") + .setExampleValue("sonar-way-js-12345"); + + setDefault.createParam(PARAM_PROFILE_KEY) + .setDescription("The key of a quality profile. If specified, language and profileName must not be set. The matching profile will be used as default for its language.") + .setExampleValue("sonar-way-js-12345"); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultActionTest.java new file mode 100644 index 00000000000..8dac75022ab --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileSetDefaultActionTest.java @@ -0,0 +1,181 @@ +/* + * 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.sonar.server.exceptions.ForbiddenException; + +import org.apache.commons.lang.StringUtils; +import org.assertj.core.api.Fail; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.resources.AbstractLanguage; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Languages; +import org.sonar.api.utils.System2; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.qualityprofile.db.QualityProfileDao; +import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.qualityprofile.QProfileFactory; +import org.sonar.server.qualityprofile.QProfileLookup; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class QProfileSetDefaultActionTest { + + @ClassRule + public static DbTester dbTester = new DbTester(); + + private DbClient dbClient; + + private QualityProfileDao qualityProfileDao; + + private Language xoo1, xoo2; + + private WsTester tester; + + private DbSession session; + + + @Before + public void setUp() throws Exception { + dbTester.truncateTables(); + qualityProfileDao = new QualityProfileDao(dbTester.myBatis(), mock(System2.class)); + dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), qualityProfileDao); + session = dbClient.openSession(false); + + xoo1 = createLanguage("xoo1"); + xoo2 = createLanguage("xoo2"); + createProfiles(); + + tester = new WsTester(new QProfilesWs( + mock(RuleActivationActions.class), + mock(BulkRuleActivationActions.class), + mock(ProjectAssociationActions.class), + new QProfileSetDefaultAction(new Languages(xoo1, xoo2), new QProfileLookup(dbClient), new QProfileFactory(dbClient)))); + } + + @After + public void tearDown() { + session.close(); + } + + @Test + public void set_default_profile_using_key() throws Exception { + MockUserSession.set().setLogin("obiwan").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "my-sonar-way-xoo2-34567"); + + tester.newPostRequest("api/qualityprofiles", "set_default").setParam("profileKey", "sonar-way-xoo2-23456").execute().assertNoContent(); + + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "sonar-way-xoo2-23456"); + assertThat(dbClient.qualityProfileDao().getByKey(session, "sonar-way-xoo2-23456").isDefault()).isTrue(); + assertThat(dbClient.qualityProfileDao().getByKey(session, "my-sonar-way-xoo2-34567").isDefault()).isFalse(); + + // One more time! + tester.newPostRequest("api/qualityprofiles", "set_default").setParam("profileKey", "sonar-way-xoo2-23456").execute().assertNoContent(); + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "sonar-way-xoo2-23456"); + } + + @Test + public void set_default_profile_using_language_and_name() throws Exception { + MockUserSession.set().setLogin("obiwan").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + tester.newPostRequest("api/qualityprofiles", "set_default").setParam("language", "xoo2").setParam("profileName", "Sonar way").execute().assertNoContent(); + + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "sonar-way-xoo2-23456"); + } + + @Test + public void fail_to_set_default_profile_using_key() throws Exception { + MockUserSession.set().setLogin("obiwan").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + try { + tester.newPostRequest("api/qualityprofiles", "set_default").setParam("profileKey", "unknown-profile-666").execute(); + Fail.failBecauseExceptionWasNotThrown(IllegalArgumentException.class); + } catch(IllegalArgumentException nfe) { + assertThat(nfe).hasMessage("Quality profile not found: unknown-profile-666"); + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "my-sonar-way-xoo2-34567"); + } + } + + + @Test + public void fail_to_set_default_profile_using_language_and_name() throws Exception { + MockUserSession.set().setLogin("obiwan").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + try { + tester.newPostRequest("api/qualityprofiles", "set_default").setParam("language", "xoo2").setParam("profileName", "Unknown").execute(); + Fail.failBecauseExceptionWasNotThrown(NotFoundException.class); + } catch(NotFoundException nfe) { + assertThat(nfe).hasMessage("Unable to find a profile for language 'xoo2' with name 'Unknown'"); + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "my-sonar-way-xoo2-34567"); + } + } + + @Test + public void fail_on_missing_permission() throws Exception { + MockUserSession.set().setLogin("obiwan"); + + try { + tester.newPostRequest("api/qualityprofiles", "set_default").setParam("profileKey", "sonar-way-xoo2-23456").execute().assertNoContent(); + Fail.failBecauseExceptionWasNotThrown(ForbiddenException.class); + } catch(ForbiddenException forbidden) { + checkDefaultProfile("xoo1", "sonar-way-xoo1-12345"); + checkDefaultProfile("xoo2", "my-sonar-way-xoo2-34567"); + } + } + + private void createProfiles() { + qualityProfileDao.insert(session, + QualityProfileDto.createFor("sonar-way-xoo1-12345").setLanguage(xoo1.getKey()).setName("Sonar way").setDefault(true), + QualityProfileDto.createFor("sonar-way-xoo2-23456").setLanguage(xoo2.getKey()).setName("Sonar way"), + QualityProfileDto.createFor("my-sonar-way-xoo2-34567").setLanguage(xoo2.getKey()).setName("My Sonar way").setParentKee("sonar-way-xoo2-23456").setDefault(true) + ); + session.commit(); + } + + private void checkDefaultProfile(String language, String key) throws Exception { + assertThat(dbClient.qualityProfileDao().getDefaultProfile(language).getKey()).isEqualTo(key); + } + + private Language createLanguage(final String key) { + return new AbstractLanguage(key, StringUtils.capitalize(key)) { + @Override + public String[] getFileSuffixes() { + return new String[] {key}; + } + }; + } +} -- 2.39.5