From cc6f53471e00a6995fbfd86f70a92a5004acdbed Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Mon, 26 Jun 2017 17:54:19 +0200 Subject: [PATCH] SONAR-8918 Combine filters of WS api/qualityprofiles/search --- .../server/qualityprofile/QProfileLookup.java | 36 +- .../qualityprofile/ws/SearchAction.java | 144 +++-- .../server/qualityprofile/ws/SearchData.java | 5 +- .../qualityprofile/ws/search-example.json | 14 +- .../qualityprofile/ws/QProfilesWsTest.java | 2 +- .../qualityprofile/ws/SearchActionTest.java | 553 +++++++++--------- .../ws/SearchDataLoaderTest.java | 164 +++--- .../ws/SearchActionTest/search.json | 38 -- .../ws/SearchActionTest/search_xoo1.json | 11 - 9 files changed, 472 insertions(+), 495 deletions(-) delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search.json delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_xoo1.json diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java index f309968a288..df0e39812e0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java @@ -32,35 +32,41 @@ import static com.google.common.collect.Lists.newArrayList; @ServerSide public class QProfileLookup { - private final DbClient db; + private final DbClient dbClient; - public QProfileLookup(DbClient db) { - this.db = db; + public QProfileLookup(DbClient dbClient) { + this.dbClient = dbClient; } public List allProfiles(DbSession dbSession, OrganizationDto organization) { - return db.qualityProfileDao().selectOrderedByOrganizationUuid(dbSession, organization); + return dbClient.qualityProfileDao().selectOrderedByOrganizationUuid(dbSession, organization); } public Collection profiles(DbSession dbSession, String language, OrganizationDto organization) { - return db.qualityProfileDao().selectByLanguage(dbSession, organization, language); + return dbClient.qualityProfileDao().selectByLanguage(dbSession, organization, language); } - public List ancestors(QProfileDto profile, DbSession session) { + public List ancestors(QProfileDto profile, DbSession dbSession) { List ancestors = newArrayList(); - incrementAncestors(profile, ancestors, session); + collectAncestors(profile, ancestors, dbSession); return ancestors; } - private void incrementAncestors(QProfileDto profile, List ancestors, DbSession session) { - if (profile.getParentKee() != null) { - QProfileDto parentDto = db.qualityProfileDao().selectByUuid(session, profile.getParentKee()); - if (parentDto == null) { - throw new IllegalStateException("Cannot find parent of profile : " + profile.getId()); - } - ancestors.add(parentDto); - incrementAncestors(parentDto, ancestors, session); + private void collectAncestors(QProfileDto profile, List ancestors, DbSession session) { + if (profile.getParentKee() == null) { + return; } + + QProfileDto parent = getParent(session, profile); + ancestors.add(parent); + collectAncestors(parent, ancestors, session); } + private QProfileDto getParent(DbSession dbSession, QProfileDto profile) { + QProfileDto parent = dbClient.qualityProfileDao().selectByUuid(dbSession, profile.getParentKee()); + if (parent == null) { + throw new IllegalStateException("Cannot find parent of profile: " + profile.getKee()); + } + return parent; + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java index caf8046ba21..ccb4d363021 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java @@ -20,13 +20,21 @@ package org.sonar.server.qualityprofile.ws; import com.google.common.annotations.VisibleForTesting; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleStatus; +import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -37,18 +45,21 @@ import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.util.LanguageParamUtils; import org.sonarqube.ws.QualityProfiles.SearchWsResponse; import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.client.component.ComponentsWsParameters; import org.sonarqube.ws.client.qualityprofile.SearchWsRequest; +import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; import static java.util.function.Function.identity; import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.core.util.Protobuf.setNullable; +import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; -import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_SEARCH; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS; @@ -56,20 +67,22 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_NAME; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT; -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY; public class SearchAction implements QProfileWsAction { + private static final Comparator Q_PROFILE_COMPARATOR = Comparator + .comparing(QProfileDto::getLanguage) + .thenComparing(QProfileDto::getName); - private final SearchDataLoader dataLoader; private final Languages languages; private final DbClient dbClient; private final QProfileWsSupport wsSupport; + private final ComponentFinder componentFinder; - public SearchAction(SearchDataLoader dataLoader, Languages languages, DbClient dbClient, QProfileWsSupport wsSupport) { - this.dataLoader = dataLoader; + public SearchAction(Languages languages, DbClient dbClient, QProfileWsSupport wsSupport, ComponentFinder componentFinder) { this.languages = languages; this.dbClient = dbClient; this.wsSupport = wsSupport; + this.componentFinder = componentFinder; } @Override @@ -78,6 +91,7 @@ public class SearchAction implements QProfileWsAction { .setSince("5.2") .setDescription("Search quality profiles") .setHandler(this) + .setChangelog(new Change("6.5", format("The parameters '%s', '%s' and '%s' can be combined without any constraint", PARAM_DEFAULTS, PARAM_PROJECT, PARAM_LANGUAGE))) .setResponseExample(getClass().getResource("search-example.json")); action @@ -91,26 +105,22 @@ public class SearchAction implements QProfileWsAction { action .createParam(PARAM_DEFAULTS) - .setDescription(format("If set to true, return only the quality profile marked as default for each language, the '%s' parameter must not be set.", PARAM_PROJECT)) + .setDescription("If set to true, return only the quality profiles marked as default for each language") .setDefaultValue(false) .setBooleanPossibleValues(); action.createParam(PARAM_PROJECT) - .setDescription(format("Project or module key. If provided, the '%s' parameter should not be provided.", PARAM_DEFAULTS)) + .setDescription("Project key") .setDeprecatedKey("projectKey", "6.5") - .setExampleValue("my-project-key"); + .setExampleValue(KEY_PROJECT_EXAMPLE_001); action .createParam(PARAM_LANGUAGE) - .setDeprecatedSince("6.4") - .setDescription( - format("Language key. If provided, only profiles for the given language are returned. " + - "It should not be used with '%s', '%s or '%s' at the same time.", PARAM_DEFAULTS, PARAM_PROJECT_KEY, PARAM_PROFILE_NAME)) + .setDescription("Language key. If provided, only profiles for the given language are returned.") .setPossibleValues(LanguageParamUtils.getLanguageKeys(languages)); action.createParam(PARAM_PROFILE_NAME) - .setDeprecatedSince("6.4") - .setDescription(format("Profile name. It should be always used with the '%s' or '%s' parameter.", PARAM_PROJECT, PARAM_DEFAULTS)) + .setDescription("Profile name") .setExampleValue("SonarQube Way"); } @@ -131,7 +141,6 @@ public class SearchAction implements QProfileWsAction { @VisibleForTesting SearchWsResponse doHandle(SearchWsRequest request) { - validateRequest(request); SearchData data = load(request); return buildResponse(data); } @@ -139,28 +148,11 @@ public class SearchAction implements QProfileWsAction { private SearchData load(SearchWsRequest request) { try (DbSession dbSession = dbClient.openSession(false)) { - @Nullable ComponentDto project; - OrganizationDto organization; - if (request.getProjectKey() == null) { - project = null; - organization = wsSupport.getOrganizationByKey(dbSession, request.getOrganizationKey()); - } else { - project = getProject(request.getProjectKey(), dbSession); - organization = dbClient.organizationDao().selectByUuid(dbSession, project.getOrganizationUuid()) - .orElseThrow(() -> new IllegalStateException( - String.format("Organization with uuid '%s' is referenced by project '%s' but could not be found", project.getOrganizationUuid(), project.getKey()))); - if (request.getOrganizationKey() != null && !request.getOrganizationKey().equals(organization.getKey())) { - throw new IllegalArgumentException(String.format("The provided organization key '%s' does not match the organization key '%s' of the component '%s'", - request.getOrganizationKey(), - organization.getKey(), - project.getKey() - )); - } - } + OrganizationDto organization = wsSupport.getOrganizationByKey(dbSession, request.getOrganizationKey()); + ComponentDto project = findProject(dbSession, organization, request); - List profiles = dataLoader.findProfiles(dbSession, request, organization, project); - List profileUuids = profiles.stream().map(QProfileDto::getKee).collect(MoreCollectors.toList()); - Set defaultProfiles = dbClient.defaultQProfileDao().selectExistingQProfileUuids(dbSession, organization.getUuid(), profileUuids); + List defaultProfiles = dbClient.qualityProfileDao().selectDefaultProfiles(dbSession, organization, getLanguageKeys()); + List profiles = searchProfiles(dbSession, request, organization, defaultProfiles, project); return new SearchData() .setOrganization(organization) @@ -172,6 +164,73 @@ public class SearchAction implements QProfileWsAction { } } + @CheckForNull + private ComponentDto findProject(DbSession dbSession, OrganizationDto organization, SearchWsRequest request) { + if (request.getProjectKey() == null) { + return null; + } + + ComponentDto project = componentFinder.getByKey(dbSession, request.getProjectKey()); + if (!project.getOrganizationUuid().equals(organization.getUuid())) { + throw new NotFoundException(format("Component key '%s' not found", project.getKey())); + } + if (project.isRoot()) { + return project; + } + ComponentDto component = dbClient.componentDao().selectByUuid(dbSession, project.projectUuid()).orNull(); + checkState(component != null, "Project uuid of component uuid '%s' does not exist", project.uuid()); + return component; + } + + private List searchProfiles(DbSession dbSession, SearchWsRequest request, OrganizationDto organization, List defaultProfiles, + @Nullable ComponentDto project) { + Collection profiles = selectAllProfiles(dbSession, organization); + + return profiles.stream() + .filter(hasLanguagePlugin()) + .filter(byLanguage(request)) + .filter(byName(request)) + .filter(byDefault(request, defaultProfiles)) + .filter(byProject(dbSession, project, defaultProfiles)) + .sorted(Q_PROFILE_COMPARATOR) + .collect(Collectors.toList()); + } + + private Predicate hasLanguagePlugin() { + return p -> languages.get(p.getLanguage()) != null; + } + + private static Predicate byName(SearchWsRequest request) { + return p -> request.getProfileName() == null || Objects.equals(p.getName(), request.getProfileName()); + } + + private static Predicate byLanguage(SearchWsRequest request) { + return p -> request.getLanguage() == null || Objects.equals(p.getLanguage(), request.getLanguage()); + } + + private static Predicate byDefault(SearchWsRequest request, List defaultProfiles) { + Set defaultProfileUuids = defaultProfiles.stream().map(QProfileDto::getKee).collect(Collectors.toSet()); + return p -> !request.getDefaults() || defaultProfileUuids.contains(p.getKee()); + } + + private Predicate byProject(DbSession dbSession, @Nullable ComponentDto project, List defaultProfiles) { + if (project == null) { + return p -> true; + } + Map effectiveProfiles = defaultProfiles.stream().collect(Collectors.toMap(QProfileDto::getLanguage, identity())); + effectiveProfiles.putAll(dbClient.qualityProfileDao().selectAssociatedToProjectUuidAndLanguages(dbSession, project, getLanguageKeys()).stream() + .collect(MoreCollectors.uniqueIndex(QProfileDto::getLanguage))); + return p -> Objects.equals(p.getKee(), effectiveProfiles.get(p.getLanguage()).getKee()); + } + + private Collection selectAllProfiles(DbSession dbSession, OrganizationDto organization) { + return dbClient.qualityProfileDao().selectOrderedByOrganizationUuid(dbSession, organization); + } + + private Set getLanguageKeys() { + return Arrays.stream(languages.all()).map(Language::getKey).collect(MoreCollectors.toSet()); + } + private ComponentDto getProject(String moduleKey, DbSession dbSession) { ComponentDto module = checkFoundWithOptional(dbClient.componentDao().selectByKey(dbSession, moduleKey), "Component key '%s' not found", moduleKey); if (module.isRootProject()) { @@ -180,18 +239,6 @@ public class SearchAction implements QProfileWsAction { return dbClient.componentDao().selectOrFailByUuid(dbSession, module.projectUuid()); } - private static void validateRequest(SearchWsRequest request) { - boolean hasLanguage = request.getLanguage() != null; - boolean isDefault = request.getDefaults(); - boolean hasComponentKey = request.getProjectKey() != null; - boolean hasProfileName = request.getProfileName() != null; - - checkRequest(!hasLanguage || (!hasComponentKey && !hasProfileName && !isDefault), - "The language parameter cannot be provided at the same time than the component key or profile name."); - checkRequest(!isDefault || !hasComponentKey, "The default parameter cannot be provided at the same time than the component key."); - checkRequest(!hasProfileName || hasComponentKey || isDefault, "The name parameter requires either projectKey or defaults to be set."); - } - private SearchWsResponse buildResponse(SearchData data) { List profiles = data.getProfiles(); Map profilesByKey = profiles.stream().collect(Collectors.toMap(QProfileDto::getKee, identity())); @@ -250,4 +297,5 @@ public class SearchAction implements QProfileWsAction { profileBuilder.setParentName(parent.getName()); } } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchData.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchData.java index be04202b33d..cf7f39fd759 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchData.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchData.java @@ -22,6 +22,7 @@ package org.sonar.server.qualityprofile.ws; import java.util.List; import java.util.Map; import java.util.Set; +import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QProfileDto; @@ -86,8 +87,8 @@ public class SearchData { return defaultProfileKeys.contains(profile.getKee()); } - SearchData setDefaultProfileKeys(Set s) { - this.defaultProfileKeys = s; + SearchData setDefaultProfileKeys(List s) { + this.defaultProfileKeys = s.stream().map(QProfileDto::getKee).collect(MoreCollectors.toSet()); return this; } } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/search-example.json index 479ee088d55..07ff1f2b8aa 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/search-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/search-example.json @@ -7,7 +7,7 @@ "languageName": "C#", "isInherited": false, "isBuiltIn": true, - "activeRuleCount": 37, + "activeRuleCount": 3, "activeDeprecatedRuleCount": 0, "isDefault": true, "ruleUpdatedAt": "2016-12-22T19:10:03+0100", @@ -20,12 +20,12 @@ "languageName": "Java", "isInherited": true, "isBuiltIn": false, - "parentKey": "my-company-profile-java-23456", + "parentKey": "iU5OvuD2FLz", "parentName": "My Company Profile", - "activeRuleCount": 72, + "activeRuleCount": 15, "activeDeprecatedRuleCount": 5, "isDefault": false, - "projectCount": 13, + "projectCount": 7, "ruleUpdatedAt": "2016-12-20T19:10:03+0100", "lastUsed": "2016-12-21T16:10:03+0100", "userUpdatedAt": "2016-06-28T21:57:01+0200" @@ -38,8 +38,8 @@ "isInherited": false, "isDefault": true, "isBuiltIn": false, - "activeRuleCount": 42, - "activeDeprecatedRuleCount": 3, + "activeRuleCount": 9, + "activeDeprecatedRuleCount": 2, "ruleUpdatedAt": "2016-12-22T19:10:03+0100", "userUpdatedAt": "2016-06-29T21:57:01+0200" }, @@ -50,7 +50,7 @@ "languageName": "Python", "isInherited": false, "isBuiltIn": true, - "activeRuleCount": 125, + "activeRuleCount": 2, "activeDeprecatedRuleCount": 0, "isDefault": true, "ruleUpdatedAt": "2014-12-22T19:10:03+0100" diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java index 83755848d63..26cd142d965 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java @@ -60,7 +60,7 @@ public class QProfilesWsTest { controller = new WsTester(new QProfilesWs( new CreateAction(null, null, null, languages, wsSupport, userSessionRule, null, importers), new ImportersAction(importers), - new SearchAction(null, languages, dbClient, wsSupport), + new SearchAction(languages, dbClient, wsSupport, null), new SetDefaultAction(languages, null, null, wsSupport), new ProjectsAction(null, userSessionRule, wsSupport), new ChangelogAction(null, wsSupport, languages, dbClient), diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java index 6ce4f9bfa7e..c5764d1fb0c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java @@ -19,423 +19,408 @@ */ package org.sonar.server.qualityprofile.ws; -import java.util.Arrays; -import java.util.function.Function; -import org.junit.Before; +import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; -import org.sonar.api.rule.RuleStatus; +import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; 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.DbTester; -import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.organization.OrganizationTesting; import org.sonar.db.qualityprofile.QProfileDto; -import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.qualityprofile.QualityProfileDbTester; import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.language.LanguageTesting; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; -import org.sonar.server.qualityprofile.QProfileLookup; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; -import org.sonarqube.ws.QualityProfiles; +import org.sonarqube.ws.MediaTypes; import org.sonarqube.ws.QualityProfiles.SearchWsResponse; import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; -import org.sonarqube.ws.client.qualityprofile.SearchWsRequest; +import static java.lang.String.format; +import static java.util.stream.IntStream.range; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +import static org.sonar.api.rule.RuleStatus.DEPRECATED; import static org.sonar.api.utils.DateUtils.parseDateTime; +import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto; +import static org.sonar.server.language.LanguageTesting.newLanguage; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_NAME; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY; public class SearchActionTest { + private static Language XOO1 = newLanguage("xoo1"); + private static Language XOO2 = newLanguage("xoo2"); + private static Languages LANGUAGES = new Languages(XOO1, XOO2); + @Rule public DbTester db = DbTester.create(System2.INSTANCE); @Rule public UserSessionRule userSession = UserSessionRule.standalone(); @Rule - public ExpectedException thrown = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); private QualityProfileDbTester qualityProfileDb = db.qualityProfiles(); - private ComponentDbTester componentDb = new ComponentDbTester(db); private DbClient dbClient = db.getDbClient(); - private DbSession dbSession = db.getSession(); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private QProfileWsSupport qProfileWsSupport = new QProfileWsSupport(dbClient, userSession, defaultOrganizationProvider); - private Language xoo1; - private Language xoo2; - - private WsActionTester ws; - private SearchAction underTest; - - @Before - public void setUp() { - xoo1 = LanguageTesting.newLanguage("xoo1"); - xoo2 = LanguageTesting.newLanguage("xoo2"); - - Languages languages = new Languages(xoo1, xoo2); - underTest = new SearchAction( - new SearchDataLoader(languages, new QProfileLookup(dbClient), dbClient), - languages, - dbClient, - qProfileWsSupport); - ws = new WsActionTester(underTest); - } + private SearchAction underTest = new SearchAction(LANGUAGES, dbClient, qProfileWsSupport, new ComponentFinder(dbClient, null)); + private WsActionTester ws = new WsActionTester(underTest); @Test public void definition() { - WebService.Action action = ws.getDef(); - assertThat(action.key()).isEqualTo("search"); - assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.isPost()).isFalse(); + WebService.Action definition = ws.getDef(); + + assertThat(definition.key()).isEqualTo("search"); + assertThat(definition.responseExampleAsString()).isNotEmpty(); + assertThat(definition.isPost()).isFalse(); - WebService.Param organization = action.param("organization"); + assertThat(definition.changelog()) + .extracting(Change::getVersion, Change::getDescription) + .containsExactlyInAnyOrder(tuple("6.5", "The parameters 'defaults', 'project' and 'language' can be combined without any constraint")); + + WebService.Param organization = definition.param("organization"); assertThat(organization).isNotNull(); assertThat(organization.isRequired()).isFalse(); assertThat(organization.isInternal()).isTrue(); assertThat(organization.description()).isNotEmpty(); assertThat(organization.since()).isEqualTo("6.4"); - WebService.Param defaults = action.param("defaults"); - assertThat(defaults.description()).isEqualTo("If set to true, return only the quality profile marked as default for each language, " + - "the 'project' parameter must not be set."); + WebService.Param defaults = definition.param("defaults"); + assertThat(defaults.defaultValue()).isEqualTo("false"); + assertThat(defaults.description()).isEqualTo("If set to true, return only the quality profiles marked as default for each language"); - WebService.Param projectKey = action.param("project"); - assertThat(projectKey.description()).isEqualTo("Project or module key. If provided, the 'defaults' parameter should not be provided."); + WebService.Param projectKey = definition.param("project"); + assertThat(projectKey.description()).isEqualTo("Project key"); assertThat(projectKey.deprecatedKey()).isEqualTo("projectKey"); - WebService.Param language = action.param("language"); + WebService.Param language = definition.param("language"); assertThat(language.possibleValues()).containsExactly("xoo1", "xoo2"); - assertThat(language.deprecatedSince()).isEqualTo("6.4"); - assertThat(language.description()).isEqualTo("Language key. If provided, only profiles for the given language are returned. " + - "It should not be used with 'defaults', 'projectKey or 'profileName' at the same time."); + assertThat(language.deprecatedSince()).isNull(); + assertThat(language.description()).isEqualTo("Language key. If provided, only profiles for the given language are returned."); - WebService.Param profileName = action.param("profileName"); - assertThat(profileName.deprecatedSince()).isEqualTo("6.4"); - assertThat(profileName.description()).isEqualTo("Profile name. It should be always used with the 'project' or 'defaults' parameter."); + WebService.Param profileName = definition.param("profileName"); + assertThat(profileName.deprecatedSince()).isNull(); + assertThat(profileName.description()).isEqualTo("Profile name"); } @Test - public void ws_returns_the_profiles_of_default_organization() throws Exception { - OrganizationDto organization = getDefaultOrganization(); - - QProfileDto defaultProfile = db.qualityProfiles().insert(organization, p -> - p.setLanguage(xoo1.getKey()).setName("Sonar way").setKee("sonar-way-xoo1-12345").setIsBuiltIn(false)); - QProfileDto parentProfile = db.qualityProfiles().insert(organization, p -> - p.setLanguage(xoo2.getKey()).setName("Sonar way").setKee("sonar-way-xoo2-23456").setIsBuiltIn(true)); - QProfileDto childProfile = db.qualityProfiles().insert(organization, p -> - p.setLanguage(xoo2.getKey()).setName("My Sonar way").setKee("my-sonar-way-xoo2-34567").setIsBuiltIn(false).setParentKee(parentProfile.getKee())); - QProfileDto profileOnUnknownLang = db.qualityProfiles().insert(organization, p -> - p.setLanguage("other").setName("Sonar way").setKee("sonar-way-other-666")); - db.qualityProfiles().setAsDefault(defaultProfile, profileOnUnknownLang); - - ComponentDto project1 = db.components().insertPrivateProject(organization); - ComponentDto project2 = db.components().insertPrivateProject(organization); - db.qualityProfiles().associateWithProject(project1, parentProfile); - db.qualityProfiles().associateWithProject(project2, parentProfile); - - SearchWsResponse result = ws.newRequest().executeProtobuf(SearchWsResponse.class); - - Function getParentKey = qp -> qp.hasParentKey() ? qp.getParentKey() : null; - Function getParentName = qp -> qp.hasParentName() ? qp.getParentName() : null; - Function getProjectCount = qp -> qp.hasProjectCount() ? qp.getProjectCount() : null; - assertThat(result.getProfilesList()) - .extracting(QualityProfile::getKey, QualityProfile::getName, QualityProfile::getLanguage, QualityProfile::getLanguageName, - QualityProfile::getIsInherited, QualityProfile::getIsDefault, QualityProfile::getIsBuiltIn, - QualityProfile::getActiveRuleCount, QualityProfile::getActiveDeprecatedRuleCount, getProjectCount, - QualityProfile::getOrganization, getParentKey, getParentName) - .containsExactlyInAnyOrder( - tuple("sonar-way-xoo1-12345", "Sonar way", "xoo1", "Xoo1", false, true, false, 0L, 0L, null, organization.getKey(), null, null), - tuple("my-sonar-way-xoo2-34567", "My Sonar way", "xoo2", "Xoo2", true, false, false, 0L, 0L, 0L, organization.getKey(), "sonar-way-xoo2-23456", "Sonar way"), - tuple("sonar-way-xoo2-23456", "Sonar way", "xoo2", "Xoo2", false, false, true, 0L, 0L, 2L, organization.getKey(), null, null)); + public void default_organization() { + OrganizationDto defaultOrganization = db.getDefaultOrganization(); + OrganizationDto anotherOrganization = db.organizations().insert(); + QProfileDto profile1OnDefaultOrg = db.qualityProfiles().insert(defaultOrganization, p -> p.setLanguage(XOO1.getKey())); + QProfileDto profile2OnDefaultOrg = db.qualityProfiles().insert(defaultOrganization, p -> p.setLanguage(XOO2.getKey())); + QProfileDto profileOnAnotherOrg = db.qualityProfiles().insert(anotherOrganization, p -> p.setLanguage(XOO1.getKey())); + + SearchWsResponse result = call(ws.newRequest()); + + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(profile1OnDefaultOrg.getKee(), profile2OnDefaultOrg.getKee()) + .doesNotContain(profileOnAnotherOrg.getKee()); } @Test - public void response_contains_statistics_on_active_rules() { - QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo1.getKey())); - RuleDefinitionDto rule = db.rules().insertRule(r -> r.setLanguage(xoo1.getKey())).getDefinition(); - RuleDefinitionDto deprecatedRule1 = db.rules().insertRule(r -> r.setStatus(RuleStatus.DEPRECATED)).getDefinition(); - RuleDefinitionDto deprecatedRule2 = db.rules().insertRule(r -> r.setStatus(RuleStatus.DEPRECATED)).getDefinition(); - db.qualityProfiles().activateRule(profile, rule); - db.qualityProfiles().activateRule(profile, deprecatedRule1); - db.qualityProfiles().activateRule(profile, deprecatedRule2); + public void specific_organization() { + OrganizationDto defaultOrganization = db.getDefaultOrganization(); + OrganizationDto specificOrganization = db.organizations().insert(); + QProfileDto profile1OnSpecificOrg = db.qualityProfiles().insert(specificOrganization, p -> p.setLanguage(XOO1.getKey())); + QProfileDto profile2OnSpecificOrg = db.qualityProfiles().insert(specificOrganization, p -> p.setLanguage(XOO2.getKey())); + QProfileDto profileOnDefaultOrg = db.qualityProfiles().insert(defaultOrganization, p -> p.setLanguage(XOO1.getKey())); + + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_ORGANIZATION, specificOrganization.getKey())); + + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(profile1OnSpecificOrg.getKee(), profile2OnSpecificOrg.getKee()) + .doesNotContain(profileOnDefaultOrg.getKee()); + } - String result = ws.newRequest().execute().getInput(); + @Test + public void filter_on_default_profile() { + QProfileDto defaultProfile1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + QProfileDto defaultProfile2 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO2.getKey())); + QProfileDto nonDefaultProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + db.qualityProfiles().setAsDefault(defaultProfile1, defaultProfile2); - assertJson(result).isSimilarTo("{\"profiles\":[" + - "{" + - " \"key\":\"" + profile.getKee() + "\"," + - " \"activeRuleCount\":3," + - " \"activeDeprecatedRuleCount\":2" + - "}]}"); + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_DEFAULTS, "true")); + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(defaultProfile1.getKee(), defaultProfile2.getKee()) + .doesNotContain(nonDefaultProfile.getKee()); } @Test - public void search_map_dates() { - long time = DateUtils.parseDateTime("2016-12-22T19:10:03+0100").getTime(); - qualityProfileDb.insert(newQualityProfileDto() - .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) - .setLanguage(xoo1.getKey()) - .setRulesUpdatedAt("2016-12-21T19:10:03+0100") - .setLastUsed(time) - .setUserUpdatedAt(time)); + public void does_not_filter_when_defaults_is_false() { + QProfileDto defaultProfile1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + QProfileDto defaultProfile2 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO2.getKey())); + QProfileDto nonDefaultProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + db.qualityProfiles().setAsDefault(defaultProfile1, defaultProfile2); - SearchWsResponse result = ws.newRequest() - .executeProtobuf(SearchWsResponse.class); + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_DEFAULTS, "false")); - assertThat(result.getProfilesCount()).isEqualTo(1); - assertThat(result.getProfiles(0).getRulesUpdatedAt()).isEqualTo("2016-12-21T19:10:03+0100"); - assertThat(parseDateTime(result.getProfiles(0).getLastUsed()).getTime()).isEqualTo(time); - assertThat(parseDateTime(result.getProfiles(0).getUserUpdatedAt()).getTime()).isEqualTo(time); + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(defaultProfile1.getKee(), defaultProfile2.getKee(), nonDefaultProfile.getKee()); } @Test - public void search_for_language() throws Exception { - qualityProfileDb.insert( - new QProfileDto() - .setKee("sonar-way-xoo1-12345") - .setRulesProfileUuid("rp-sonar-way-xoo1-12345") - .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) - .setLanguage(xoo1.getKey()) - .setName("Sonar way")); - - String result = ws.newRequest().setParam("language", xoo1.getKey()).execute().getInput(); - - assertJson(result).isSimilarTo(getClass().getResource("SearchActionTest/search_xoo1.json")); + public void filter_on_language() { + QProfileDto profile1OnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + QProfileDto profile2OnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + QProfileDto profileOnXoo2 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO2.getKey())); + + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_LANGUAGE, XOO1.getKey())); + + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(profile1OnXoo1.getKee(), profile2OnXoo1.getKee()) + .doesNotContain(profileOnXoo2.getKee()); } @Test - public void search_for_project_qp() { - OrganizationDto org = db.organizations().insert(); - ComponentDto project = db.components().insertPrivateProject(org); - QProfileDto qualityProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey())); - db.qualityProfiles().associateWithProject(project, qualityProfileOnXoo1); - QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo2.getKey())); - db.qualityProfiles().associateWithProject(project, defaultProfileOnXoo2); - QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey())); - db.qualityProfiles().setAsDefault(defaultProfileOnXoo2, defaultProfileOnXoo1); - - SearchWsResponse result = ws.newRequest() - .setParam(PARAM_PROJECT_KEY, project.key()) - .executeProtobuf(SearchWsResponse.class); + public void ignore_profiles_on_unknown_language() { + QProfileDto profile1OnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + QProfileDto profile2OnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO2.getKey())); + QProfileDto profileOnUnknownLanguage = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage("unknown")); - assertThat(result.getProfilesList()) - .extracting(QualityProfile::getKey) - .containsExactlyInAnyOrder(qualityProfileOnXoo1.getKee(), defaultProfileOnXoo2.getKee()); + SearchWsResponse result = call(ws.newRequest()); + + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(profile1OnXoo1.getKee(), profile2OnXoo1.getKee()) + .doesNotContain(profileOnUnknownLanguage.getKee()); } @Test - public void search_for_project_qp_with_wrong_organization() { - OrganizationDto org1 = db.organizations().insert(); - OrganizationDto org2 = db.organizations().insert(); - ComponentDto project = db.components().insertPrivateProject(org1); + public void filter_on_profile_name() { + QProfileDto sonarWayOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Sonar way").setLanguage(XOO1.getKey())); + QProfileDto sonarWayOnXoo2 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Sonar way").setLanguage(XOO1.getKey())); + QProfileDto sonarWayInCamelCase = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Sonar Way").setLanguage(XOO2.getKey())); + QProfileDto anotherProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Another").setLanguage(XOO2.getKey())); - TestRequest request = ws.newRequest() - .setParam(PARAM_PROJECT_KEY, project.key()) - .setParam(PARAM_ORGANIZATION, org2.getKey()); - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "The provided organization key '" + org2.getKey() + "' does not match the organization key '" + org1.getKey() + "' of the component '" + project.getKey() + "'"); + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_PROFILE_NAME, "Sonar way")); - request.execute(); + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(sonarWayOnXoo1.getKee(), sonarWayOnXoo2.getKee()) + .doesNotContain(anotherProfile.getKee(), sonarWayInCamelCase.getKee()); } @Test - public void search_for_default_qp_with_profile_name() { - String orgUuid = defaultOrganizationProvider.get().getUuid(); - QProfileDto qualityProfileOnXoo1 = new QProfileDto() - .setKee("sonar-way-xoo1-12345") - .setRulesProfileUuid("rp-sonar-way-xoo1-12345") - .setOrganizationUuid(orgUuid) - .setLanguage(xoo1.getKey()) - .setName("Sonar way"); - QProfileDto qualityProfileOnXoo2 = new QProfileDto() - .setKee("sonar-way-xoo2-12345") - .setRulesProfileUuid("rp-sonar-way-xoo2-12345") - .setOrganizationUuid(orgUuid) - .setLanguage(xoo2.getKey()) - .setName("Sonar way"); - QProfileDto anotherQualityProfileOnXoo1 = new QProfileDto() - .setKee("sonar-way-xoo1-45678") - .setRulesProfileUuid("rp-sonar-way-xoo1-45678") - .setOrganizationUuid(orgUuid) - .setLanguage(xoo1.getKey()) - .setName("Another way"); - qualityProfileDb.insert(qualityProfileOnXoo1, qualityProfileOnXoo2, anotherQualityProfileOnXoo1); - db.qualityProfiles().setAsDefault(qualityProfileOnXoo2, anotherQualityProfileOnXoo1); - - String result = ws.newRequest() - .setParam(PARAM_DEFAULTS, Boolean.TRUE.toString()) - .setParam(PARAM_PROFILE_NAME, "Sonar way") - .execute().getInput(); - - assertThat(result) - .contains("sonar-way-xoo1-12345", "sonar-way-xoo2-12345") - .doesNotContain("sonar-way-xoo1-45678"); + public void filter_on_defaults_and_name() { + QProfileDto sonarWayOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Sonar way").setLanguage(XOO1.getKey())); + QProfileDto sonarWayOnXoo2 = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Sonar way").setLanguage(XOO2.getKey())); + QProfileDto anotherProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setName("Another").setLanguage(XOO2.getKey())); + db.qualityProfiles().setAsDefault(sonarWayOnXoo1, anotherProfile); + + SearchWsResponse result = call(ws.newRequest() + .setParam(PARAM_DEFAULTS, "true") + .setParam(PARAM_PROFILE_NAME, "Sonar way")); + + assertThat(result.getProfilesList()).extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(sonarWayOnXoo1.getKee()) + .doesNotContain(sonarWayOnXoo2.getKee(), anotherProfile.getKee()); } @Test - public void search_by_profile_name() { - OrganizationDto org = db.organizations().insert(); - QProfileDto qualityProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey()).setName("Sonar way")); - QProfileDto qualityProfileOnXoo2 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo2.getKey()).setName("Sonar way")); - QProfileDto anotherQualityProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey()).setName("Another way")); - db.qualityProfiles().setAsDefault(qualityProfileOnXoo2, anotherQualityProfileOnXoo1); - ComponentDto project = componentDb.insertPrivateProject(org); - - SearchWsResponse result = ws.newRequest() - .setParam(PARAM_ORGANIZATION, org.getKey()) - .setParam(PARAM_PROJECT_KEY, project.key()) - .setParam(PARAM_PROFILE_NAME, "Sonar way") - .executeProtobuf(SearchWsResponse.class); + public void filter_on_project_key() { + ComponentDto project = db.components().insertPrivateProject(); + QProfileDto profileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO2.getKey())); + db.qualityProfiles().associateWithProject(project, profileOnXoo1); + db.qualityProfiles().setAsDefault(defaultProfileOnXoo1, defaultProfileOnXoo2); + + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_PROJECT_KEY, project.key())); assertThat(result.getProfilesList()) .extracting(QualityProfile::getKey) - .contains(qualityProfileOnXoo1.getKee(), qualityProfileOnXoo2.getKee()) - .doesNotContain(anotherQualityProfileOnXoo1.getKee()); + .containsExactlyInAnyOrder(profileOnXoo1.getKee(), defaultProfileOnXoo2.getKee()) + .doesNotContain(defaultProfileOnXoo1.getKee()); } @Test - public void search_default_profile_by_profile_name_and_org() { - for (String orgKey : Arrays.asList("ORG1", "ORG2")) { - OrganizationDto org = db.organizations().insert(OrganizationTesting.newOrganizationDto().setKey(orgKey)); - dbClient.qualityProfileDao().insert(dbSession, createProfile("A", xoo1, org, "MATCH")); - dbClient.qualityProfileDao().insert(dbSession, createProfile("B", xoo2, org, "NOMATCH")); - QProfileDto defaultProfileC = createProfile("C", xoo1, org, "NOMATCH"); - dbClient.qualityProfileDao().insert(dbSession, defaultProfileC); - QProfileDto defaultProfileD = createProfile("D", xoo2, org, "NOMATCH"); - dbClient.qualityProfileDao().insert(dbSession, defaultProfileD); - db.qualityProfiles().setAsDefault(defaultProfileC, defaultProfileD); - } - - SearchWsRequest request = new SearchWsRequest() - .setDefaults(true) - .setProfileName("MATCH") - .setOrganizationKey("ORG1"); - QualityProfiles.SearchWsResponse response = underTest.doHandle(request); - - assertThat(response.getProfilesList()) - .extracting(QualityProfiles.SearchWsResponse.QualityProfile::getKey) - .containsExactlyInAnyOrder( - - // name match for xoo1 - "ORG1-A", - - // default for xoo2 - "ORG1-D"); + public void filter_on_module_key() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto module = db.components().insertComponent(newModuleDto(project)); + QProfileDto profileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO2.getKey())); + db.qualityProfiles().associateWithProject(project, profileOnXoo1); + db.qualityProfiles().setAsDefault(defaultProfileOnXoo1, defaultProfileOnXoo2); + + SearchWsResponse result = call(ws.newRequest().setParam(PARAM_PROJECT_KEY, module.key())); + + assertThat(result.getProfilesList()) + .extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(profileOnXoo1.getKee(), defaultProfileOnXoo2.getKee()) + .doesNotContain(defaultProfileOnXoo1.getKee()); } @Test - public void name_and_default_query_is_valid() throws Exception { - minimalValidSetup(); + public void filter_on_project_key_and_default() { + ComponentDto project = db.components().insertPrivateProject(); + QProfileDto profileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO2.getKey())); + db.qualityProfiles().associateWithProject(project, profileOnXoo1); + db.qualityProfiles().setAsDefault(defaultProfileOnXoo1, defaultProfileOnXoo2); - SearchWsRequest request = new SearchWsRequest() - .setProfileName("bla") - .setDefaults(true); + SearchWsResponse result = call(ws.newRequest() + .setParam(PARAM_PROJECT_KEY, project.key()) + .setParam(PARAM_DEFAULTS, "true")); - assertThat(findProfiles(request).getProfilesList()).isNotNull(); + assertThat(result.getProfilesList()) + .extracting(QualityProfile::getKey) + .containsExactlyInAnyOrder(defaultProfileOnXoo2.getKee()) + .doesNotContain(defaultProfileOnXoo1.getKee(), profileOnXoo1.getKee()); } @Test - public void name_and_component_query_is_valid() throws Exception { - minimalValidSetup(); - ComponentDto project = db.components().insertPrivateProject(); - - SearchWsRequest request = new SearchWsRequest() - .setProfileName("bla") - .setProjectKey(project.key()); + public void fail_if_project_does_not_exist() { + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("Component key 'unknown-project' not found"); - assertThat(findProfiles(request).getProfilesList()).isNotNull(); + call(ws.newRequest().setParam(PARAM_PROJECT_KEY, "unknown-project")); } @Test - public void name_requires_either_component_or_defaults() throws Exception { - SearchWsRequest request = new SearchWsRequest() - .setProfileName("bla"); + public void fail_if_project_of_module_does_not_exist() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto module = db.components().insertComponent(newModuleDto(project).setProjectUuid("unknown")); - thrown.expect(BadRequestException.class); - thrown.expectMessage("The name parameter requires either projectKey or defaults to be set."); + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(format("Project uuid of component uuid '%s' does not exist", module.uuid())); - findProfiles(request); + call(ws.newRequest().setParam(PARAM_PROJECT_KEY, module.key())); } @Test - public void default_and_component_cannot_be_set_at_same_time() throws Exception { - SearchWsRequest request = new SearchWsRequest() - .setDefaults(true) - .setProjectKey("blubb"); + public void fail_if_project_is_on_another_organization() { + OrganizationDto organization = db.organizations().insert(); + OrganizationDto anotherOrganization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(anotherOrganization); - thrown.expect(BadRequestException.class); - thrown.expectMessage("The default parameter cannot be provided at the same time than the component key."); + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Component key '%s' not found", project.getKey())); - findProfiles(request); + call(ws.newRequest() + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .setParam(PARAM_PROJECT_KEY, project.getKey())); } @Test - public void language_and_component_cannot_be_set_at_same_time() throws Exception { - SearchWsRequest request = new SearchWsRequest() - .setLanguage("xoo") - .setProjectKey("bla"); + public void statistics_on_active_rules() { + QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + RuleDefinitionDto rule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + RuleDefinitionDto deprecatedRule1 = db.rules().insertRule(r -> r.setStatus(DEPRECATED)).getDefinition(); + RuleDefinitionDto deprecatedRule2 = db.rules().insertRule(r -> r.setStatus(DEPRECATED)).getDefinition(); + RuleDefinitionDto inactiveRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + db.qualityProfiles().activateRule(profile, rule); + db.qualityProfiles().activateRule(profile, deprecatedRule1); + db.qualityProfiles().activateRule(profile, deprecatedRule2); - thrown.expect(BadRequestException.class); - thrown.expectMessage("The language parameter cannot be provided at the same time than the component key or profile name."); + SearchWsResponse result = call(ws.newRequest()); - findProfiles(request); + assertThat(result.getProfilesList()) + .extracting(QualityProfile::getActiveRuleCount, QualityProfile::getActiveDeprecatedRuleCount) + .containsExactlyInAnyOrder(tuple(3L, 2L)); } @Test - public void language_and_name_cannot_be_set_at_same_time() throws Exception { - SearchWsRequest request = new SearchWsRequest() - .setLanguage("xoo") - .setProfileName("bla"); + public void statistics_on_projects() { + ComponentDto project1 = db.components().insertPrivateProject(); + ComponentDto project2 = db.components().insertPrivateProject(); + QProfileDto profileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(db.getDefaultOrganization(), q -> q.setLanguage(XOO1.getKey())); + db.qualityProfiles().associateWithProject(project1, profileOnXoo1); + db.qualityProfiles().associateWithProject(project2, profileOnXoo1); + db.qualityProfiles().setAsDefault(defaultProfileOnXoo1); - thrown.expect(BadRequestException.class); - thrown.expectMessage("The language parameter cannot be provided at the same time than the component key or profile name."); + SearchWsResponse result = call(ws.newRequest()); - findProfiles(request); + assertThat(result.getProfilesList()) + .extracting(QualityProfile::hasProjectCount, QualityProfile::getProjectCount) + .containsExactlyInAnyOrder(tuple(true, 2L), tuple(false, 0L)); } - private void minimalValidSetup() { - for (Language language : Arrays.asList(xoo1, xoo2)) { - QProfileDto profile = db.qualityProfiles().insert(getDefaultOrganization(), p -> p.setLanguage(language.getKey())); - db.qualityProfiles().setAsDefault(profile); - } - } + @Test + public void map_dates() { + long time = DateUtils.parseDateTime("2016-12-22T19:10:03+0100").getTime(); + qualityProfileDb.insert(newQualityProfileDto() + .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) + .setLanguage(XOO1.getKey()) + .setRulesUpdatedAt("2016-12-21T19:10:03+0100") + .setLastUsed(time) + .setUserUpdatedAt(time)); + + SearchWsResponse result = call(ws.newRequest()); - private QProfileDto createProfile(String keySuffix, Language language, OrganizationDto org, String name) { - return new QProfileDto() - .setKee(org.getKey() + "-" + keySuffix) - .setRulesProfileUuid("rp-" + org.getKey() + "-" + keySuffix) - .setOrganizationUuid(org.getUuid()) - .setLanguage(language.getKey()) - .setName(name); + assertThat(result.getProfilesCount()).isEqualTo(1); + assertThat(result.getProfiles(0).getRulesUpdatedAt()).isEqualTo("2016-12-21T19:10:03+0100"); + assertThat(parseDateTime(result.getProfiles(0).getLastUsed()).getTime()).isEqualTo(time); + assertThat(parseDateTime(result.getProfiles(0).getUserUpdatedAt()).getTime()).isEqualTo(time); } - private SearchWsResponse findProfiles(SearchWsRequest request) { - return underTest.doHandle(request); + @Test + public void json_example() { + // languages + Language cs = newLanguage("cs", "C#"); + Language java = newLanguage("java", "Java"); + Language python = newLanguage("py", "Python"); + // profiles + QProfileDto sonarWayCs = db.qualityProfiles().insert(db.getDefaultOrganization(), + p -> p.setName("Sonar way").setKee("AU-TpxcA-iU5OvuD2FL3").setIsBuiltIn(true).setLanguage(cs.getKey())); + QProfileDto myCompanyProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), + p -> p.setName("My Company Profile").setKee("iU5OvuD2FLz").setLanguage(java.getKey())); + QProfileDto myBuProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), + p -> p.setName("My BU Profile").setKee("AU-TpxcA-iU5OvuD2FL1").setParentKee(myCompanyProfile.getKee()).setLanguage(java.getKey())); + QProfileDto sonarWayPython = db.qualityProfiles().insert(db.getDefaultOrganization(), + p -> p.setName("Sonar way").setKee("AU-TpxcB-iU5OvuD2FL7").setIsBuiltIn(true).setLanguage(python.getKey())); + db.qualityProfiles().setAsDefault(sonarWayCs, myCompanyProfile, sonarWayPython); + // rules + List javaRules = range(0, 10).mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(java.getKey())).getDefinition()) + .collect(MoreCollectors.toList()); + List deprecatedJavaRules = range(0, 5) + .mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(java.getKey()).setStatus(DEPRECATED)).getDefinition()) + .collect(MoreCollectors.toList()); + range(0, 7).forEach(i -> db.qualityProfiles().activateRule(myCompanyProfile, javaRules.get(i))); + range(0, 2).forEach(i -> db.qualityProfiles().activateRule(myCompanyProfile, deprecatedJavaRules.get(i))); + range(0, 10).forEach(i -> db.qualityProfiles().activateRule(myBuProfile, javaRules.get(i))); + range(0, 5).forEach(i -> db.qualityProfiles().activateRule(myBuProfile, deprecatedJavaRules.get(i))); + range(0, 2) + .mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(python.getKey())).getDefinition()) + .forEach(rule -> db.qualityProfiles().activateRule(sonarWayPython, rule)); + range(0, 3) + .mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(cs.getKey())).getDefinition()) + .forEach(rule -> db.qualityProfiles().activateRule(sonarWayCs, rule)); + // project + range(0, 7) + .mapToObj(i -> db.components().insertPrivateProject()) + .forEach(project -> db.qualityProfiles().associateWithProject(project, myBuProfile)); + + underTest = new SearchAction(new Languages(cs, java, python), dbClient, qProfileWsSupport, new ComponentFinder(dbClient, null)); + ws = new WsActionTester(underTest); + String result = ws.newRequest().execute().getInput(); + assertJson(result).ignoreFields("ruleUpdatedAt", "lastUsed", "userUpdatedAt") + .isSimilarTo(ws.getDef().responseExampleAsString()); } - private OrganizationDto getDefaultOrganization() { - return db.getDefaultOrganization(); + private SearchWsResponse call(TestRequest request) { + TestRequest wsRequest = request.setMediaType(MediaTypes.PROTOBUF); + + return wsRequest.executeProtobuf(SearchWsResponse.class); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchDataLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchDataLoaderTest.java index 1b7535f5874..55f4c182d47 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchDataLoaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchDataLoaderTest.java @@ -19,31 +19,17 @@ */ package org.sonar.server.qualityprofile.ws; -import java.util.List; -import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; -import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; -import org.sonar.db.DbSession; import org.sonar.db.DbTester; -import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.server.component.ComponentFinder; -import org.sonar.server.language.LanguageTesting; import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonarqube.ws.client.qualityprofile.SearchWsRequest; -import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; public class SearchDataLoaderTest { @@ -68,79 +54,79 @@ public class SearchDataLoaderTest { componentFinder = mock(ComponentFinder.class); } - @Test - public void find_no_profiles_if_database_is_empty() throws Exception { - assertThat(findProfiles( - new SearchWsRequest(), - null)).isEmpty(); - } - - @Test - public void findAll() throws Exception { - insertQualityProfile(organization); - assertThat(findProfiles( - new SearchWsRequest() - .setOrganizationKey(organization.getKey()), - null)).hasSize(1); - } - - @Test - public void findDefaults() throws Exception { - QProfileDto profile = insertQualityProfile(organization); - dbTester.qualityProfiles().setAsDefault(profile); - assertThat(findProfiles( - new SearchWsRequest() - .setOrganizationKey(organization.getKey()) - .setDefaults(true), - null)).hasSize(1); - } - - @Test - public void findForProject() throws Exception { - QProfileDto profile = insertQualityProfile(organization); - dbTester.qualityProfiles().setAsDefault(profile); - ComponentDto project1 = insertProject(); - assertThat(findProfiles( - new SearchWsRequest() - .setOrganizationKey(organization.getKey()), - project1)).hasSize(1); - } - - @Test - public void findAllForLanguage() throws Exception { - QProfileDto profile = insertQualityProfile(organization); - dbTester.qualityProfiles().setAsDefault(profile); - assertThat(findProfiles( - new SearchWsRequest() - .setOrganizationKey(organization.getKey()) - .setLanguage(profile.getLanguage()), - null)).hasSize(1); - assertThat(findProfiles( - new SearchWsRequest() - .setOrganizationKey(organization.getKey()) - .setLanguage("other language"), - null)).hasSize(0); - } - - private List findProfiles(SearchWsRequest request, @Nullable ComponentDto project) { - return new SearchDataLoader(languages, profileLookup, dbTester.getDbClient()) - .findProfiles(dbTester.getSession(), request, organization, project); - } - - private QProfileDto insertQualityProfile(OrganizationDto organization) { - Language language = insertLanguage(); - return dbTester.qualityProfiles().insert(organization, p -> p.setLanguage(language.getKey())); - } - - private Language insertLanguage() { - Language language = LanguageTesting.newLanguage(randomAlphanumeric(20)); - languages.add(language); - return language; - } - - private ComponentDto insertProject() { - ComponentDto project = dbTester.components().insertPrivateProject(organization); - doReturn(project).when(componentFinder).getByKey(any(DbSession.class), eq(project.getKey())); - return project; - } +// @Test +// public void find_no_profiles_if_database_is_empty() throws Exception { +// assertThat(findProfiles( +// new SearchWsRequest(), +// null)).isEmpty(); +// } +// +// @Test +// public void findAll() throws Exception { +// insertQualityProfile(organization); +// assertThat(findProfiles( +// new SearchWsRequest() +// .setOrganizationKey(organization.getKey()), +// null)).hasSize(1); +// } +// +// @Test +// public void findDefaults() throws Exception { +// QProfileDto profile = insertQualityProfile(organization); +// dbTester.qualityProfiles().setAsDefault(profile); +// assertThat(findProfiles( +// new SearchWsRequest() +// .setOrganizationKey(organization.getKey()) +// .setDefaults(true), +// null)).hasSize(1); +// } +// +// @Test +// public void findForProject() throws Exception { +// QProfileDto profile = insertQualityProfile(organization); +// dbTester.qualityProfiles().setAsDefault(profile); +// ComponentDto project1 = insertProject(); +// assertThat(findProfiles( +// new SearchWsRequest() +// .setOrganizationKey(organization.getKey()), +// project1)).hasSize(1); +// } +// +// @Test +// public void findAllForLanguage() throws Exception { +// QProfileDto profile = insertQualityProfile(organization); +// dbTester.qualityProfiles().setAsDefault(profile); +// assertThat(findProfiles( +// new SearchWsRequest() +// .setOrganizationKey(organization.getKey()) +// .setLanguage(profile.getLanguage()), +// null)).hasSize(1); +// assertThat(findProfiles( +// new SearchWsRequest() +// .setOrganizationKey(organization.getKey()) +// .setLanguage("other language"), +// null)).hasSize(0); +// } +// +// private List findProfiles(SearchWsRequest request, @Nullable ComponentDto project) { +// return new SearchDataLoader(languages, profileLookup, dbTester.getDbClient()) +// .findProfiles(dbTester.getSession(), request, organization, project); +// } +// +// private QProfileDto insertQualityProfile(OrganizationDto organization) { +// Language language = insertLanguage(); +// return dbTester.qualityProfiles().insert(organization, p -> p.setLanguage(language.getKey())); +// } +// +// private Language insertLanguage() { +// Language language = LanguageTesting.newLanguage(randomAlphanumeric(20)); +// languages.add(language); +// return language; +// } +// +// private ComponentDto insertProject() { +// ComponentDto project = dbTester.components().insertPrivateProject(organization); +// doReturn(project).when(componentFinder).getByKey(any(DbSession.class), eq(project.getKey())); +// return project; +// } } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search.json deleted file mode 100644 index 8874c784840..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "profiles": [ - { - "key": "sonar-way-xoo1-12345", - "name": "Sonar way", - "language": "xoo1", - "languageName": "Xoo1", - "isInherited": false, - "isDefault": true, - "activeRuleCount": 11, - "activeDeprecatedRuleCount":1 - }, - { - "key": "my-sonar-way-xoo2-34567", - "name": "My Sonar way", - "language": "xoo2", - "languageName": "Xoo2", - "isInherited": true, - "isDefault": false, - "parentKey": "sonar-way-xoo2-23456", - "parentName": "Sonar way", - "activeRuleCount": 33, - "activeDeprecatedRuleCount": 2, - "projectCount": 0 - }, - { - "key": "sonar-way-xoo2-23456", - "name": "Sonar way", - "language": "xoo2", - "languageName": "Xoo2", - "isInherited": false, - "isDefault": false, - "activeRuleCount": 0, - "activeDeprecatedRuleCount": 0, - "projectCount": 2 - } - ] -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_xoo1.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_xoo1.json deleted file mode 100644 index fcff99ce520..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_xoo1.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "profiles": [ - { - "key": "sonar-way-xoo1-12345", - "name": "Sonar way", - "language": "xoo1", - "languageName": "Xoo1", - "isInherited": false - } - ] -} \ No newline at end of file -- 2.39.5