diff options
22 files changed, 596 insertions, 248 deletions
diff --git a/microbenchmark-template/pom.xml b/microbenchmark-template/pom.xml index 9aeaa598e29..3a23a5b48a4 100644 --- a/microbenchmark-template/pom.xml +++ b/microbenchmark-template/pom.xml @@ -28,7 +28,7 @@ </dependency> <dependency> <groupId>${project.groupId}</groupId> - <artifactId>sonar-dbClient</artifactId> + <artifactId>sonar-db</artifactId> <version>${project.version}</version> </dependency> <dependency> diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 62a58ce92a4..5a1c1e91476 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -252,6 +252,7 @@ import org.sonar.server.qualityprofile.ws.RenameAction; import org.sonar.server.qualityprofile.ws.RestoreAction; import org.sonar.server.qualityprofile.ws.RestoreBuiltInAction; import org.sonar.server.qualityprofile.ws.RuleActivationActions; +import org.sonar.server.qualityprofile.ws.SearchDataLoader; import org.sonar.server.qualityprofile.ws.SetDefaultAction; import org.sonar.server.rule.CommonRuleDefinitionsImpl; import org.sonar.server.rule.DefaultRuleFinder; @@ -344,16 +345,16 @@ public class PlatformLevel4 extends PlatformLevel { IndexDefinitions.class, IndexCreator.class, - // Activity + // Activity ActivityService.class, ActivityIndexDefinition.class, ActivityIndexer.class, ActivityIndex.class, - // batch + // batch BatchWsModule.class, - // Dashboard + // Dashboard DashboardsWs.class, org.sonar.server.dashboard.ws.ShowAction.class, ProjectDefaultDashboard.class, @@ -391,12 +392,12 @@ public class PlatformLevel4 extends PlatformLevel { ProjectIssueFilterWidget.class, IssueTagCloudWidget.class, - // update center + // update center UpdateCenterClient.class, UpdateCenterMatrixFactory.class, UpdateCenterWs.class, - // quality profile + // quality profile XMLProfileParser.class, XMLProfileSerializer.class, AnnotationProfileParser.class, @@ -408,6 +409,7 @@ public class PlatformLevel4 extends PlatformLevel { BuiltInProfiles.class, RestoreBuiltInAction.class, org.sonar.server.qualityprofile.ws.SearchAction.class, + SearchDataLoader.class, SetDefaultAction.class, ProjectsAction.class, org.sonar.server.qualityprofile.ws.DeleteAction.class, @@ -439,7 +441,7 @@ public class PlatformLevel4 extends PlatformLevel { QProfileReset.class, RubyQProfileActivityService.class, - // rule + // rule AnnotationRuleParser.class, XMLRuleParser.class, DefaultRuleFinder.class, @@ -467,17 +469,17 @@ public class PlatformLevel4 extends PlatformLevel { RepositoriesAction.class, org.sonar.server.rule.ws.AppAction.class, - // languages + // languages Languages.class, LanguageWs.class, org.sonar.server.language.ws.ListAction.class, - // activity + // activity ActivitiesWs.class, org.sonar.server.activity.ws.SearchAction.class, ActivityMapping.class, - // measure + // measure MeasureFilterFactory.class, MeasureFilterExecutor.class, MeasureFilterEngine.class, @@ -489,14 +491,14 @@ public class PlatformLevel4 extends PlatformLevel { DefaultMetricFinder.class, TimeMachineWs.class, - // quality gates + // quality gates QualityGateDao.class, QualityGateConditionDao.class, QualityGates.class, ProjectQgateAssociationDao.class, QgateProjectFinder.class, - org.sonar.server.qualitygate.ws.ListAction.class, + org.sonar.server.qualitygate.ws.ListAction.class, org.sonar.server.qualitygate.ws.SearchAction.class, org.sonar.server.qualitygate.ws.ShowAction.class, org.sonar.server.qualitygate.ws.CreateAction.class, @@ -513,17 +515,17 @@ public class PlatformLevel4 extends PlatformLevel { org.sonar.server.qualitygate.ws.AppAction.class, QGatesWs.class, - // web services + // web services WebServiceEngine.class, ListingWs.class, - // localization + // localization L10nWs.class, - // authentication + // authentication AuthenticationWs.class, - // users + // users SecurityRealmFactory.class, DeprecatedUserFinder.class, NewUserNotifier.class, @@ -545,12 +547,12 @@ public class PlatformLevel4 extends PlatformLevel { UserIndex.class, UserUpdater.class, - // groups + // groups GroupMembershipService.class, GroupMembershipFinder.class, UserGroupsModule.class, - // permissions + // permissions PermissionRepository.class, PermissionService.class, PermissionUpdater.class, @@ -558,7 +560,7 @@ public class PlatformLevel4 extends PlatformLevel { PermissionFinder.class, PermissionsWsModule.class, - // components + // components ProjectsWsModule.class, DefaultComponentFinder.class, DefaultRubyComponentService.class, @@ -573,12 +575,12 @@ public class PlatformLevel4 extends PlatformLevel { NewAlerts.newMetadata(), ComponentCleanerService.class, - // views + // views ViewIndexDefinition.class, ViewIndexer.class, ViewIndex.class, - // issues + // issues IssueIndexDefinition.class, IssueIndexer.class, IssueAuthorizationIndexer.class, @@ -612,13 +614,13 @@ public class PlatformLevel4 extends PlatformLevel { EmailNotificationChannel.class, AlertsEmailTemplate.class, - IssueFilterWsModule.class, + IssueFilterWsModule.class, - // action plan + // action plan ActionPlanWs.class, ActionPlanService.class, - // issues actions + // issues actions AssignAction.class, PlanAction.class, SetSeverityAction.class, @@ -627,7 +629,7 @@ public class PlatformLevel4 extends PlatformLevel { AddTagsAction.class, RemoveTagsAction.class, - // technical debt + // technical debt DebtModelService.class, DebtModelOperations.class, DebtModelLookup.class, @@ -637,7 +639,7 @@ public class PlatformLevel4 extends PlatformLevel { DebtRulesXMLImporter.class, DebtCharacteristicsXMLImporter.class, - // source + // source HtmlSourceDecorator.class, SourceService.class, SourcesWs.class, @@ -648,23 +650,23 @@ public class PlatformLevel4 extends PlatformLevel { IndexAction.class, ScmAction.class, - // Duplications + // Duplications DuplicationsParser.class, DuplicationsWs.class, DuplicationsJsonWriter.class, org.sonar.server.duplication.ws.ShowAction.class, - // text + // text MacroInterpreter.class, RubyTextService.class, - // Notifications + // Notifications EmailSettings.class, NotificationService.class, NotificationCenter.class, DefaultNotificationManager.class, - // Tests + // Tests CoverageService.class, TestsWs.class, CoveredFilesAction.class, @@ -673,12 +675,12 @@ public class PlatformLevel4 extends PlatformLevel { TestIndex.class, TestIndexer.class, - // Properties + // Properties PropertiesWs.class, - TypeValidationModule.class, + TypeValidationModule.class, - // System + // System RestartAction.class, InfoAction.class, UpgradesAction.class, @@ -693,7 +695,7 @@ public class PlatformLevel4 extends PlatformLevel { MigrateDbAction.class, DbMigrationStatusAction.class, - // Plugins WS + // Plugins WS PluginWSCommons.class, PluginUpdateAggregator.class, InstalledAction.class, @@ -706,7 +708,7 @@ public class PlatformLevel4 extends PlatformLevel { CancelAllAction.class, PluginsWs.class, - // Compute engine + // Compute engine CEQueueStatusImpl.class, ComputeEngineQueueMonitor.class, ReportQueue.class, @@ -720,11 +722,11 @@ public class PlatformLevel4 extends PlatformLevel { ProjectSettingsFactory.class, IndexPurgeListener.class, - // Views plugin + // Views plugin ViewsBootstrap.class, ViewsStopper.class, - // UI + // UI GlobalNavigationAction.class, SettingsNavigationAction.class, ComponentNavigationAction.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfile.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfile.java index c30b024c0e0..ba1dc1e05bd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfile.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfile.java @@ -20,13 +20,12 @@ package org.sonar.server.qualityprofile; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.db.qualityprofile.QualityProfileDto; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - public class QProfile { private int id; @@ -35,6 +34,7 @@ public class QProfile { private String language; private String parent; private boolean isDefault; + private String rulesUpdatedAt; /** * @deprecated in 4.4 @@ -94,11 +94,24 @@ public class QProfile { return parent != null; } + public boolean isDefault() { + return isDefault; + } + public QProfile setDefault(boolean isDefault) { this.isDefault = isDefault; return this; } + public String getRulesUpdatedAt() { + return rulesUpdatedAt; + } + + public QProfile setRulesUpdatedAt(String rulesUpdatedAt) { + this.rulesUpdatedAt = rulesUpdatedAt; + return this; + } + public static QProfile from(QualityProfileDto dto) { return new QProfile() .setId(dto.getId()) @@ -106,15 +119,12 @@ public class QProfile { .setName(dto.getName()) .setLanguage(dto.getLanguage()) .setParent(dto.getParentKee()) - .setDefault(dto.isDefault()); + .setDefault(dto.isDefault()) + .setRulesUpdatedAt(dto.getRulesUpdatedAt()); } @Override public String toString() { return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); } - - public boolean isDefault() { - return isDefault; - } } 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 e754e15a31c..552f6b07971 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 @@ -19,65 +19,36 @@ */ package org.sonar.server.qualityprofile.ws; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; -import java.util.Collections; -import java.util.Comparator; +import com.google.common.base.Function; import java.util.List; import java.util.Map; -import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.apache.commons.lang.builder.CompareToBuilder; +import javax.annotation.Nonnull; import org.sonar.api.resources.Languages; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.NewAction; -import org.sonar.api.server.ws.WebService.Param; -import org.sonar.core.util.NonNullInputFunction; -import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.server.qualityprofile.QProfile; -import org.sonar.server.qualityprofile.QProfileLoader; -import org.sonar.server.qualityprofile.QProfileLookup; import org.sonarqube.ws.QualityProfiles.WsSearchResponse; import org.sonarqube.ws.QualityProfiles.WsSearchResponse.QualityProfile; +import static com.google.common.collect.Maps.uniqueIndex; +import static java.lang.String.format; import static org.sonar.server.ws.WsUtils.writeProtobuf; public class SearchAction implements QProfileWsAction { - private static final String FIELD_KEY = "key"; - private static final String FIELD_NAME = "name"; - private static final String FIELD_LANGUAGE = "language"; - private static final String FIELD_LANGUAGE_NAME = "languageName"; - private static final String FIELD_IS_INHERITED = "isInherited"; - private static final String FIELD_IS_DEFAULT = "isDefault"; - private static final String FIELD_PARENT_KEY = "parentKey"; - private static final String FIELD_PARENT_NAME = "parentName"; - private static final String FIELD_ACTIVE_RULE_COUNT = "activeRuleCount"; - private static final String FIELD_PROJECT_COUNT = "projectCount"; - private static final String FIELD_RULES_UPDATED_AT = "rulesUpdatedAt"; - - private static final Set<String> ALL_FIELDS = ImmutableSet.of( - FIELD_KEY, FIELD_NAME, FIELD_LANGUAGE, FIELD_LANGUAGE_NAME, FIELD_IS_INHERITED, FIELD_PARENT_KEY, FIELD_PARENT_NAME, FIELD_IS_DEFAULT, FIELD_ACTIVE_RULE_COUNT, - FIELD_PROJECT_COUNT, FIELD_RULES_UPDATED_AT); - - private static final String PARAM_LANGUAGE = FIELD_LANGUAGE; - private static final String PARAM_COMPONENT_KEY = "componentKey"; - private static final String PARAM_DEFAULT = "default"; - private static final String PARAM_PROFILE_NAME = "profileName"; + static final String PARAM_LANGUAGE = "language"; + static final String PARAM_PROJECT_KEY = "projectKey"; + static final String PARAM_DEFAULTS = "defaults"; + static final String PARAM_PROFILE_NAME = "profileName"; + private final SearchDataLoader dataLoader; private final Languages languages; - private final QProfileLookup profileLookup; - private final QProfileLoader profileLoader; - private final QualityProfileDao qualityProfileDao; - public SearchAction(Languages languages, QProfileLookup profileLookup, QProfileLoader profileLoader, QualityProfileDao qualityProfileDao) { + public SearchAction(SearchDataLoader dataLoader, Languages languages) { + this.dataLoader = dataLoader; this.languages = languages; - this.profileLookup = profileLookup; - this.profileLoader = profileLoader; - this.qualityProfileDao = qualityProfileDao; } @Override @@ -86,137 +57,107 @@ public class SearchAction implements QProfileWsAction { .setSince("5.2") .setDescription("List quality profiles.") .setHandler(this) - .addFieldsParam(ALL_FIELDS) .setResponseExample(getClass().getResource("example-search.json")); - action.createParam(PARAM_LANGUAGE) - .setDescription("The key of a language supported by the platform. If specified, only profiles for the given language are returned.") + action + .createParam(PARAM_LANGUAGE) + .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)) .setPossibleValues(LanguageParamUtils.getLanguageKeys(languages)); - action.createParam(PARAM_COMPONENT_KEY) - .setDescription("Project or module key") - .setExampleValue("org.codehaus.sonar:sonar"); - - action.createParam(PARAM_DEFAULT) - .setDescription("Return default quality profiles") + action.createParam(PARAM_PROJECT_KEY) + .setDescription(format("Project or module key. If provided, '%s' and '%s' parameters should not be provided.", + PARAM_LANGUAGE, PARAM_DEFAULTS)) + .setExampleValue("my-project-key"); + + action + .createParam(PARAM_DEFAULTS) + .setDescription(format("Return the quality profile marked as default for each language. " + + "If provided, then the parameters '%s', '%s' and '%s' must not be set.", + PARAM_LANGUAGE, PARAM_PROJECT_KEY, PARAM_PROFILE_NAME)) + .setDefaultValue(false) .setBooleanPossibleValues(); action.createParam(PARAM_PROFILE_NAME) - .setDescription("Profile name") + .setDescription(format("Profile name. It should be always used with the '%s' parameter.", PARAM_PROJECT_KEY)) .setExampleValue("SonarQube Way"); } @Override public void handle(Request request, Response response) throws Exception { - List<String> fields = request.paramAsStrings(Param.FIELDS); - - String language = request.param(PARAM_LANGUAGE); - - List<QProfile> profiles; - if (language == null) { - profiles = profileLookup.allProfiles(); - } else { - profiles = profileLookup.profiles(language); - } - - Collections.sort(profiles, new Comparator<QProfile>() { - @Override - public int compare(QProfile o1, QProfile o2) { - return new CompareToBuilder() - .append(o1.language(), o2.language()) - .append(o1.name(), o2.name()) - .toComparison(); - } - }); - WsSearchResponse protobufResponse = buildResponse(profiles, fields); - + SearchData data = dataLoader.load(request); + WsSearchResponse protobufResponse = buildResponse(data); writeProtobuf(protobufResponse, request, response); } - private WsSearchResponse buildResponse(List<QProfile> profiles, List<String> fields) { - Map<String, QProfile> profilesByKey = Maps.uniqueIndex(profiles, new NonNullInputFunction<QProfile, String>() { - @Override - protected String doApply(QProfile input) { - return input.key(); - } - }); - Map<String, Long> activeRuleCountByKey = profileLoader.countAllActiveRules(); - Map<String, Long> projectCountByKey = qualityProfileDao.countProjectsByProfileKey(); + private WsSearchResponse buildResponse(SearchData data) { + List<QProfile> profiles = data.getProfiles(); + Map<String, QProfile> profilesByKey = uniqueIndex(profiles, new QProfileToKey()); WsSearchResponse.Builder response = WsSearchResponse.newBuilder(); QualityProfile.Builder profileBuilder = QualityProfile.newBuilder(); for (QProfile profile : profiles) { - if (languages.get(profile.language()) == null) { - // Hide profiles on an unsupported language - continue; - } - - String key = profile.key(); - Long activeRuleCount = activeRuleCountByKey.containsKey(key) ? activeRuleCountByKey.get(key) : 0L; - Long projectCount = projectCountByKey.containsKey(key) ? projectCountByKey.get(key) : 0L; + String profileKey = profile.key(); profileBuilder.clear(); - if (shouldSetValue(FIELD_KEY, profile.key(), fields)) { + if (isNotNull(profile.key())) { profileBuilder.setKey(profile.key()); } - if (shouldSetValue(FIELD_NAME, profile.name(), fields)) { + if (isNotNull(profile.name())) { profileBuilder.setName(profile.name()); } - if (shouldSetValue(FIELD_ACTIVE_RULE_COUNT, activeRuleCount, fields)) { - profileBuilder.setActiveRuleCount(activeRuleCount); + if (isNotNull(profile.getRulesUpdatedAt())) { + profileBuilder.setRulesUpdatedAt(profile.getRulesUpdatedAt()); } - if (!profile.isDefault() && shouldSetValue(FIELD_PROJECT_COUNT, projectCount, fields)) { - profileBuilder.setProjectCount(projectCount); - } - - writeLanguageFields(profileBuilder, profile, fields); - writeParentFields(profileBuilder, profile, fields, profilesByKey); - // Special case for booleans - if (fieldIsNeeded(FIELD_IS_INHERITED, fields)) { - profileBuilder.setIsInherited(profile.isInherited()); + if (isNotNull(data.getActiveRuleCount(profileKey))) { + profileBuilder.setActiveRuleCount(data.getActiveRuleCount(profileKey)); } - if (fieldIsNeeded(FIELD_IS_DEFAULT, fields)) { - profileBuilder.setIsDefault(profile.isDefault()); + if (!profile.isDefault() && isNotNull(data.getProjectCount(profileKey))) { + profileBuilder.setProjectCount(data.getProjectCount(profileKey)); } + + writeLanguageFields(profileBuilder, profile); + writeParentFields(profileBuilder, profile, profilesByKey); + profileBuilder.setIsInherited(profile.isInherited()); + profileBuilder.setIsDefault(profile.isDefault()); response.addProfiles(profileBuilder); } return response.build(); } - private void writeLanguageFields(QualityProfile.Builder profileBuilder, QProfile profile, List<String> fields) { + private void writeLanguageFields(QualityProfile.Builder profileBuilder, QProfile profile) { String languageKey = profile.language(); - if (shouldSetValue(FIELD_LANGUAGE, languageKey, fields)) { + if (isNotNull(languageKey)) { profileBuilder.setLanguage(languageKey); } String languageName = languages.get(languageKey).getName(); - if (shouldSetValue(FIELD_LANGUAGE_NAME, languageName, fields)) { + if (isNotNull(languageName)) { profileBuilder.setLanguageName(languageName); } } - private static void writeParentFields(QualityProfile.Builder profileBuilder, QProfile profile, List<String> fields, Map<String, QProfile> profilesByKey) { + private static void writeParentFields(QualityProfile.Builder profileBuilder, QProfile profile, Map<String, QProfile> profilesByKey) { String parentKey = profile.parent(); QProfile parent = parentKey == null ? null : profilesByKey.get(parentKey); - if (shouldSetValue(FIELD_PARENT_KEY, parentKey, fields)) { + if (isNotNull(parentKey)) { profileBuilder.setParentKey(parentKey); } - if (parent != null && shouldSetValue(FIELD_PARENT_NAME, parent.name(), fields)) { + if (isNotNull(parent) && isNotNull(parent.name())) { profileBuilder.setParentName(parent.name()); } } - @CheckForNull - private static <T> T valueIfFieldNeeded(String field, T value, @Nullable List<String> fields) { - return fieldIsNeeded(field, fields) ? value : null; - } - - private static <T> boolean shouldSetValue(String field, T value, List<String> fields) { - return valueIfFieldNeeded(field, value, fields) != null; + private static <T> boolean isNotNull(T value) { + return value != null; } - private static boolean fieldIsNeeded(String field, @Nullable List<String> fields) { - return fields == null || fields.contains(field); + private static class QProfileToKey implements Function<QProfile, String> { + @Override + public String apply(@Nonnull QProfile input) { + return input.key(); + } } } 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 new file mode 100644 index 00000000000..88d7660774d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchData.java @@ -0,0 +1,62 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.qualityprofile.ws; + +import java.util.List; +import java.util.Map; +import org.sonar.server.qualityprofile.QProfile; + +import static com.google.common.base.Objects.firstNonNull; +import static com.google.common.collect.ImmutableList.copyOf; +import static com.google.common.collect.ImmutableMap.copyOf; + +public class SearchData { + private List<QProfile> profiles; + private Map<String, Long> activeRuleCountByProfileKey; + private Map<String, Long> projectCountByProfileKey; + + public List<QProfile> getProfiles() { + return profiles; + } + + public SearchData setProfiles(List<QProfile> profiles) { + this.profiles = copyOf(profiles); + return this; + } + + public SearchData setActiveRuleCountByProfileKey(Map<String, Long> activeRuleCountByProfileKey) { + this.activeRuleCountByProfileKey = copyOf(activeRuleCountByProfileKey); + return this; + } + + public SearchData setProjectCountByProfileKey(Map<String, Long> projectCountByProfileKey) { + this.projectCountByProfileKey = copyOf(projectCountByProfileKey); + return this; + } + + public long getActiveRuleCount(String profileKey) { + return firstNonNull(activeRuleCountByProfileKey.get(profileKey), 0L); + } + + public long getProjectCount(String profileKey) { + return firstNonNull(projectCountByProfileKey.get(profileKey), 0L); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java new file mode 100644 index 00000000000..e0de16f8746 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java @@ -0,0 +1,215 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.qualityprofile.ws; + +import com.google.common.base.Predicate; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import javax.annotation.Nullable; +import org.apache.commons.lang.builder.CompareToBuilder; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Languages; +import org.sonar.api.server.ws.Request; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.qualityprofile.QualityProfileDto; +import org.sonar.server.qualityprofile.QProfile; +import org.sonar.server.qualityprofile.QProfileFactory; +import org.sonar.server.qualityprofile.QProfileLoader; +import org.sonar.server.qualityprofile.QProfileLookup; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.FluentIterable.from; +import static java.lang.String.format; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_DEFAULTS; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_LANGUAGE; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_PROFILE_NAME; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_PROJECT_KEY; +import static org.sonar.server.ws.WsUtils.checkRequest; + +public class SearchDataLoader { + private final Languages languages; + private final QProfileLookup profileLookup; + private final QProfileLoader profileLoader; + private final QProfileFactory profileFactory; + private final DbClient dbClient; + + public SearchDataLoader(Languages languages, QProfileLookup profileLookup, QProfileLoader profileLoader, QProfileFactory profileFactory, DbClient dbClient) { + this.languages = languages; + this.profileLookup = profileLookup; + this.profileLoader = profileLoader; + this.profileFactory = profileFactory; + this.dbClient = dbClient; + } + + SearchData load(Request request) { + validateRequest(request); + + return new SearchData() + .setProfiles(findProfiles(request)) + .setActiveRuleCountByProfileKey(profileLoader.countAllActiveRules()) + .setProjectCountByProfileKey(dbClient.qualityProfileDao().countProjectsByProfileKey()); + } + + private List<QProfile> findProfiles(Request request) { + List<QProfile> profiles; + if (askDefaultProfiles(request)) { + profiles = findDefaultProfiles(); + } else if (hasComponentKey(request)) { + profiles = findProjectProfiles(request); + } else { + profiles = findAllProfiles(request); + } + + return orderProfiles(profiles); + } + + private static List<QProfile> orderProfiles(List<QProfile> profiles) { + return from(profiles) + .toSortedList(QProfileComparator.INSTANCE); + } + + private List<QProfile> findAllProfiles(Request request) { + String language = request.param(PARAM_LANGUAGE); + + List<QProfile> profiles = language != null ? + profileLookup.profiles(language) + : profileLookup.allProfiles(); + + return from(profiles) + .filter(new IsLanguageKnown()) + .toList(); + } + + private List<QProfile> findProjectProfiles(Request request) { + String projectKey = request.param(PARAM_PROJECT_KEY); + String profileName = request.param(PARAM_PROFILE_NAME); + + List<QProfile> profiles = new ArrayList<>(); + + DbSession dbSession = dbClient.openSession(false); + try { + for (Language language : languages.all()) { + String languageKey = language.getKey(); + profiles.add(getProfile(dbSession, languageKey, projectKey, profileName)); + } + } finally { + dbClient.closeSession(dbSession); + } + + return profiles; + } + + private List<QProfile> findDefaultProfiles() { + List<QProfile> profiles = new ArrayList<>(); + + DbSession dbSession = dbClient.openSession(false); + try { + for (Language language : languages.all()) { + profiles.add(getDefaultProfile(dbSession, language.getKey())); + } + } finally { + dbClient.closeSession(dbSession); + } + + return profiles; + } + + private static QProfile profileDtoToQProfile(QualityProfileDto dto) { + return new QProfile() + .setKey(dto.getKey()) + .setName(dto.getName()) + .setLanguage(dto.getLanguage()) + .setDefault(dto.isDefault()) + .setRulesUpdatedAt(dto.getRulesUpdatedAt()); + } + + /** + * First try to find a quality profile matching the given name (if provided) and current language + * If no profile found, try to find the quality profile set on the project (if provided) + * If still no profile found, try to find the default profile of the language + * <p/> + * Never return null because a default profile should always be set on each language + */ + private QProfile getProfile(DbSession dbSession, String languageKey, @Nullable String projectKey, @Nullable String profileName) { + QualityProfileDto profileDto = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null; + if (profileDto == null && projectKey != null) { + profileDto = profileFactory.getByProjectAndLanguage(dbSession, projectKey, languageKey); + } + profileDto = profileDto != null ? profileDto : profileFactory.getDefault(dbSession, languageKey); + checkState(profileDto != null, format("No quality profile can been found on language '%s' for project '%s'", languageKey, projectKey)); + + return profileDtoToQProfile(profileDto); + } + + private QProfile getDefaultProfile(DbSession dbSession, String languageKey) { + QualityProfileDto profile = profileFactory.getDefault(dbSession, languageKey); + checkState(profile != null, format("No quality profile can been found on language '%s'", languageKey)); + + return profileDtoToQProfile(profile); + } + + private static void validateRequest(Request request) { + boolean hasLanguage = hasLanguage(request); + boolean isDefault = askDefaultProfiles(request); + boolean hasComponentKey = hasComponentKey(request); + boolean hasProfileName = hasProfileName(request); + + 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 && !hasProfileName), "The default parameter cannot be provided at the same time than the component key or profile name"); + } + + private static boolean hasProfileName(Request request) { + return request.hasParam(PARAM_PROFILE_NAME); + } + + private static boolean hasComponentKey(Request request) { + return request.hasParam(PARAM_PROJECT_KEY); + } + + private static Boolean askDefaultProfiles(Request request) { + return request.paramAsBoolean(PARAM_DEFAULTS); + } + + private static boolean hasLanguage(Request request) { + return request.hasParam(PARAM_LANGUAGE); + } + + private enum QProfileComparator implements Comparator<QProfile> { + INSTANCE; + @Override + public int compare(QProfile o1, QProfile o2) { + return new CompareToBuilder() + .append(o1.language(), o2.language()) + .append(o1.name(), o2.name()) + .toComparison(); + } + } + + private class IsLanguageKnown implements Predicate<QProfile> { + @Override + public boolean apply(@Nullable QProfile profile) { + return languages.get(profile.language()) != null; + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectActionTest.java index dc70a1eae9d..24362ac49c9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectActionTest.java @@ -22,10 +22,7 @@ package org.sonar.server.batch; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.server.ws.WsTester; @@ -33,11 +30,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) public class ProjectActionTest { - @Mock - ProjectRepositoryLoader projectRepositoryLoader; + ProjectRepositoryLoader projectRepositoryLoader = mock(ProjectRepositoryLoader.class); WsTester tester; diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java index f2849863e44..6dde0afeffb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java @@ -41,6 +41,7 @@ import org.sonar.batch.protocol.input.QProfile; import org.sonar.core.permission.GlobalPermissions; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ComponentTesting; import org.sonar.db.property.PropertyDto; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.db.rule.RuleDto; @@ -49,11 +50,9 @@ import org.sonar.db.rule.RuleTesting; import org.sonar.db.source.FileSourceDao; import org.sonar.db.source.FileSourceDto; import org.sonar.db.source.FileSourceDto.Type; -import org.sonar.db.component.ComponentTesting; import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.qualityprofile.QProfileName; -import org.sonar.server.qualityprofile.QProfileTesting; import org.sonar.server.qualityprofile.RuleActivation; import org.sonar.server.qualityprofile.RuleActivator; import org.sonar.server.tester.ServerTester; @@ -62,6 +61,8 @@ import org.sonar.server.tester.UserSessionRule; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; +import static org.sonar.api.utils.DateUtils.formatDateTime; +import static org.sonar.server.qualityprofile.QProfileTesting.newQProfileDto; public class ProjectRepositoryLoaderMediumTest { @@ -454,8 +455,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(ruleUpdatedAt)); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(ruleUpdatedAt)); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); tester.get(DbClient.class).qualityProfileDao().insertProjectProfileAssociation(project.uuid(), profileDto.getKee(), dbSession); @@ -478,8 +479,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(ruleUpdatedAt)).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(ruleUpdatedAt)).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); dbSession.commit(); @@ -501,8 +502,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(ruleUpdatedAt)).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(ruleUpdatedAt)).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); dbSession.commit(); @@ -521,8 +522,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); Date ruleUpdatedAt = DateUtils.parseDateTime("2014-01-14T13:00:00+0100"); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(ruleUpdatedAt)).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(ruleUpdatedAt)).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); dbSession.commit(); @@ -545,8 +546,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(ruleUpdatedAt)); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(ruleUpdatedAt)); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); tester.get(DbClient.class).qualityProfileDao().insertProjectProfileAssociation(project.uuid(), profileDto.getKee(), dbSession); @@ -582,8 +583,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(DateUtils.parseDateTime("2014-01-14T13:00:00+0100"))).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(DateUtils.parseDateTime("2014-01-14T13:00:00+0100"))).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); @@ -624,8 +625,8 @@ public class ProjectRepositoryLoaderMediumTest { RuleTesting.newDto(ruleKey2).setName("Avoid NPE").setLanguage(ServerTester.Xoo.KEY) ); - QualityProfileDto profileDto1 = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd"); - QualityProfileDto profileDto2 = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "Another profile"), "efgh"); + QualityProfileDto profileDto1 = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd"); + QualityProfileDto profileDto2 = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "Another profile"), "efgh"); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto1, profileDto2); // The first profile is the profile used but the project @@ -655,8 +656,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd") - .setRulesUpdatedAt(DateUtils.formatDateTime(DateUtils.parseDateTime("2014-01-14T13:00:00+0100"))).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd") + .setRulesUpdatedAt(formatDateTime(DateUtils.parseDateTime("2014-01-14T13:00:00+0100"))).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); for (int i = 0; i < 20; i++) { @@ -679,8 +680,8 @@ public class ProjectRepositoryLoaderMediumTest { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); tester.get(DbClient.class).componentDao().insert(dbSession, project); - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(ruleUpdatedAt)).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(ruleUpdatedAt)).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); RuleKey ruleKey = RuleKey.of("squid", "ArchitecturalConstraint"); @@ -832,8 +833,8 @@ public class ProjectRepositoryLoaderMediumTest { } private void addDefaultProfile() { - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( - DateUtils.formatDateTime(new Date())).setDefault(true); + QualityProfileDto profileDto = newQProfileDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( + formatDateTime(new Date())).setDefault(true); tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleBackendMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleBackendMediumTest.java index 4f679064967..b7e8c0fff41 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleBackendMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleBackendMediumTest.java @@ -371,7 +371,7 @@ public class ActiveRuleBackendMediumTest { List<String> profileKeys = newArrayList(); for (int i = 0; i < 30; i++) { - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor("xoo", "profile-" + i), "profile-" + i); + QualityProfileDto profileDto = QProfileTesting.newQProfileDto(QProfileName.createFor("xoo", "profile-" + i), "profile-" + i); profileKeys.add(profileDto.getKey()); db.qualityProfileDao().insert(dbSession, profileDto); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java index b93717d10b0..7b875b2bcaf 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java @@ -129,7 +129,7 @@ public class QProfileExportersTest { @Test public void import_xml() { - QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor("xoo", "import_xml"), "import_xml"); + QualityProfileDto profileDto = QProfileTesting.newQProfileDto(QProfileName.createFor("xoo", "import_xml"), "import_xml"); db.qualityProfileDao().insert(dbSession, profileDto); dbSession.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileFactoryMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileFactoryMediumTest.java index f204c850702..cfde9ba3aff 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileFactoryMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileFactoryMediumTest.java @@ -334,7 +334,7 @@ public class QProfileFactoryMediumTest { @Test public void get_profile_by_name_and_language() { - QualityProfileDto profileDto = QProfileTesting.newDto(new QProfileName("xoo", "SonarQube way"), "abcd"); + QualityProfileDto profileDto = QProfileTesting.newQProfileDto(new QProfileName("xoo", "SonarQube way"), "abcd"); db.qualityProfileDao().insert(dbSession, profileDto); dbSession.commit(); dbSession.clearCache(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTest.java index 4fb83dae022..1ae6949cb88 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTest.java @@ -39,7 +39,7 @@ public class QProfileTest { @Test public void to_string() { assertThat(new QProfile().setId(1).setName("Default").setLanguage("java").setParent("Parent").toString()) - .contains("[id=1,key=<null>,name=Default,language=java,parent=Parent,isDefault=false]"); + .contains("[id=1,key=<null>,name=Default,language=java,parent=Parent,isDefault=false,rulesUpdatedAt=<null>]"); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTesting.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTesting.java index a74a5822491..0f13b76ecb2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTesting.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileTesting.java @@ -33,19 +33,19 @@ public class QProfileTesting { public static final QProfileName XOO_P3_NAME = new QProfileName("xoo", "P3"); public static final String XOO_P3_KEY = "XOO_P3"; - public static QualityProfileDto newDto(QProfileName name, String key) { + public static QualityProfileDto newQProfileDto(QProfileName name, String key) { return QualityProfileDto.createFor(key).setName(name.getName()).setLanguage(name.getLanguage()); } public static QualityProfileDto newXooP1() { - return newDto(XOO_P1_NAME, XOO_P1_KEY); + return newQProfileDto(XOO_P1_NAME, XOO_P1_KEY); } public static QualityProfileDto newXooP2() { - return newDto(XOO_P2_NAME, XOO_P2_KEY); + return newQProfileDto(XOO_P2_NAME, XOO_P2_KEY); } public static QualityProfileDto newXooP3() { - return newDto(XOO_P3_NAME, XOO_P3_KEY); + return newQProfileDto(XOO_P3_NAME, XOO_P3_KEY); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionMediumTest.java index 4754961c4ce..917f55c4b23 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionMediumTest.java @@ -269,7 +269,7 @@ public class ChangeParentActionMediumTest { } private QualityProfileDto createProfile(String lang, String name) { - QualityProfileDto profile = QProfileTesting.newDto(new QProfileName(lang, name), "p" + lang + "-" + name.toLowerCase()); + QualityProfileDto profile = QProfileTesting.newQProfileDto(new QProfileName(lang, name), "p" + lang + "-" + name.toLowerCase()); db.qualityProfileDao().insert(session, profile); return profile; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionMediumTest.java index 56df66cbde7..ebdfbdd0deb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionMediumTest.java @@ -188,7 +188,7 @@ public class CompareActionMediumTest { } private QualityProfileDto createProfile(String lang, String name, String key) { - QualityProfileDto profile = QProfileTesting.newDto(new QProfileName(lang, name), key); + QualityProfileDto profile = QProfileTesting.newQProfileDto(new QProfileName(lang, name), key); db.qualityProfileDao().insert(session, profile); session.commit(); return profile; diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionMediumTest.java index 5583e65f88b..9c25d7ba87c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionMediumTest.java @@ -115,7 +115,7 @@ public class InheritanceActionMediumTest { } private QualityProfileDto createProfile(String lang, String name, String key) { - QualityProfileDto profile = QProfileTesting.newDto(new QProfileName(lang, name), key); + QualityProfileDto profile = QProfileTesting.newQProfileDto(new QProfileName(lang, name), key); db.qualityProfileDao().insert(session, profile); session.commit(); return profile; diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java index 8b9a2c94f96..a4b6f74c50c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java @@ -549,7 +549,7 @@ public class QProfilesWsMediumTest { } private QualityProfileDto createProfile(String lang) { - QualityProfileDto profile = QProfileTesting.newDto(new QProfileName(lang, "P" + lang), "p" + lang); + QualityProfileDto profile = QProfileTesting.newQProfileDto(new QProfileName(lang, "P" + lang), "p" + lang); db.qualityProfileDao().insert(session, profile); return profile; } 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 56930f9d279..f7f120ed209 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 @@ -30,7 +30,6 @@ import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.server.ws.WebService; -import org.sonar.api.server.ws.WebService.Param; import org.sonar.api.utils.ValidationMessages; import org.sonar.server.language.LanguageTesting; import org.sonar.server.qualityprofile.QProfileExporters; @@ -68,7 +67,7 @@ public class QProfilesWsTest { new CreateAction(null, null, null, languages, importers, userSessionRule), new ImportersAction(importers), new RestoreBuiltInAction(null), - new SearchAction(languages, null, null, null), + new SearchAction(null, languages), new SetDefaultAction(languages, null, null, userSessionRule), new ProjectsAction(null, userSessionRule), new BackupAction(null, null, null, languages), @@ -82,7 +81,7 @@ public class QProfilesWsTest { new ExportersAction(), new InheritanceAction(null, null, null, null, languages), new RenameAction(null, userSessionRule) - )).controller(QProfilesWs.API_ENDPOINT); + )).controller(QProfilesWs.API_ENDPOINT); } private ProfileImporter[] createImporters(Languages languages) { @@ -123,10 +122,8 @@ public class QProfilesWsTest { WebService.Action search = controller.action("search"); assertThat(search).isNotNull(); assertThat(search.isPost()).isFalse(); - assertThat(search.params()).hasSize(2); + assertThat(search.params()).hasSize(4); assertThat(search.param("language").possibleValues()).containsOnly(xoo1Key, xoo2Key); - assertThat(search.param(Param.FIELDS).possibleValues()) - .containsOnly("key", "name", "language", "languageName", "isInherited", "parentKey", "parentName", "isDefault", "activeRuleCount", "projectCount"); } @Test 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 4fc7b8e9ffb..d83aac49b21 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 @@ -20,30 +20,42 @@ package org.sonar.server.qualityprofile.ws; import com.google.common.collect.ImmutableMap; +import java.util.Date; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; -import org.sonar.api.server.ws.WebService.Param; 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.ComponentDao; -import org.sonar.db.component.ComponentTesting; +import org.sonar.db.component.ComponentDto; import org.sonar.db.qualityprofile.QualityProfileDao; +import org.sonar.db.qualityprofile.QualityProfileDbTester; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.server.language.LanguageTesting; +import org.sonar.server.qualityprofile.QProfileFactory; import org.sonar.server.qualityprofile.QProfileLoader; import org.sonar.server.qualityprofile.QProfileLookup; +import org.sonar.server.qualityprofile.db.ActiveRuleDao; +import org.sonar.server.rule.db.RuleDao; import org.sonar.server.ws.WsActionTester; +import org.sonar.test.DbTests; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.db.component.ComponentTesting.newProjectDto; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_DEFAULTS; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_PROFILE_NAME; +import static org.sonar.server.qualityprofile.ws.SearchAction.PARAM_PROJECT_KEY; import static org.sonar.test.JsonAssert.assertJson; +@Category(DbTests.class) public class SearchActionTest { @Rule @@ -59,22 +71,29 @@ public class SearchActionTest { private Language xoo2; private DbSession dbSession; private WsActionTester ws; + private QualityProfileDbTester qualityProfileDb; @Before public void setUp() { - db.truncateTables(); dbClient = db.getDbClient(); - qualityProfileDao = dbClient.qualityProfileDao(); dbSession = db.getSession(); + qualityProfileDao = dbClient.qualityProfileDao(); + qualityProfileDb = new QualityProfileDbTester(db); xoo1 = LanguageTesting.newLanguage("xoo1"); xoo2 = LanguageTesting.newLanguage("xoo2"); + Languages languages = new Languages(xoo1, xoo2); + org.sonar.server.db.DbClient oldDbClient = new org.sonar.server.db.DbClient( + db.database(), + db.myBatis(), + dbClient.qualityProfileDao(), + new ActiveRuleDao( + dbClient.qualityProfileDao(), + new RuleDao(System2.INSTANCE), + System2.INSTANCE)); ws = new WsActionTester(new SearchAction( - new Languages(xoo1, xoo2), - new QProfileLookup(dbClient), - profileLoader, - qualityProfileDao)); + new SearchDataLoader(languages, new QProfileLookup(dbClient), profileLoader, new QProfileFactory(oldDbClient), dbClient), languages)); } @Test @@ -91,8 +110,8 @@ public class SearchActionTest { QualityProfileDto.createFor("sonar-way-other-666").setLanguage("other").setName("Sonar way").setDefault(true) ); new ComponentDao().insert(dbSession, - ComponentTesting.newProjectDto("project-uuid1"), - ComponentTesting.newProjectDto("project-uuid2")); + newProjectDto("project-uuid1"), + newProjectDto("project-uuid2")); qualityProfileDao.insertProjectProfileAssociation("project-uuid1", "sonar-way-xoo2-23456", dbSession); qualityProfileDao.insertProjectProfileAssociation("project-uuid2", "sonar-way-xoo2-23456", dbSession); commit(); @@ -103,29 +122,99 @@ public class SearchActionTest { } @Test - public void search_with_fields() throws Exception { + public void search_for_language() throws Exception { qualityProfileDao.insert(dbSession, - QualityProfileDto.createFor("sonar-way-xoo1-12345").setLanguage(xoo1.getKey()).setName("Sonar way").setDefault(true), - QualityProfileDto.createFor("sonar-way-xoo2-23456").setLanguage(xoo2.getKey()).setName("Sonar way"), - QualityProfileDto.createFor("my-sonar-way-xoo2-34567").setLanguage(xoo2.getKey()).setName("My Sonar way").setParentKee("sonar-way-xoo2-23456") + QualityProfileDto.createFor("sonar-way-xoo1-12345").setLanguage(xoo1.getKey()).setName("Sonar way") ); commit(); - String result = ws.newRequest().setParam(Param.FIELDS, "key,language").execute().getInput(); + String result = ws.newRequest().setParam("language", xoo1.getKey()).execute().getInput(); - assertJson(result).isSimilarTo(getClass().getResource("SearchActionTest/search_fields.json")); + assertJson(result).isSimilarTo(getClass().getResource("SearchActionTest/search_xoo1.json")); } @Test - public void search_for_language() throws Exception { - qualityProfileDao.insert(dbSession, - QualityProfileDto.createFor("sonar-way-xoo1-12345").setLanguage(xoo1.getKey()).setName("Sonar way") - ); + public void search_for_project_qp() { + QualityProfileDto qualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-12345") + .setLanguage(xoo1.getKey()) + .setRulesUpdatedAtAsDate(new Date()) + .setName("Sonar way"); + QualityProfileDto qualityProfileOnXoo2 = QualityProfileDto.createFor("sonar-way-xoo2-12345") + .setLanguage(xoo2.getKey()) + .setRulesUpdatedAtAsDate(new Date()) + .setName("Sonar way"); + QualityProfileDto anotherQualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-45678") + .setLanguage(xoo1.getKey()) + .setRulesUpdatedAtAsDate(new Date()) + .setName("Another way"); + ComponentDto project = newProjectDto("project-uuid"); + qualityProfileDb.insertQualityProfiles(qualityProfileOnXoo1, qualityProfileOnXoo2, anotherQualityProfileOnXoo1); + qualityProfileDb.insertProjectWithQualityProfileAssociations(project, qualityProfileOnXoo1, qualityProfileOnXoo2); commit(); - String result = ws.newRequest().setParam("language", xoo1.getKey()).execute().getInput(); + String result = ws.newRequest() + .setParam(PARAM_PROJECT_KEY, project.key()) + .execute().getInput(); + + assertThat(result) + .contains("sonar-way-xoo1-12345", "sonar-way-xoo2-12345") + .doesNotContain("sonar-way-xoo1-45678"); + } + + @Test + public void search_for_default_qp() { + QualityProfileDto qualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-12345") + .setLanguage(xoo1.getKey()) + .setName("Sonar way") + .setDefault(true); + QualityProfileDto qualityProfileOnXoo2 = QualityProfileDto.createFor("sonar-way-xoo2-12345") + .setLanguage(xoo2.getKey()) + .setName("Sonar way") + .setDefault(true); + QualityProfileDto anotherQualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-45678") + .setLanguage(xoo1.getKey()) + .setName("Sonar way") + .setDefault(false); + qualityProfileDb.insertQualityProfiles(qualityProfileOnXoo1, qualityProfileOnXoo2, anotherQualityProfileOnXoo1); + commit(); + + String result = ws.newRequest() + .setParam(PARAM_DEFAULTS, Boolean.TRUE.toString()) + .execute().getInput(); + + assertThat(result) + .contains("sonar-way-xoo1-12345", "sonar-way-xoo2-12345") + .doesNotContain("sonar-way-xoo1-45678"); + } + + @Test + public void search_by_profile_name() { + QualityProfileDto qualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-12345") + .setLanguage(xoo1.getKey()) + .setRulesUpdatedAtAsDate(new Date()) + .setName("Sonar way"); + QualityProfileDto qualityProfileOnXoo2 = QualityProfileDto.createFor("sonar-way-xoo2-12345") + .setLanguage(xoo2.getKey()) + .setRulesUpdatedAtAsDate(new Date()) + .setName("Sonar way"); + QualityProfileDto anotherQualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-45678") + .setLanguage(xoo1.getKey()) + .setRulesUpdatedAtAsDate(new Date()) + .setName("Another way"); + ComponentDto project = newProjectDto("project-uuid"); + qualityProfileDb.insertQualityProfiles(qualityProfileOnXoo1, qualityProfileOnXoo2, anotherQualityProfileOnXoo1); + dbClient.componentDao().insert(dbSession, project); + commit(); + + String result = ws.newRequest() + .setParam(PARAM_PROJECT_KEY, project.key()) + .setParam(PARAM_PROFILE_NAME, "Sonar way") + .execute().getInput(); + + assertThat(result) + .contains("sonar-way-xoo1-12345", "sonar-way-xoo2-12345") + .doesNotContain("sonar-way-xoo1-45678"); - assertJson(result).isSimilarTo(getClass().getResource("SearchActionTest/search_xoo1.json")); } private void commit() { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_fields.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_fields.json deleted file mode 100644 index 143625c201f..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/SearchActionTest/search_fields.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "profiles": [ - { - "key": "sonar-way-xoo1-12345", - "language": "xoo1" - }, - { - "key": "my-sonar-way-xoo2-34567", - "language": "xoo2" - }, - { - "key": "sonar-way-xoo2-23456", - "language": "xoo2" - } - ] -}
\ No newline at end of file diff --git a/sonar-db/src/test/java/org/sonar/db/DbTester.java b/sonar-db/src/test/java/org/sonar/db/DbTester.java index 5ed9728972c..86545936b9b 100644 --- a/sonar-db/src/test/java/org/sonar/db/DbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/DbTester.java @@ -109,6 +109,10 @@ public class DbTester extends ExternalResource { return session; } + public void commit() { + getSession().commit(); + } + public void truncateTables() { db.truncateTables(); } diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java new file mode 100644 index 00000000000..963814f7b10 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java @@ -0,0 +1,48 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.db.qualityprofile; + +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; + +public class QualityProfileDbTester { + private final DbClient dbClient; + private final DbSession dbSession; + + public QualityProfileDbTester(DbTester db) { + this.dbClient = db.getDbClient(); + this.dbSession = db.getSession(); + } + + public void insertQualityProfiles(QualityProfileDto qualityProfile, QualityProfileDto... qualityProfiles) { + dbClient.qualityProfileDao().insert(dbSession, qualityProfile, qualityProfiles); + } + + public void insertProjectWithQualityProfileAssociations(ComponentDto project, QualityProfileDto... qualityProfiles) { + dbClient.componentDao().insert(dbSession, project); + for (QualityProfileDto qualityProfile : qualityProfiles) { + dbClient.qualityProfileDao().insertProjectProfileAssociation(project.uuid(), qualityProfile.getKey(), dbSession); + } + } + +} |