From 323bede55a00b3b6e180b3b8951aa63b639bc308 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 30 Jun 2017 09:27:37 +0200 Subject: [PATCH] SONAR-9483 Add "compareToProfile" in rules search ws --- .../server/rule/ws/RuleQueryFactory.java | 78 +++++--- .../sonar/server/rule/ws/SearchAction.java | 17 +- ...xample-search.json => search-example.json} | 0 .../ws/ActivateRulesActionTest.java | 1 + .../ws/DeactivateRulesActionTest.java | 3 +- .../server/rule/ws/RuleQueryFactoryTest.java | 73 +++++++- .../server/rule/ws/SearchActionTest.java | 168 +++++++++++------- .../ws/client/rule/RulesWsParameters.java | 1 + 8 files changed, 244 insertions(+), 97 deletions(-) rename server/sonar-server/src/main/resources/org/sonar/server/rule/ws/{example-search.json => search-example.json} (100%) diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java index 376d2e4f153..0046aa91904 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java @@ -21,7 +21,6 @@ package org.sonar.server.rule.ws; import com.google.common.collect.ImmutableList; import java.util.Date; -import java.util.List; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rules.RuleType; import org.sonar.api.server.ServerSide; @@ -32,13 +31,16 @@ import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.server.rule.index.RuleQuery; -import org.sonar.server.ws.WsUtils; +import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static org.sonar.server.util.EnumUtils.toEnums; +import static org.sonar.server.ws.WsUtils.checkFound; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVATION; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVE_SEVERITIES; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_AVAILABLE_SINCE; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_INHERITANCE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_IS_TEMPLATE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_LANGUAGES; @@ -78,30 +80,12 @@ public class RuleQueryFactory { Boolean activation = request.paramAsBoolean(PARAM_ACTIVATION); query.setActivation(activation); - String profileUuid = request.param(PARAM_QPROFILE); - String organizationKey = request.param(PARAM_ORGANIZATION); - OrganizationDto organization; - List languages; - if (profileUuid == null) { - organization = wsSupport.getOrganizationByKey(dbSession, organizationKey); - languages = request.paramAsStrings(PARAM_LANGUAGES); - } else { - QProfileDto profileOptional = dbClient.qualityProfileDao().selectByUuid(dbSession, profileUuid); - QProfileDto profile = WsUtils.checkFound(profileOptional, "The specified qualityProfile '%s' does not exist", profileUuid); - query.setQProfile(profile); - languages = ImmutableList.of(profile.getLanguage()); - organization = WsUtils.checkFoundWithOptional(dbClient.organizationDao().selectByUuid(dbSession, profile.getOrganizationUuid()), "No organization with UUID ", - profile.getOrganizationUuid()); - if (organizationKey != null) { - OrganizationDto inputOrganization = WsUtils.checkFoundWithOptional(dbClient.organizationDao().selectByKey(dbSession, organizationKey), "No organization with key '%s'", - organizationKey); - if (!organization.getUuid().equals(inputOrganization.getUuid())) { - throw new IllegalArgumentException(format("The specified quality profile '%s' is not part of the specified organization '%s'", profileUuid, organizationKey)); - } - } - } - query.setOrganization(organization); - query.setLanguages(languages); + // Order is important : 1. Load profile, 2. Load organization either from parameter or from profile, 3. Load compare to profile + setProfile(dbSession, query, request); + setOrganization(dbSession, query, request); + setCompareToProfile(dbSession, query, request); + QProfileDto profile = query.getQProfile(); + query.setLanguages(profile == null ? request.paramAsStrings(PARAM_LANGUAGES) : ImmutableList.of(profile.getLanguage())); query.setTags(request.paramAsStrings(PARAM_TAGS)); query.setInheritance(request.paramAsStrings(PARAM_INHERITANCE)); @@ -118,4 +102,46 @@ public class RuleQueryFactory { } return query; } + + private void setProfile(DbSession dbSession, RuleQuery query, Request request) { + String profileUuid = request.param(PARAM_QPROFILE); + if (profileUuid == null) { + return; + } + QProfileDto profileOptional = dbClient.qualityProfileDao().selectByUuid(dbSession, profileUuid); + QProfileDto profile = checkFound(profileOptional, "The specified qualityProfile '%s' does not exist", profileUuid); + query.setQProfile(profile); + } + + private void setOrganization(DbSession dbSession, RuleQuery query, Request request) { + String organizationKey = request.param(PARAM_ORGANIZATION); + QProfileDto profile = query.getQProfile(); + if (profile == null) { + query.setOrganization(wsSupport.getOrganizationByKey(dbSession, organizationKey)); + return; + } + OrganizationDto organization = checkFoundWithOptional(dbClient.organizationDao().selectByUuid(dbSession, profile.getOrganizationUuid()), "No organization with UUID ", + profile.getOrganizationUuid()); + if (organizationKey != null) { + OrganizationDto inputOrganization = checkFoundWithOptional(dbClient.organizationDao().selectByKey(dbSession, organizationKey), "No organization with key '%s'", + organizationKey); + checkArgument(organization.getUuid().equals(inputOrganization.getUuid()), + format("The specified quality profile '%s' is not part of the specified organization '%s'", profile.getKee(), organizationKey)); + } + query.setOrganization(organization); + } + + private void setCompareToProfile(DbSession dbSession, RuleQuery query, Request request) { + String compareToProfileUuid = request.param(PARAM_COMPARE_TO_PROFILE); + if (compareToProfileUuid == null) { + return; + } + QProfileDto profileOptional = dbClient.qualityProfileDao().selectByUuid(dbSession, compareToProfileUuid); + QProfileDto profile = checkFound(profileOptional, "The specified qualityProfile '%s' does not exist", compareToProfileUuid); + + checkArgument(query.getOrganization().getUuid().equals(profile.getOrganizationUuid()), + format("The specified quality profile '%s' is not part of the specified organization '%s'", profile.getKee(), query.getOrganization().getKey())); + + query.setCompareToQProfile(profile); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java index 51fe97c7090..fb5a05efabc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java @@ -26,7 +26,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; -import com.google.common.io.Resources; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -69,6 +68,8 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE; import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; import static org.sonar.api.server.ws.WebService.Param.SORT; import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; +import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; +import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02; import static org.sonar.server.es.SearchOptions.MAX_LIMIT; import static org.sonar.server.rule.index.RuleIndex.ALL_STATUSES_EXCEPT_REMOVED; import static org.sonar.server.rule.index.RuleIndex.FACET_ACTIVE_SEVERITIES; @@ -84,6 +85,7 @@ import static org.sonarqube.ws.client.rule.RulesWsParameters.OPTIONAL_FIELDS; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVATION; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVE_SEVERITIES; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_AVAILABLE_SINCE; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_INHERITANCE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_IS_TEMPLATE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_LANGUAGES; @@ -148,7 +150,6 @@ public class SearchAction implements RulesWsAction { .setPossibleValues(Ordering.natural().sortedCopy(OPTIONAL_FIELDS)); Iterator it = OPTIONAL_FIELDS.iterator(); paramFields.setExampleValue(format("%s,%s", it.next(), it.next())); - doDefinition(action); } @@ -191,7 +192,7 @@ public class SearchAction implements RulesWsAction { "
  • \"defaultDebtRemFnOffset\" becomes \"defaultRemFnBaseEffort\"
  • " + "
  • \"debtOverloaded\" becomes \"remFnOverloaded\"
  • " + "") - .setResponseExample(Resources.getResource(getClass(), "example-search.json")) + .setResponseExample(getClass().getResource("search-example.json")) .setSince("4.4") .setHandler(this); @@ -257,9 +258,15 @@ public class SearchAction implements RulesWsAction { action .createParam(PARAM_QPROFILE) - .setDescription("Key of Quality profile to filter on. Used only if the parameter '" + + .setDescription("Quality profile key to filter on. Used only if the parameter '" + PARAM_ACTIVATION + "' is set.") - .setExampleValue("sonar-way-cs-12345"); + .setExampleValue(UUID_EXAMPLE_01); + + action.createParam(PARAM_COMPARE_TO_PROFILE) + .setDescription("Quality profile key to filter rules that are activated. Meant to compare easily to profile set in '%s'", PARAM_QPROFILE) + .setInternal(true) + .setSince("6.5") + .setExampleValue(UUID_EXAMPLE_02); action .createParam(PARAM_INHERITANCE) diff --git a/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/example-search.json b/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json similarity index 100% rename from server/sonar-server/src/main/resources/org/sonar/server/rule/ws/example-search.json rename to server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java index 1074eda558a..0e475029e23 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java @@ -82,6 +82,7 @@ public class ActivateRulesActionTest { "is_template", "inheritance", "qprofile", + "compareToProfile", "targetSeverity", "tags", "asc", diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java index 7936fb0387a..0312273fc84 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java @@ -28,8 +28,8 @@ import org.sonar.db.DbClient; import org.sonar.db.DbTester; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; -import org.sonar.server.exceptions.BadRequestException; import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.organization.TestDefaultOrganizationProvider; @@ -81,6 +81,7 @@ public class DeactivateRulesActionTest { "is_template", "inheritance", "qprofile", + "compareToProfile", "tags", "asc", "q", diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java index 1010fc65c49..c81b8a7611d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java @@ -31,6 +31,7 @@ import org.sonar.db.DbClient; import org.sonar.db.DbTester; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.rule.index.RuleQuery; import org.sonar.server.ws.TestRequest; @@ -54,6 +55,7 @@ import static org.sonar.server.rule.ws.SearchAction.defineRuleSearchParameters; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVATION; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVE_SEVERITIES; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_AVAILABLE_SINCE; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_INHERITANCE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_IS_TEMPLATE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_LANGUAGES; @@ -72,7 +74,7 @@ public class RuleQueryFactoryTest { @Rule public DbTester dbTester = DbTester.create(System2.INSTANCE); @Rule - public ExpectedException thrown = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); private DbClient dbClient = dbTester.getDbClient(); @@ -108,13 +110,14 @@ public class RuleQueryFactoryTest { assertThat(result.getTags()).isNull(); assertThat(result.templateKey()).isNull(); assertThat(result.getTypes()).isEmpty(); - assertThat(result.getSortField()).isNull(); + assertThat(result.getCompareToQProfile()).isNull(); } @Test public void create_query() throws Exception { QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(organization); RuleQuery result = execute( PARAM_RULE_KEY, "ruleKey", @@ -126,8 +129,9 @@ public class RuleQueryFactoryTest { PARAM_IS_TEMPLATE, "true", PARAM_LANGUAGES, "java,js", TEXT_QUERY, "S001", - PARAM_QPROFILE, qualityProfile.getKee(), PARAM_ORGANIZATION, organization.getKey(), + PARAM_QPROFILE, qualityProfile.getKee(), + PARAM_COMPARE_TO_PROFILE, compareToQualityProfile.getKee(), PARAM_REPOSITORIES, "pmd,checkstyle", PARAM_SEVERITIES, "MINOR,CRITICAL", PARAM_STATUSES, "DEPRECATED,READY", @@ -149,6 +153,7 @@ public class RuleQueryFactoryTest { assertThat(result.getLanguages()).containsOnly(qualityProfile.getLanguage()); assertThat(result.getQueryText()).isEqualTo("S001"); assertThat(result.getQProfile().getKee()).isEqualTo(qualityProfile.getKee()); + assertThat(result.getCompareToQProfile().getKee()).isEqualTo(compareToQualityProfile.getKee()); assertThat(result.getOrganization().getUuid()).isEqualTo(organization.getUuid()); assertThat(result.getRepositories()).containsOnly("pmd", "checkstyle"); assertThat(result.getRuleKey()).isNull(); @@ -205,6 +210,17 @@ public class RuleQueryFactoryTest { assertThat(result.getOrganization().getUuid()).isEqualTo(organization.getUuid()); } + @Test + public void filter_on_compare_to() { + QProfileDto compareToProfile = dbTester.qualityProfiles().insert(organization); + + RuleQuery result = execute( + PARAM_ORGANIZATION, organization.getKey(), + PARAM_COMPARE_TO_PROFILE, compareToProfile.getKee()); + + assertThat(result.getCompareToQProfile().getKee()).isEqualTo(compareToProfile.getKee()); + } + @Test public void fail_if_organization_and_quality_profile_are_contradictory() throws Exception { OrganizationDto organization1 = dbTester.organizations().insert(); @@ -215,13 +231,60 @@ public class RuleQueryFactoryTest { String qualityProfileKey = qualityProfile.getKee(); String organization2Key = organization2.getKey(); - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("The specified quality profile '" + qualityProfileKey + "' is not part of the specified organization '" + organization2Key + "'"); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The specified quality profile '" + qualityProfileKey + "' is not part of the specified organization '" + organization2Key + "'"); execute(PARAM_QPROFILE, qualityProfileKey, PARAM_ORGANIZATION, organization2Key); } + @Test + public void fail_if_organization_and_compare_to_quality_profile_are_contradictory() throws Exception { + OrganizationDto organization = dbTester.organizations().insert(); + QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + + OrganizationDto otherOrganization = dbTester.organizations().insert(); + QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(otherOrganization); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The specified quality profile '" + compareToQualityProfile.getKee() + "' is not part of the specified organization '" + organization.getKey() + "'"); + + execute(PARAM_QPROFILE, qualityProfile.getKee(), + PARAM_COMPARE_TO_PROFILE, compareToQualityProfile.getKee(), + PARAM_ORGANIZATION, organization.getKey()); + } + + @Test + public void fail_when_organization_does_not_exist() { + QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + String qualityProfileKey = qualityProfile.getKee(); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("No organization with key 'unknown'"); + + execute(PARAM_QPROFILE, qualityProfileKey, + PARAM_ORGANIZATION, "unknown"); + } + + @Test + public void fail_when_profile_does_not_exist() { + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("The specified qualityProfile 'unknown' does not exist"); + + execute(PARAM_QPROFILE, "unknown"); + } + + @Test + public void fail_when_compare_to_profile_does_not_exist() { + QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("The specified qualityProfile 'unknown' does not exist"); + + execute(PARAM_QPROFILE, qualityProfile.getKee(), + PARAM_COMPARE_TO_PROFILE, "unknown"); + } + private RuleQuery execute(String... paramsKeyAndValue) { WsActionTester ws = new WsActionTester(fakeAction); TestRequest request = ws.newRequest(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java index 1d5507795ea..4f9108f2256 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java @@ -82,36 +82,42 @@ import static org.mockito.Mockito.mock; import static org.sonar.api.rule.Severity.BLOCKER; import static org.sonar.db.rule.RuleTesting.setSystemTags; import static org.sonar.db.rule.RuleTesting.setTags; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVATION; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ORGANIZATION; +import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_QPROFILE; import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_RULE_KEY; public class SearchActionTest { + private static final String JAVA = "java"; + @org.junit.Rule public UserSessionRule userSession = UserSessionRule.standalone(); @org.junit.Rule - public ExpectedException thrown = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); private System2 system2 = new AlwaysIncreasingSystem2(); @org.junit.Rule - public DbTester dbTester = DbTester.create(system2); + public DbTester db = DbTester.create(system2); @org.junit.Rule public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings())); - private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester); + private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private RuleIndex ruleIndex = new RuleIndex(es.client()); - private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), dbTester.getDbClient()); - private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(dbTester.getDbClient(), es.client(), new ActiveRuleIteratorFactory(dbTester.getDbClient())); - private Languages languages = LanguageTesting.newLanguages("java", "js"); - private ActiveRuleCompleter activeRuleCompleter = new ActiveRuleCompleter(dbTester.getDbClient(), languages); - private RuleWsSupport wsSupport = new RuleWsSupport(dbTester.getDbClient(), userSession, defaultOrganizationProvider); - private RuleQueryFactory ruleQueryFactory = new RuleQueryFactory(dbTester.getDbClient(), wsSupport); + private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient()); + private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client(), new ActiveRuleIteratorFactory(db.getDbClient())); + private Languages languages = LanguageTesting.newLanguages(JAVA, "js"); + private ActiveRuleCompleter activeRuleCompleter = new ActiveRuleCompleter(db.getDbClient(), languages); + private RuleWsSupport wsSupport = new RuleWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider); + private RuleQueryFactory ruleQueryFactory = new RuleQueryFactory(db.getDbClient(), wsSupport); private MacroInterpreter macroInterpreter = mock(MacroInterpreter.class); private RuleMapper ruleMapper = new RuleMapper(languages, macroInterpreter); - private SearchAction underTest = new SearchAction(ruleIndex, activeRuleCompleter, ruleQueryFactory, dbTester.getDbClient(), ruleMapper); + private SearchAction underTest = new SearchAction(ruleIndex, activeRuleCompleter, ruleQueryFactory, db.getDbClient(), ruleMapper); - private RuleActivatorContextFactory contextFactory = new RuleActivatorContextFactory(dbTester.getDbClient()); + private RuleActivatorContextFactory contextFactory = new RuleActivatorContextFactory(db.getDbClient()); private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation())); - private RuleActivator ruleActivator = new RuleActivator(system2, dbTester.getDbClient(), ruleIndex, contextFactory, typeValidations, activeRuleIndexer, + private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), ruleIndex, contextFactory, typeValidations, activeRuleIndexer, userSession); private WsActionTester ws = new WsActionTester(underTest); @@ -122,10 +128,19 @@ public class SearchActionTest { @Test public void test_definition() { - assertThat(ws.getDef().isPost()).isFalse(); - assertThat(ws.getDef().since()).isEqualTo("4.4"); - assertThat(ws.getDef().isInternal()).isFalse(); - assertThat(ws.getDef().params()).hasSize(22); + WebService.Action def = ws.getDef(); + + assertThat(def.isPost()).isFalse(); + assertThat(def.since()).isEqualTo("4.4"); + assertThat(def.isInternal()).isFalse(); + assertThat(def.responseExampleAsString()).isNotEmpty(); + assertThat(def.params()).hasSize(23); + + WebService.Param compareToProfile = def.param("compareToProfile"); + assertThat(compareToProfile.since()).isEqualTo("6.5"); + assertThat(compareToProfile.isRequired()).isFalse(); + assertThat(compareToProfile.isInternal()).isTrue(); + assertThat(compareToProfile.description()).isEqualTo("Quality profile key to filter rules that are activated. Meant to compare easily to profile set in 'qprofile'"); } @Test @@ -208,7 +223,7 @@ public class SearchActionTest { @Test public void should_filter_on_organization_specific_tags() throws IOException { - OrganizationDto organization = dbTester.organizations().insert(); + OrganizationDto organization = db.organizations().insert(); RuleDefinitionDto rule1 = createJavaRule(); RuleMetadataDto metadata1 = insertMetadata(organization, rule1, setTags("tag1", "tag2")); RuleDefinitionDto rule2 = createJavaRule(); @@ -244,8 +259,8 @@ public class SearchActionTest { @Test public void should_list_tags_in_tags_facet() throws IOException { - OrganizationDto organization = dbTester.organizations().insert(); - RuleDefinitionDto rule = dbTester.rules().insert(setSystemTags("tag1", "tag3", "tag5", "tag7", "tag9", "x")); + OrganizationDto organization = db.organizations().insert(); + RuleDefinitionDto rule = db.rules().insert(setSystemTags("tag1", "tag3", "tag5", "tag7", "tag9", "x")); RuleMetadataDto metadata = insertMetadata(organization, rule, setTags("tag2", "tag4", "tag6", "tag8", "tagA")); indexRules(); @@ -260,7 +275,7 @@ public class SearchActionTest { @Test public void should_include_selected_matching_tag_in_facet() throws IOException { - RuleDefinitionDto rule = dbTester.rules().insert(setSystemTags("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tagA", "x")); + RuleDefinitionDto rule = db.rules().insert(setSystemTags("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tagA", "x")); indexRules(); SearchResponse result = ws.newRequest() @@ -272,7 +287,7 @@ public class SearchActionTest { @Test public void should_included_selected_non_matching_tag_in_facet() throws IOException { - RuleDefinitionDto rule = dbTester.rules().insert(setSystemTags("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tagA")); + RuleDefinitionDto rule = db.rules().insert(setSystemTags("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tagA")); indexRules(); SearchResponse result = ws.newRequest() @@ -284,7 +299,7 @@ public class SearchActionTest { @Test public void should_return_organization_specific_tags() throws IOException { - OrganizationDto organization = dbTester.organizations().insert(); + OrganizationDto organization = db.organizations().insert(); RuleDefinitionDto rule = createJavaRule(); RuleMetadataDto metadata = insertMetadata(organization, rule, setTags("tag1", "tag2")); indexRules(); @@ -344,9 +359,9 @@ public class SearchActionTest { @Test public void return_lang_key_field_when_language_name_is_not_available() throws Exception { - OrganizationDto organization = dbTester.organizations().insert(); + OrganizationDto organization = db.organizations().insert(); String unknownLanguage = "unknown_" + randomAlphanumeric(5); - RuleDefinitionDto rule = dbTester.rules().insert(r -> r.setLanguage(unknownLanguage)); + RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage(unknownLanguage)); indexRules(); @@ -364,13 +379,13 @@ public class SearchActionTest { @Test public void search_debt_rules_with_default_and_overridden_debt_values() throws Exception { - RuleDefinitionDto rule = dbTester.rules().insert(r -> + RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("java") .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) .setDefRemediationGapMultiplier("1h") .setDefRemediationBaseEffort("15min")); - RuleMetadataDto metadata = insertMetadata(dbTester.getDefaultOrganization(), rule, + RuleMetadataDto metadata = insertMetadata(db.getDefaultOrganization(), rule, r -> r.setRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) .setRemediationGapMultiplier("2h") .setRemediationBaseEffort("25min")); @@ -399,13 +414,13 @@ public class SearchActionTest { @Test public void search_debt_rules_with_default_linear_offset_and_overridden_constant_debt() throws Exception { - RuleDefinitionDto rule = dbTester.rules().insert(r -> + RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("java") .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) .setDefRemediationGapMultiplier("1h") .setDefRemediationBaseEffort("15min")); - RuleMetadataDto metadata = insertMetadata(dbTester.getDefaultOrganization(), rule, + RuleMetadataDto metadata = insertMetadata(db.getDefaultOrganization(), rule, r -> r.setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()) .setRemediationGapMultiplier(null) .setRemediationBaseEffort("5min")); @@ -435,13 +450,13 @@ public class SearchActionTest { @Test public void search_debt_rules_with_default_linear_offset_and_overridden_linear_debt() throws Exception { - RuleDefinitionDto rule = dbTester.rules().insert(r -> + RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("java") .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) .setDefRemediationGapMultiplier("1h") .setDefRemediationBaseEffort("15min")); - RuleMetadataDto metadata = insertMetadata(dbTester.getDefaultOrganization(), rule, + RuleMetadataDto metadata = insertMetadata(db.getDefaultOrganization(), rule, r -> r.setRemediationFunction(DebtRemediationFunction.Type.LINEAR.name()) .setRemediationGapMultiplier("1h") .setRemediationBaseEffort(null)); @@ -470,10 +485,10 @@ public class SearchActionTest { @Test public void search_template_rules() throws Exception { - RuleDefinitionDto templateRule = dbTester.rules().insert(r -> + RuleDefinitionDto templateRule = db.rules().insert(r -> r.setLanguage("java") .setIsTemplate(true)); - RuleDefinitionDto rule = dbTester.rules().insert(r -> + RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("java") .setTemplateId(templateRule.getId())); @@ -494,10 +509,10 @@ public class SearchActionTest { @Test public void search_custom_rules_from_template_key() throws Exception { - RuleDefinitionDto templateRule = dbTester.rules().insert(r -> + RuleDefinitionDto templateRule = db.rules().insert(r -> r.setLanguage("java") .setIsTemplate(true)); - RuleDefinitionDto rule = dbTester.rules().insert(r -> + RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("java") .setTemplateId(templateRule.getId())); @@ -518,11 +533,11 @@ public class SearchActionTest { @Test public void search_all_active_rules() throws Exception { - OrganizationDto organization = dbTester.organizations().insert(); - QProfileDto profile = dbTester.qualityProfiles().insert(organization, p -> p.setLanguage("java")); + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage("java")); RuleDefinitionDto rule = createJavaRule(); RuleActivation activation = RuleActivation.create(rule.getKey(), BLOCKER, null); - ruleActivator.activate(dbTester.getSession(), activation, profile); + ruleActivator.activate(db.getSession(), activation, profile); indexRules(); @@ -543,38 +558,38 @@ public class SearchActionTest { @Test public void search_profile_active_rules() throws Exception { - OrganizationDto organization = dbTester.organizations().insert(); - QProfileDto profile = dbTester.qualityProfiles().insert(organization, p -> p.setLanguage("java")); - QProfileDto waterproofProfile = dbTester.qualityProfiles().insert(organization, p -> p.setLanguage("java")); + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage("java")); + QProfileDto waterproofProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage("java")); RuleDefinitionDto rule = createJavaRule(); - RuleParamDto ruleParam1 = dbTester.rules().insertRuleParam(rule, p -> + RuleParamDto ruleParam1 = db.rules().insertRuleParam(rule, p -> p.setDefaultValue("some value") .setType("STRING") .setDescription("My small description") .setName("my_var")); - RuleParamDto ruleParam2 = dbTester.rules().insertRuleParam(rule, p -> + RuleParamDto ruleParam2 = db.rules().insertRuleParam(rule, p -> p.setDefaultValue("1") .setType("INTEGER") .setDescription("My small description") .setName("the_var")); // SONAR-7083 - RuleParamDto ruleParam3 = dbTester.rules().insertRuleParam(rule, p -> + RuleParamDto ruleParam3 = db.rules().insertRuleParam(rule, p -> p.setDefaultValue(null) .setType("STRING") .setDescription("Empty Param") .setName("empty_var")); RuleActivation activation = RuleActivation.create(rule.getKey()); - List activeRuleChanges1 = ruleActivator.activate(dbTester.getSession(), activation, profile); - ruleActivator.activate(dbTester.getSession(), activation, waterproofProfile); + List activeRuleChanges1 = ruleActivator.activate(db.getSession(), activation, profile); + ruleActivator.activate(db.getSession(), activation, waterproofProfile); assertThat(activeRuleChanges1).hasSize(1); - dbTester.commit(); + db.commit(); indexRules(); indexActiveRules(); @@ -602,8 +617,8 @@ public class SearchActionTest { ); String unknownProfile = "unknown_profile" + randomAlphanumeric(5); - thrown.expect(NotFoundException.class); - thrown.expectMessage("The specified qualityProfile '" + unknownProfile + "' does not exist"); + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("The specified qualityProfile '" + unknownProfile + "' does not exist"); ws.newRequest() .setParam("activation", "true") @@ -612,26 +627,26 @@ public class SearchActionTest { } @Test - public void test_SONAR7083() { - OrganizationDto organization = dbTester.organizations().insert(); - QProfileDto profile = dbTester.qualityProfiles().insert(organization, p -> p.setLanguage("java")); + public void search_for_active_rules_when_parameter_value_is_null() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage("java")); RuleDefinitionDto rule = createJavaRule(); - RuleParamDto ruleParam = dbTester.rules().insertRuleParam(rule, p -> + RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setDefaultValue("some value") .setType("STRING") .setDescription("My small description") .setName("my_var")); RuleActivation activation = RuleActivation.create(rule.getKey()); - List activeRuleChanges = ruleActivator.activate(dbTester.getSession(), activation, profile); + List activeRuleChanges = ruleActivator.activate(db.getSession(), activation, profile); // Insert directly in database a rule parameter with a null value ActiveRuleParamDto activeRuleParam = ActiveRuleParamDto.createFor(ruleParam).setValue(null); - dbTester.getDbClient().activeRuleDao().insertParam(dbTester.getSession(), activeRuleChanges.get(0).getActiveRule(), activeRuleParam); + db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRuleChanges.get(0).getActiveRule(), activeRuleParam); - dbTester.commit(); + db.commit(); indexRules(); indexActiveRules(); @@ -660,9 +675,9 @@ public class SearchActionTest { @Test public void statuses_facet_should_be_sticky() throws Exception { - RuleDefinitionDto rule1 = dbTester.rules().insert(r -> r.setLanguage("java")); - RuleDefinitionDto rule2 = dbTester.rules().insert(r -> r.setLanguage("java").setStatus(RuleStatus.BETA)); - RuleDefinitionDto rule3 = dbTester.rules().insert(r -> r.setLanguage("java").setStatus(RuleStatus.DEPRECATED)); + RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("java")); + RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("java").setStatus(RuleStatus.BETA)); + RuleDefinitionDto rule3 = db.rules().insert(r -> r.setLanguage("java").setStatus(RuleStatus.DEPRECATED)); indexRules(); @@ -679,6 +694,39 @@ public class SearchActionTest { ); } + @Test + public void compare_to_another_profile() throws Exception { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(JAVA)); + QProfileDto anotherProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage(JAVA)); + RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(JAVA)).getDefinition(); + RuleDefinitionDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(JAVA)).getDefinition(); + RuleDefinitionDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(JAVA)).getDefinition(); + RuleDefinitionDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(JAVA)).getDefinition(); + RuleDefinitionDto anotherProfileRule1 = db.rules().insertRule(r -> r.setLanguage(JAVA)).getDefinition(); + RuleDefinitionDto anotherProfileRule2 = db.rules().insertRule(r -> r.setLanguage(JAVA)).getDefinition(); + db.qualityProfiles().activateRule(profile, commonRule); + db.qualityProfiles().activateRule(profile, profileRule1); + db.qualityProfiles().activateRule(profile, profileRule2); + db.qualityProfiles().activateRule(profile, profileRule3); + db.qualityProfiles().activateRule(anotherProfile, commonRule); + db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule1); + db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2); + indexRules(); + indexActiveRules(); + + SearchResponse result = ws.newRequest() + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .setParam(PARAM_QPROFILE, profile.getKee()) + .setParam(PARAM_ACTIVATION, "false") + .setParam(PARAM_COMPARE_TO_PROFILE, anotherProfile.getKee()) + .executeProtobuf(SearchResponse.class); + + assertThat(result.getRulesList()) + .extracting(Rule::getKey) + .containsExactlyInAnyOrder(anotherProfileRule1.getKey().toString(), anotherProfileRule2.getKey().toString()); + } + @SafeVarargs private final void checkField(RuleDefinitionDto rule, String fieldName, Extractor responseExtractor, T... expected) throws IOException { SearchResponse result = ws.newRequest() @@ -690,7 +738,7 @@ public class SearchActionTest { @SafeVarargs private final RuleMetadataDto insertMetadata(OrganizationDto organization, RuleDefinitionDto rule, Consumer... populaters) { - RuleMetadataDto metadata = dbTester.rules().insertOrUpdateMetadata(rule, organization, populaters); + RuleMetadataDto metadata = db.rules().insertOrUpdateMetadata(rule, organization, populaters); ruleIndexer.indexRuleExtension(organization, rule.getKey()); return metadata; } @@ -723,6 +771,6 @@ public class SearchActionTest { } private RuleDefinitionDto createJavaRule() { - return dbTester.rules().insert(r -> r.setLanguage("java")); + return db.rules().insert(r -> r.setLanguage("java")); } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/rule/RulesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/rule/RulesWsParameters.java index 0024d620430..0cbe4dc7e70 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/rule/RulesWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/rule/RulesWsParameters.java @@ -38,6 +38,7 @@ public class RulesWsParameters { public static final String PARAM_IS_TEMPLATE = "is_template"; public static final String PARAM_TEMPLATE_KEY = "template_key"; public static final String PARAM_ORGANIZATION = "organization"; + public static final String PARAM_COMPARE_TO_PROFILE = "compare_to_profile"; public static final String FIELD_REPO = "repo"; public static final String FIELD_NAME = "name"; -- 2.39.5