diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2017-03-22 15:03:03 +0100 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2017-03-23 17:38:34 +0100 |
commit | 0ad05f38e2b642c9286ceb10f61597868110417e (patch) | |
tree | 45d8d21b9c4401cc9c6c70a3bca9462cf18d932e | |
parent | f49a935f1e586a4b71f931c13eb197d6ccdfdcff (diff) | |
download | sonarqube-0ad05f38e2b642c9286ceb10f61597868110417e.tar.gz sonarqube-0ad05f38e2b642c9286ceb10f61597868110417e.zip |
SONAR-8924 compute active rules statistics via database
Using Elasticsearch is not accurate because the results
are not exhaustive. They can be paginated by the aggregations.
Moreover it brings useless complexity compared to the
simple and performance SQL requests.
22 files changed, 331 insertions, 632 deletions
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index fdb860bf229..6f4aee6effe 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -122,7 +122,6 @@ import org.sonar.server.plugins.ServerExtensionInstaller; import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper; import org.sonar.server.plugins.privileged.PrivilegedPluginsStopper; import org.sonar.server.property.InternalPropertiesImpl; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.rule.CommonRuleDefinitionsImpl; import org.sonar.server.rule.DefaultRuleFinder; @@ -240,7 +239,6 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { // rules/qprofiles RuleIndex.class, - ActiveRuleIndex.class, // issues IssueIndex.class, diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index f1e24e5c84c..3e9a7291fb4 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -107,7 +107,7 @@ public class ComputeEngineContainerImplTest { ); assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION - + 23 // level 1 + + 22 // level 1 + 48 // content of DaoModule + 3 // content of EsSearchModule + 57 // content of CorePropertyDefinitions diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java new file mode 100644 index 00000000000..7ade1b65dde --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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; + +import java.util.List; +import java.util.Map; + +import static org.sonar.core.util.stream.Collectors.uniqueIndex; + +public class KeyLongValue { + + private String key; + private Long value; + + public String getKey() { + return key; + } + + public Long getValue() { + return value; + } + + /** + * This method does not keep order of list. + */ + public static Map<String, Long> toMap(List<KeyLongValue> values) { + return values + .stream() + .collect(uniqueIndex(KeyLongValue::getKey, KeyLongValue::getValue, values.size())); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index b55c6e18984..e60a2dede42 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -153,6 +153,7 @@ public class MyBatis implements Startable { confBuilder.loadAlias("IdUuidPair", IdUuidPair.class); confBuilder.loadAlias("InternalProperty", InternalPropertyDto.class); confBuilder.loadAlias("IssueChange", IssueChangeDto.class); + confBuilder.loadAlias("KeyLongValue", KeyLongValue.class); confBuilder.loadAlias("Issue", IssueDto.class); confBuilder.loadAlias("LoadedTemplate", LoadedTemplateDto.class); confBuilder.loadAlias("Measure", MeasureDto.class); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java index 705cfad9a1a..246e79feb80 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java @@ -23,11 +23,15 @@ import com.google.common.base.Optional; import com.google.common.base.Preconditions; import java.util.Collection; import java.util.List; +import java.util.Map; import javax.annotation.CheckForNull; +import org.sonar.api.rule.RuleStatus; import org.sonar.db.Dao; import org.sonar.db.DatabaseUtils; import org.sonar.db.DbSession; +import org.sonar.db.KeyLongValue; import org.sonar.db.RowNotFoundException; +import org.sonar.db.organization.OrganizationDto; import static org.sonar.db.DatabaseUtils.executeLargeInputs; @@ -188,6 +192,21 @@ public class ActiveRuleDao implements Dao { } } + public Map<String, Long> countActiveRulesByProfileKey(DbSession dbSession, OrganizationDto organization) { + return KeyLongValue.toMap( + mapper(dbSession).countActiveRulesByProfileKey(organization.getUuid())); + } + + public Map<String, Long> countActiveRulesForRuleStatusByProfileKey(DbSession dbSession, OrganizationDto organization, RuleStatus ruleStatus) { + return KeyLongValue.toMap( + mapper(dbSession).countActiveRulesForRuleStatusByProfileKey(organization.getUuid(), ruleStatus)); + } + + public Map<String, Long> countActiveRulesForInheritanceByProfileKey(DbSession dbSession, OrganizationDto organization, String inheritance) { + return KeyLongValue.toMap( + mapper(dbSession).countActiveRulesForInheritanceByProfileKey(organization.getUuid(), inheritance)); + } + private static ActiveRuleMapper mapper(DbSession session) { return session.getMapper(ActiveRuleMapper.class); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java index 75ef5f059f9..af4ee4eabc1 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java @@ -23,6 +23,8 @@ import java.util.Collection; import java.util.List; import javax.annotation.CheckForNull; import org.apache.ibatis.annotations.Param; +import org.sonar.api.rule.RuleStatus; +import org.sonar.db.KeyLongValue; public interface ActiveRuleMapper { @@ -64,4 +66,10 @@ public interface ActiveRuleMapper { List<ActiveRuleParamDto> selectParamsByActiveRuleIds(@Param("ids") List<Integer> ids); List<ActiveRuleParamDto> selectAllParams(); + + List<KeyLongValue> countActiveRulesByProfileKey(@Param("organizationUuid") String organizationUuid); + + List<KeyLongValue> countActiveRulesForRuleStatusByProfileKey(@Param("organizationUuid") String organizationUuid, @Param("ruleStatus") RuleStatus ruleStatus); + + List<KeyLongValue> countActiveRulesForInheritanceByProfileKey(@Param("organizationUuid") String organizationUuid, @Param("inheritance") String inheritance); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml index bafcddb727e..c4e104d4796 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml @@ -187,5 +187,31 @@ from active_rule_parameters p </select> + <select id="countActiveRulesByProfileKey" resultType="KeyLongValue" parameterType="map"> + select p.kee as "key", count(ar.id) as "value" + from active_rules ar + inner join rules_profiles p on p.id = ar.profile_id + where p.organization_uuid = #{organizationUuid, jdbcType=VARCHAR} + group by p.kee + </select> + + <select id="countActiveRulesForRuleStatusByProfileKey" resultType="KeyLongValue" parameterType="map"> + select p.kee as "key", count(ar.id) as "value" + from active_rules ar + inner join rules_profiles p on p.id = ar.profile_id + inner join rules r on r.id = ar.rule_id + where p.organization_uuid = #{organizationUuid, jdbcType=VARCHAR} + and r.status = #{ruleStatus, jdbcType=VARCHAR} + group by p.kee + </select> + + <select id="countActiveRulesForInheritanceByProfileKey" resultType="KeyLongValue" parameterType="map"> + select p.kee as "key", count(ar.id) as "value" + from active_rules ar + inner join rules_profiles p on p.id = ar.profile_id + where p.organization_uuid = #{organizationUuid, jdbcType=VARCHAR} + and ar.inheritance = #{inheritance, jdbcType=VARCHAR} + group by p.kee + </select> </mapper> diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java index 0482e942a68..7fd29dac487 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java @@ -21,6 +21,7 @@ package org.sonar.db.qualityprofile; import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -44,6 +45,7 @@ import static com.google.common.collect.Lists.newArrayList; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.MapEntry.entry; import static org.assertj.guava.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -599,4 +601,83 @@ public class ActiveRuleDaoTest { underTest.deleteParamsByRuleParam(dbSession, rule1.getId(), "unknown"); } + + @Test + public void test_countActiveRulesByProfileKey_for_a_specified_organization() { + dbTester.qualityProfiles().activateRule(profile1, rule1); + dbTester.qualityProfiles().activateRule(profile1, rule2); + dbTester.qualityProfiles().activateRule(profile2, rule1); + + Map<String, Long> counts = underTest.countActiveRulesByProfileKey(dbSession, organization); + + assertThat(counts).containsOnly( + entry(profile1.getKey(), 2L), + entry(profile2.getKey(), 1L)); + } + + @Test + public void countActiveRulesByProfileKey_returns_empty_map_if_organization_does_not_exist() { + Map<String, Long> counts = underTest.countActiveRulesByProfileKey(dbSession, OrganizationTesting.newOrganizationDto()); + + assertThat(counts).isEmpty(); + } + + @Test + public void countActiveRulesByProfileKey_returns_empty_map_if_profile_does_not_have_active_rules() { + Map<String, Long> counts = underTest.countActiveRulesByProfileKey(dbSession, organization); + + assertThat(counts).isEmpty(); + } + + @Test + public void test_countActiveRulesForRuleStatusByProfileKey_for_a_specified_organization() { + RuleDto betaRule1 = dbTester.rules().insertRule(RuleTesting.newRuleDto().setStatus(RuleStatus.BETA)); + RuleDto betaRule2 = dbTester.rules().insertRule(RuleTesting.newRuleDto().setStatus(RuleStatus.BETA)); + dbTester.qualityProfiles().activateRule(profile1, rule1); + dbTester.qualityProfiles().activateRule(profile2, betaRule1); + dbTester.qualityProfiles().activateRule(profile2, betaRule2); + + Map<String, Long> counts = underTest.countActiveRulesForRuleStatusByProfileKey(dbSession, organization, RuleStatus.BETA); + + assertThat(counts).containsOnly(entry(profile2.getKey(), 2L)); + } + + @Test + public void countActiveRulesForRuleStatusByProfileKey_returns_empty_map_if_organization_does_not_exist() { + Map<String, Long> counts = underTest.countActiveRulesForRuleStatusByProfileKey(dbSession, OrganizationTesting.newOrganizationDto(), RuleStatus.READY); + + assertThat(counts).isEmpty(); + } + + @Test + public void countActiveRulesForRuleStatusByProfileKey_returns_empty_map_if_profile_does_not_have_rules_with_specified_status() { + Map<String, Long> counts = underTest.countActiveRulesForRuleStatusByProfileKey(dbSession, organization, RuleStatus.DEPRECATED); + + assertThat(counts).isEmpty(); + } + + @Test + public void test_countActiveRulesForInheritanceByProfileKey_for_a_specified_organization() { + dbTester.qualityProfiles().activateRule(profile1, rule1); + dbTester.qualityProfiles().activateRule(profile2, rule1, ar -> ar.setInheritance(ActiveRuleDto.OVERRIDES)); + dbTester.qualityProfiles().activateRule(profile2, rule2, ar -> ar.setInheritance(ActiveRuleDto.INHERITED)); + + Map<String, Long> counts = underTest.countActiveRulesForInheritanceByProfileKey(dbSession, organization, ActiveRuleDto.OVERRIDES); + + assertThat(counts).containsOnly(entry(profile2.getKey(), 1L)); + } + + @Test + public void countActiveRulesForInheritanceByProfileKey_returns_empty_map_if_organization_does_not_exist() { + Map<String, Long> counts = underTest.countActiveRulesForInheritanceByProfileKey(dbSession, OrganizationTesting.newOrganizationDto(), ActiveRuleDto.OVERRIDES); + + assertThat(counts).isEmpty(); + } + + @Test + public void countActiveRulesForInheritanceByProfileKey_returns_empty_map_if_profile_does_not_have_rules_with_specified_status() { + Map<String, Long> counts = underTest.countActiveRulesForInheritanceByProfileKey(dbSession, organization, ActiveRuleDto.OVERRIDES); + + assertThat(counts).isEmpty(); + } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java index 3ecba000939..25e58c5b6e4 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java @@ -27,6 +27,10 @@ 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.rule.RuleDto; + +import static org.sonar.api.rule.Severity.MAJOR; +import static org.sonar.db.qualityprofile.ActiveRuleDto.createFor; public class QualityProfileDbTester { private final DbClient dbClient; @@ -78,4 +82,12 @@ public class QualityProfileDbTester { dbSession.commit(); } + public void activateRule(QualityProfileDto profile, RuleDto rule, Consumer<ActiveRuleDto>... consumers) { + ActiveRuleDto activeRule = createFor(profile, rule).setSeverity(MAJOR); + for (Consumer<ActiveRuleDto> consumer : consumers) { + consumer.accept(activeRule); + } + dbClient.activeRuleDao().insert(dbSession, activeRule); + dbSession.commit(); + } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDbTester.java index 298fdbad504..73def5548eb 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDbTester.java @@ -40,6 +40,9 @@ public class RuleDbTester { return ruleDto; } + /** + * Create and persist a rule with random values. + */ public RuleDto insertRule() { return insertRule(rule -> { }); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java index 4f0042e6d2f..7a2f30b5bba 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java @@ -48,7 +48,6 @@ import org.sonar.server.platform.TempFolderProvider; import org.sonar.server.platform.UrlSettings; import org.sonar.server.platform.cluster.ClusterImpl; import org.sonar.server.platform.db.EmbeddedDatabaseFactory; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.search.EsSearchModule; import org.sonar.server.setting.ThreadLocalSettings; @@ -109,7 +108,6 @@ public class PlatformLevel1 extends PlatformLevel { // rules/qprofiles RuleIndex.class, - ActiveRuleIndex.class, // issues IssueIndex.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java deleted file mode 100644 index 9bd7dc1fb3f..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.index; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.annotation.Nullable; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.aggregations.Aggregation; -import org.elasticsearch.search.aggregations.AggregationBuilders; -import org.elasticsearch.search.aggregations.Aggregations; -import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.metrics.valuecount.InternalValueCount; -import org.sonar.api.rule.RuleStatus; -import org.sonar.server.es.BaseIndex; -import org.sonar.server.es.EsClient; -import org.sonar.server.rule.index.RuleIndexDefinition; -import org.sonar.server.search.FacetValue; - -import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE; -import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY; -import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY; -import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_STATUS; -import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_ACTIVE_RULE; -import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_RULE; - -/** - * The unique entry-point to interact with Elasticsearch index "active rules". - * All the requests are listed here. - */ -public class ActiveRuleIndex extends BaseIndex { - - public static final String COUNT_ACTIVE_RULES = "countActiveRules"; - - public ActiveRuleIndex(EsClient client) { - super(client); - } - - public Map<String, Long> countAllByQualityProfileKey() { - return countByField(FIELD_ACTIVE_RULE_PROFILE_KEY, - QueryBuilders.hasParentQuery(INDEX_TYPE_RULE.getType(), - QueryBuilders.boolQuery() - .mustNot(QueryBuilders.termQuery(FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))); - } - - public Map<String, Long> countAllDeprecatedByQualityProfileKey() { - return countByField(FIELD_ACTIVE_RULE_PROFILE_KEY, - QueryBuilders.hasParentQuery(INDEX_TYPE_RULE.getType(), - QueryBuilders.boolQuery().must( - QueryBuilders.termQuery(FIELD_RULE_STATUS, RuleStatus.DEPRECATED.name())))); - } - - private Map<String, Long> countByField(String indexField, QueryBuilder filter) { - Map<String, Long> counts = new HashMap<>(); - - SearchRequestBuilder request = getClient().prepareSearch(INDEX_TYPE_ACTIVE_RULE) - .setQuery(QueryBuilders.filteredQuery( - QueryBuilders.matchAllQuery(), - filter)) - .setSize(0) - .addAggregation(AggregationBuilders - .terms(indexField) - .field(indexField) - .order(Terms.Order.count(false)) - .size(Integer.MAX_VALUE) - .minDocCount(0)); - - SearchResponse response = request.get(); - - Terms values = response.getAggregations().get(indexField); - - for (Terms.Bucket value : values.getBuckets()) { - counts.put(value.getKeyAsString(), value.getDocCount()); - } - return counts; - } - - public Map<String, Multimap<String, FacetValue>> getStatsByProfileKeys(List<String> keys) { - SearchRequestBuilder request = getClient().prepareSearch(INDEX_TYPE_RULE.getIndex()) - .setQuery(QueryBuilders.boolQuery().must(QueryBuilders.termsQuery(FIELD_ACTIVE_RULE_PROFILE_KEY, keys)).filter( - QueryBuilders.boolQuery() - .mustNot(QueryBuilders.hasParentQuery(INDEX_TYPE_RULE.getType(), - QueryBuilders.termQuery(FIELD_RULE_STATUS, RuleStatus.REMOVED.name()))))) - .addAggregation(AggregationBuilders.terms(FIELD_ACTIVE_RULE_PROFILE_KEY) - .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY).size(0) - .subAggregation(AggregationBuilders.terms(FIELD_ACTIVE_RULE_INHERITANCE) - .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE)) - .subAggregation(AggregationBuilders.terms(FIELD_ACTIVE_RULE_SEVERITY) - .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY)) - .subAggregation(AggregationBuilders.count(COUNT_ACTIVE_RULES))) - .setSize(0); - SearchResponse response = request.get(); - Map<String, Multimap<String, FacetValue>> stats = new HashMap<>(); - Aggregation aggregation = response.getAggregations().get(FIELD_ACTIVE_RULE_PROFILE_KEY); - for (Terms.Bucket value : ((Terms) aggregation).getBuckets()) { - stats.put(value.getKeyAsString(), processAggregations(value.getAggregations())); - } - - return stats; - } - - private static Multimap<String, FacetValue> processAggregations(@Nullable Aggregations aggregations) { - Multimap<String, FacetValue> stats = ArrayListMultimap.create(); - if (aggregations == null) { - return stats; - } - for (Aggregation aggregation : aggregations.asList()) { - if (aggregation instanceof StringTerms) { - for (Terms.Bucket value : ((Terms) aggregation).getBuckets()) { - FacetValue facetValue = new FacetValue(value.getKeyAsString(), value.getDocCount()); - stats.put(aggregation.getName(), facetValue); - } - } else if (aggregation instanceof InternalValueCount) { - InternalValueCount count = (InternalValueCount) aggregation; - FacetValue facetValue = new FacetValue(count.getName(), count.getValue()); - stats.put(count.getName(), facetValue); - } - } - return stats; - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java index 5f25333ee85..97a431af65d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java @@ -19,11 +19,8 @@ */ package org.sonar.server.qualityprofile.ws; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Multimap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import javax.annotation.Nullable; import org.sonar.api.resources.Languages; import org.sonar.api.server.ws.Request; @@ -34,26 +31,21 @@ import org.sonar.api.utils.text.JsonWriter; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.qualityprofile.ActiveRuleDao; +import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; -import org.sonar.server.rule.index.RuleIndexDefinition; -import org.sonar.server.search.FacetValue; - -import static org.sonar.server.qualityprofile.index.ActiveRuleIndex.COUNT_ACTIVE_RULES; public class InheritanceAction implements QProfileWsAction { private final DbClient dbClient; private final QProfileLookup profileLookup; - private final ActiveRuleIndex activeRuleIndex; private final QProfileWsSupport wsSupport; private final Languages languages; - public InheritanceAction(DbClient dbClient, QProfileLookup profileLookup, ActiveRuleIndex activeRuleIndex, QProfileWsSupport wsSupport, Languages languages) { + public InheritanceAction(DbClient dbClient, QProfileLookup profileLookup, QProfileWsSupport wsSupport, Languages languages) { this.dbClient = dbClient; this.profileLookup = profileLookup; - this.activeRuleIndex = activeRuleIndex; this.wsSupport = wsSupport; this.languages = languages; } @@ -81,88 +73,66 @@ public class InheritanceAction implements QProfileWsAction { .orElseThrow(() -> new IllegalStateException(String.format("Could not find organization with uuid '%s' for quality profile '%s'", organizationUuid, profile.getKee()))); List<QualityProfileDto> ancestors = profileLookup.ancestors(profile, dbSession); List<QualityProfileDto> children = dbClient.qualityProfileDao().selectChildren(dbSession, profile.getKey()); - Map<String, Multimap<String, FacetValue>> profileStats = getAllProfileStats(dbSession, organization); + Statistics statistics = new Statistics(dbSession, organization); - writeResponse(response.newJsonWriter(), profile, ancestors, children, profileStats); + writeResponse(response.newJsonWriter(), profile, ancestors, children, statistics); } } - @VisibleForTesting - Map<String, Multimap<String, FacetValue>> getAllProfileStats(DbSession dbSession, OrganizationDto organization) { - List<String> keys = dbClient.qualityProfileDao().selectAll(dbSession, organization).stream().map(QualityProfileDto::getKey).collect(Collectors.toList()); - return activeRuleIndex.getStatsByProfileKeys(keys); - } - - private static void writeResponse(JsonWriter json, QualityProfileDto profile, List<QualityProfileDto> ancestors, List<QualityProfileDto> children, - Map<String, Multimap<String, FacetValue>> profileStats) { + private static void writeResponse(JsonWriter json, QualityProfileDto profile, List<QualityProfileDto> ancestors, List<QualityProfileDto> children, Statistics statistics) { json.beginObject(); - writeProfile(json, profile, profileStats); - writeAncestors(json, ancestors, profileStats); - writeChildren(json, children, profileStats); + writeProfile(json, profile, statistics); + writeAncestors(json, ancestors, statistics); + writeChildren(json, children, statistics); json.endObject().close(); } - private static void writeProfile(JsonWriter json, QualityProfileDto profile, Map<String, Multimap<String, FacetValue>> profileStats) { + private static void writeProfile(JsonWriter json, QualityProfileDto profile, Statistics statistics) { String profileKey = profile.getKey(); json.name("profile"); - writeProfileAttributes(json, profileKey, profile.getName(), profile.getParentKee(), profileStats); + writeProfileAttributes(json, profileKey, profile.getName(), profile.getParentKee(), statistics); } - private static void writeAncestors(JsonWriter json, List<QualityProfileDto> ancestors, Map<String, Multimap<String, FacetValue>> profileStats) { + private static void writeAncestors(JsonWriter json, List<QualityProfileDto> ancestors, Statistics statistics) { json.name("ancestors").beginArray(); for (QualityProfileDto ancestor : ancestors) { String ancestorKey = ancestor.getKey(); - writeProfileAttributes(json, ancestorKey, ancestor.getName(), ancestor.getParentKee(), profileStats); + writeProfileAttributes(json, ancestorKey, ancestor.getName(), ancestor.getParentKee(), statistics); } json.endArray(); } - private static void writeChildren(JsonWriter json, List<QualityProfileDto> children, Map<String, Multimap<String, FacetValue>> profileStats) { + private static void writeChildren(JsonWriter json, List<QualityProfileDto> children, Statistics statistics) { json.name("children").beginArray(); for (QualityProfileDto child : children) { String childKey = child.getKey(); - writeProfileAttributes(json, childKey, child.getName(), null, profileStats); + writeProfileAttributes(json, childKey, child.getName(), null, statistics); } json.endArray(); } - private static void writeProfileAttributes(JsonWriter json, String key, String name, @Nullable String parentKey, Map<String, Multimap<String, FacetValue>> profileStats) { + private static void writeProfileAttributes(JsonWriter json, String key, String name, @Nullable String parentKey, Statistics statistics) { json.beginObject(); json.prop("key", key) .prop("name", name) .prop("parent", parentKey); - writeStats(json, key, profileStats); + writeStats(json, key, statistics); json.endObject(); } - private static void writeStats(JsonWriter json, String profileKey, Map<String, Multimap<String, FacetValue>> profileStats) { - if (profileStats.containsKey(profileKey)) { - Multimap<String, FacetValue> ancestorStats = profileStats.get(profileKey); - json.prop("activeRuleCount", getActiveRuleCount(ancestorStats)); - json.prop("overridingRuleCount", getOverridingRuleCount(ancestorStats)); - } else { - json.prop("activeRuleCount", 0); - } + private static void writeStats(JsonWriter json, String profileKey, Statistics statistics) { + json.prop("activeRuleCount", statistics.countRulesByProfileKey.getOrDefault(profileKey, 0L)); + json.prop("overridingRuleCount", statistics.countOverridingRulesByProfileKey.getOrDefault(profileKey, 0L)); } - private static Long getActiveRuleCount(Multimap<String, FacetValue> profileStats) { - Long result = null; - if (profileStats.containsKey(COUNT_ACTIVE_RULES)) { - result = profileStats.get(COUNT_ACTIVE_RULES).iterator().next().getValue(); - } - return result; - } + private class Statistics { + private final Map<String, Long> countRulesByProfileKey; + private final Map<String, Long> countOverridingRulesByProfileKey; - private static Long getOverridingRuleCount(Multimap<String, FacetValue> profileStats) { - Long result = null; - if (profileStats.containsKey(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE)) { - for (FacetValue value : profileStats.get(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE)) { - if ("OVERRIDES".equals(value.getKey())) { - result = value.getValue(); - } - } + private Statistics(DbSession dbSession, OrganizationDto organization) { + ActiveRuleDao dao = dbClient.activeRuleDao(); + countRulesByProfileKey = dao.countActiveRulesByProfileKey(dbSession, organization); + countOverridingRulesByProfileKey = dao.countActiveRulesForInheritanceByProfileKey(dbSession, organization, ActiveRuleDto.OVERRIDES); } - return result; } - } 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 87517cb2d59..94c96edbc46 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 @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.sonar.api.resources.Languages; +import org.sonar.api.rule.RuleStatus; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -31,7 +32,6 @@ import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.server.util.LanguageParamUtils; import org.sonarqube.ws.QualityProfiles.SearchWsResponse; @@ -54,14 +54,12 @@ public class SearchAction implements QProfileWsAction { private final SearchDataLoader dataLoader; private final Languages languages; - private final ActiveRuleIndex activeRuleIndex; private final DbClient dbClient; private final QProfileWsSupport wsSupport; - public SearchAction(SearchDataLoader dataLoader, Languages languages, ActiveRuleIndex activeRuleIndex, DbClient dbClient, QProfileWsSupport wsSupport) { + public SearchAction(SearchDataLoader dataLoader, Languages languages, DbClient dbClient, QProfileWsSupport wsSupport) { this.dataLoader = dataLoader; this.languages = languages; - this.activeRuleIndex = activeRuleIndex; this.dbClient = dbClient; this.wsSupport = wsSupport; } @@ -93,7 +91,7 @@ public class SearchAction implements QProfileWsAction { action .createParam(PARAM_DEFAULTS) .setDescription(format("Return the quality profile marked as default for each language. " + - "If provided, then the parameters '%s', '%s' must not be set.", + "If provided, then the parameters '%s', '%s' must not be set.", PARAM_LANGUAGE, PARAM_PROJECT_KEY)) .setDefaultValue(false) .setBooleanPossibleValues(); @@ -132,8 +130,8 @@ public class SearchAction implements QProfileWsAction { return new SearchData() .setOrganization(organization) .setProfiles(dataLoader.findProfiles(dbSession, request, organization)) - .setActiveRuleCountByProfileKey(activeRuleIndex.countAllByQualityProfileKey()) - .setActiveDeprecatedRuleCountByProfileKey(activeRuleIndex.countAllDeprecatedByQualityProfileKey()) + .setActiveRuleCountByProfileKey(dbClient.activeRuleDao().countActiveRulesByProfileKey(dbSession, organization)) + .setActiveDeprecatedRuleCountByProfileKey(dbClient.activeRuleDao().countActiveRulesForRuleStatusByProfileKey(dbSession, organization, RuleStatus.DEPRECATED)) .setProjectCountByProfileKey(dbClient.qualityProfileDao().countProjectsByProfileKey(dbSession)); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java deleted file mode 100644 index 33ee5da82c9..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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; - -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.util.Map; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.api.profiles.ProfileExporter; -import org.sonar.api.profiles.ProfileImporter; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rule.RuleStatus; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.utils.ValidationMessages; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.rule.RuleDto; -import org.sonar.db.rule.RuleTesting; -import org.sonar.server.organization.DefaultOrganizationProvider; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; -import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; -import org.sonar.server.rule.index.RuleIndexer; -import org.sonar.server.tester.ServerTester; -import org.sonar.server.tester.UserSessionRule; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; -import static org.sonar.db.rule.RuleTesting.newXooX1; -import static org.sonar.db.rule.RuleTesting.newXooX2; -import static org.sonar.db.rule.RuleTesting.newXooX3; -import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P1_KEY; -import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P2_KEY; -import static org.sonar.server.qualityprofile.QProfileTesting.getDefaultOrganization; - -public class QProfileServiceMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester().withStartupTasks().withEsIndexes().addComponents(XooProfileImporter.class, XooExporter.class); - - @org.junit.Rule - public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester); - - private DbClient dbClient; - private DbSession dbSession; - private QProfileService service; - private ActiveRuleIndex activeRuleIndex; - private RuleIndexer ruleIndexer; - private ActiveRuleIndexer activeRuleIndexer; - - private RuleDto xooRule1 = newXooX1().setSeverity("MINOR"); - - @Before - public void before() { - tester.clearDbAndIndexes(); - dbClient = tester.get(DbClient.class); - dbSession = dbClient.openSession(false); - service = tester.get(QProfileService.class); - ruleIndexer = tester.get(RuleIndexer.class); - activeRuleIndexer = tester.get(ActiveRuleIndexer.class); - activeRuleIndex = tester.get(ActiveRuleIndex.class); - - dbClient.ruleDao().insert(dbSession, xooRule1); - - // create pre-defined profiles P1 and P2 - OrganizationDto defaultOrganization = getDefaultOrganization(tester, dbClient, dbSession); - dbClient.qualityProfileDao().insert(dbSession, QProfileTesting.newXooP1(defaultOrganization), QProfileTesting.newXooP2(defaultOrganization)); - - dbSession.commit(); - dbSession.clearCache(); - ruleIndexer.index(); - } - - @After - public void after() { - dbSession.close(); - } - - @Test - public void count_by_all_profiles() { - logInAsQProfileAdministrator(); - - service.activate(XOO_P1_KEY, new RuleActivation(RuleTesting.XOO_X1).setSeverity("BLOCKER")); - service.activate(XOO_P2_KEY, new RuleActivation(RuleTesting.XOO_X1).setSeverity("BLOCKER")); - dbSession.clearCache(); - activeRuleIndexer.index(); - - Map<String, Long> counts = activeRuleIndex.countAllByQualityProfileKey(); - assertThat(counts).hasSize(2); - assertThat(counts.keySet()).containsOnly(XOO_P1_KEY, XOO_P2_KEY); - assertThat(counts.values()).containsOnly(1L, 1L); - } - - @Test - public void count_by_all_deprecated_profiles() { - logInAsQProfileAdministrator(); - - RuleDto xooRule2 = newXooX2().setStatus(RuleStatus.DEPRECATED); - RuleDto xooRule3 = newXooX3().setStatus(RuleStatus.DEPRECATED); - dbClient.ruleDao().insert(dbSession, xooRule2); - dbClient.ruleDao().insert(dbSession, xooRule3); - dbSession.commit(); - ruleIndexer.index(); - - // active some rules - service.activate(XOO_P1_KEY, new RuleActivation(xooRule1.getKey())); - service.activate(XOO_P1_KEY, new RuleActivation(xooRule2.getKey())); - service.activate(XOO_P1_KEY, new RuleActivation(xooRule3.getKey())); - service.activate(XOO_P2_KEY, new RuleActivation(xooRule1.getKey())); - service.activate(XOO_P2_KEY, new RuleActivation(xooRule3.getKey())); - dbSession.commit(); - - Map<String, Long> counts = activeRuleIndex.countAllDeprecatedByQualityProfileKey(); - assertThat(counts) - .hasSize(2) - .containsEntry(XOO_P1_KEY, 2L) - .containsEntry(XOO_P2_KEY, 1L); - } - - public static class XooExporter extends ProfileExporter { - public XooExporter() { - super("xootool", "Xoo Tool"); - } - - @Override - public String[] getSupportedLanguages() { - return new String[] {"xoo"}; - } - - @Override - public String getMimeType() { - return "plain/custom"; - } - - @Override - public void exportProfile(RulesProfile profile, Writer writer) { - try { - writer.write("xoo -> " + profile.getName() + " -> " + profile.getActiveRules().size()); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - } - - public static class XooProfileImporter extends ProfileImporter { - public XooProfileImporter() { - super("XooProfileImporter", "Xoo Profile Importer"); - } - - @Override - public String[] getSupportedLanguages() { - return new String[] {"xoo"}; - } - - @Override - public RulesProfile importProfile(Reader reader, ValidationMessages messages) { - RulesProfile rulesProfile = RulesProfile.create(); - Rule rule = Rule.create("xoo", "R1"); - rule.createParameter("acceptWhitespace"); - org.sonar.api.rules.ActiveRule activeRule = rulesProfile.activateRule(rule, RulePriority.CRITICAL); - activeRule.setParameter("acceptWhitespace", "true"); - return rulesProfile; - } - } - - private void logInAsQProfileAdministrator() { - userSessionRule.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, tester.get(DefaultOrganizationProvider.class).get().getUuid()); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java index a5465f6e9f1..e67ddb0a0ed 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java @@ -43,7 +43,6 @@ import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; import org.sonar.server.es.SearchOptions; import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleIndexer; @@ -92,7 +91,6 @@ public class RuleActivatorMediumTest { RuleIndexer ruleIndexer; - ActiveRuleIndex activeRuleIndex; ActiveRuleIndexer activeRuleIndexer; QualityProfileDto profileDto; @@ -103,7 +101,6 @@ public class RuleActivatorMediumTest { db = tester.get(DbClient.class); dbSession = db.openSession(false); ruleActivator = tester.get(RuleActivator.class); - activeRuleIndex = tester.get(ActiveRuleIndex.class); activeRuleIndexer = tester.get(ActiveRuleIndexer.class); ruleIndexer = tester.get(RuleIndexer.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java index cb660f6251f..1866cabae52 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java @@ -19,6 +19,7 @@ */ package org.sonar.server.qualityprofile.index; +import org.apache.commons.lang.RandomStringUtils; import org.sonar.api.rule.Severity; import org.sonar.db.qualityprofile.ActiveRuleKey; import org.sonar.db.rule.RuleTesting; @@ -31,6 +32,7 @@ public class ActiveRuleDocTesting { public static ActiveRuleDoc newDoc(ActiveRuleKey key) { return new ActiveRuleDoc(key) + .setOrganizationUuid(RandomStringUtils.random(40)) .setSeverity(Severity.CRITICAL) .setInheritance(null).setCreatedAt(150000000L) .setUpdatedAt(160000000L); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexTest.java deleted file mode 100644 index a146836478e..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.index; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Multimap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.config.MapSettings; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.System2; -import org.sonar.db.qualityprofile.ActiveRuleKey; -import org.sonar.db.rule.RuleTesting; -import org.sonar.server.es.EsTester; -import org.sonar.server.rule.index.RuleDoc; -import org.sonar.server.rule.index.RuleDocTesting; -import org.sonar.server.rule.index.RuleIndexDefinition; -import org.sonar.server.rule.index.RuleIndexer; -import org.sonar.server.search.FacetValue; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.sonar.api.rule.Severity.BLOCKER; -import static org.sonar.api.rule.Severity.MAJOR; -import static org.sonar.api.rule.Severity.MINOR; -import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.INHERITED; -import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.OVERRIDES; -import static org.sonar.server.rule.index.RuleDocTesting.newDoc; -import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_ACTIVE_RULE; - -public class ActiveRuleIndexTest { - - private static final RuleKey RULE_KEY_1 = RuleTesting.XOO_X1; - private static final RuleKey RULE_KEY_2 = RuleTesting.XOO_X2; - private static final String QUALITY_PROFILE_KEY1 = "qp1"; - private static final String QUALITY_PROFILE_KEY2 = "qp2"; - - private System2 system2 = System2.INSTANCE; - - @Rule - public EsTester tester = new EsTester(new RuleIndexDefinition(new MapSettings())); - - ActiveRuleIndex index; - ActiveRuleIndexer activeRuleIndexer; - RuleIndexer ruleIndexer; - - @Before - public void setUp() { - activeRuleIndexer = new ActiveRuleIndexer(system2, null, tester.client()); - ruleIndexer = new RuleIndexer(system2, null, tester.client()); - index = new ActiveRuleIndex(tester.client()); - } - - @Test - public void count_all_by_quality_profile_key() { - indexRules(RuleDocTesting.newDoc(RULE_KEY_1)); - - indexActiveRules( - ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)), - ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1))); - - // 0. Test base case - assertThat(tester.countDocuments(INDEX_TYPE_ACTIVE_RULE)).isEqualTo(2); - - // 1. Assert by term aggregation; - assertThat(index.countAllByQualityProfileKey()).containsOnly(entry(QUALITY_PROFILE_KEY1, 1L), entry(QUALITY_PROFILE_KEY2, 1L)); - } - - @Test - public void stats_for_all() { - indexRules( - newDoc(RULE_KEY_1), - newDoc(RULE_KEY_2)); - - ActiveRuleKey activeRuleKey1 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1); - ActiveRuleKey activeRuleKey2 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2); - - indexActiveRules( - ActiveRuleDocTesting.newDoc(activeRuleKey1).setSeverity(BLOCKER), - ActiveRuleDocTesting.newDoc(activeRuleKey2).setSeverity(MINOR), - // Profile 2 is a child a profile 1 - ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)).setSeverity(MAJOR).setInheritance(INHERITED.name()), - ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2)).setSeverity(BLOCKER).setInheritance(OVERRIDES.name())); - - // 0. Test base case - assertThat(tester.countDocuments(INDEX_TYPE_ACTIVE_RULE)).isEqualTo(4); - - // 1. Assert by term aggregation; - Map<String, Multimap<String, FacetValue>> stats = index.getStatsByProfileKeys(ImmutableList.of(QUALITY_PROFILE_KEY1, QUALITY_PROFILE_KEY2)); - assertThat(stats).hasSize(2); - } - - /** - * SONAR-5844 - */ - @Test - public void stats_for_all_with_lof_of_profiles() { - indexRules(RuleDocTesting.newDoc(RULE_KEY_1), RuleDocTesting.newDoc(RULE_KEY_2)); - - indexActiveRules( - ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)), - ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1))); - - List<String> profileKeys = new ArrayList<>(); - List<ActiveRuleDoc> docs = new ArrayList<>(); - for (int i = 0; i < 30; i++) { - String profileKey = "profile-" + i; - profileKeys.add(profileKey); - docs.add(ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(profileKey, RULE_KEY_1)).setSeverity(BLOCKER)); - docs.add(ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(profileKey, RULE_KEY_2)).setSeverity(MAJOR)); - } - indexActiveRules(docs.toArray(new ActiveRuleDoc[] {})); - - Map<String, Multimap<String, FacetValue>> stats = index.getStatsByProfileKeys(profileKeys); - assertThat(stats).hasSize(30); - } - - private void indexActiveRules(ActiveRuleDoc... docs) { - activeRuleIndexer.index(asList(docs).iterator()); - } - - private void indexRules(RuleDoc... rules) { - ruleIndexer.index(asList(rules).iterator()); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java index 6f79e574283..e435c9d2236 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java @@ -19,10 +19,8 @@ */ package org.sonar.server.qualityprofile.ws; -import com.google.common.collect.Multimap; import java.util.ArrayList; import java.util.Date; -import java.util.Map; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -52,19 +50,16 @@ import org.sonar.server.qualityprofile.QProfileTesting; import org.sonar.server.qualityprofile.RuleActivation; import org.sonar.server.qualityprofile.RuleActivator; import org.sonar.server.qualityprofile.RuleActivatorContextFactory; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleIndexDefinition; import org.sonar.server.rule.index.RuleIndexer; -import org.sonar.server.search.FacetValue; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.util.TypeValidations; import org.sonar.server.ws.WsActionTester; import org.sonar.test.JsonAssert; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; -import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; public class InheritanceActionTest { @@ -98,10 +93,8 @@ public class InheritanceActionTest { underTest = new InheritanceAction( dbClient, new QProfileLookup(dbClient), - new ActiveRuleIndex(esClient), new QProfileWsSupport(dbClient, userSession, defaultOrganizationProvider), - new Languages() - ); + new Languages()); wsActionTester = new WsActionTester(underTest); ruleActivator = new RuleActivator( System2.INSTANCE, @@ -110,15 +103,13 @@ public class InheritanceActionTest { new RuleActivatorContextFactory(dbClient), new TypeValidations(new ArrayList<>()), activeRuleIndexer, - userSession - ); + userSession); service = new QProfileService( dbClient, activeRuleIndexer, ruleActivator, userSession, - defaultOrganizationProvider - ); + defaultOrganizationProvider); organization = dbTester.organizations().insert(); } @@ -215,14 +206,6 @@ public class InheritanceActionTest { userSession.logIn() .addPermission(ADMINISTER_QUALITY_PROFILES, organization.getUuid()); - - Map<String, Multimap<String, FacetValue>> stats = underTest.getAllProfileStats(dbSession, organization); - - assertThat(stats.size()).isEqualTo(2); - assertThat(stats.get(profile1.getKey()).size()).isEqualTo(3); - assertThat(stats.get(profile1.getKey()).get(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY).size()).isEqualTo(1); - assertThat(stats.get(profile1.getKey()).get(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE).size()).isEqualTo(1); - assertThat(stats.get(profile1.getKey()).get("countActiveRules").size()).isEqualTo(1); } private QualityProfileDto createProfile(String lang, String name, String key) { 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 8877613f098..054dbb69f85 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 @@ -66,7 +66,7 @@ public class QProfilesWsTest { new BulkRuleActivationActions(profileService, null), new CreateAction(null, null, null, languages, wsSupport, userSessionRule, null, importers), new ImportersAction(importers), - new SearchAction(null, languages, null, dbClient, wsSupport ), + new SearchAction(null, languages, dbClient, wsSupport), new SetDefaultAction(languages, null, null, wsSupport), new ProjectsAction(null, userSessionRule), new ChangelogAction(null, wsSupport, languages, dbClient), @@ -74,7 +74,7 @@ public class QProfilesWsTest { new CompareAction(null, null, languages), new DeleteAction(languages, null, null, userSessionRule, wsSupport), new ExportersAction(), - new InheritanceAction(null, null, null, null, languages), + new InheritanceAction(null, null, null, languages), new RenameAction(dbClient, userSessionRule))).controller(QProfilesWs.API_ENDPOINT); } 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 452f2c5e225..ec3f84206fd 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,7 +19,6 @@ */ package org.sonar.server.qualityprofile.ws; -import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -31,12 +30,12 @@ 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.utils.DateUtils; 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.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; @@ -45,13 +44,13 @@ import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.qualityprofile.QualityProfileDbTester; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.db.qualityprofile.QualityProfileTesting; +import org.sonar.db.rule.RuleDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.language.LanguageTesting; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; @@ -64,8 +63,6 @@ import org.sonarqube.ws.client.qualityprofile.SearchWsRequest; import static com.google.common.base.Throwables.propagate; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.db.component.ComponentTesting.newProjectDto; @@ -90,7 +87,6 @@ public class SearchActionTest { private DbSession dbSession = db.getSession(); private QualityProfileDao qualityProfileDao = dbClient.qualityProfileDao(); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); - private ActiveRuleIndex activeRuleIndex = mock(ActiveRuleIndex.class); private QProfileWsSupport qProfileWsSupport = new QProfileWsSupport(dbClient, userSession, defaultOrganizationProvider); private Language xoo1; @@ -112,57 +108,106 @@ public class SearchActionTest { dbClient, new ComponentFinder(dbClient)), languages, - activeRuleIndex, dbClient, qProfileWsSupport); ws = new WsActionTester(underTest); } @Test - public void search_nominal() throws Exception { - when(activeRuleIndex.countAllByQualityProfileKey()).thenReturn(ImmutableMap.of( - "sonar-way-xoo1-12345", 11L, - "my-sonar-way-xoo2-34567", 33L)); - when(activeRuleIndex.countAllDeprecatedByQualityProfileKey()).thenReturn(ImmutableMap.of( - "sonar-way-xoo1-12345", 1L, - "my-sonar-way-xoo2-34567", 2L)); - - OrganizationDto organizationDto = getDefaultOrganization(); - String organizationUuid = organizationDto.getUuid(); - qualityProfileDao.insert(dbSession, - QualityProfileDto.createFor("sonar-way-xoo1-12345") - .setOrganizationUuid(organizationUuid) - .setLanguage(xoo1.getKey()) - .setName("Sonar way") - .setDefault(true), - - QualityProfileDto - .createFor("sonar-way-xoo2-23456") - .setOrganizationUuid(organizationUuid) - .setLanguage(xoo2.getKey()) - .setName("Sonar way"), - - QualityProfileDto - .createFor("my-sonar-way-xoo2-34567") - .setOrganizationUuid(organizationUuid) - .setLanguage(xoo2.getKey()) - .setName("My Sonar way") - .setParentKee("sonar-way-xoo2-23456"), - - QualityProfileDto.createFor("sonar-way-other-666") - .setOrganizationUuid(organizationUuid) - .setLanguage("other").setName("Sonar way") - .setDefault(true)); - new ComponentDao().insert(dbSession, - newProjectDto(organizationDto, "project-uuid1"), - newProjectDto(organizationDto, "project-uuid2")); - qualityProfileDao.insertProjectProfileAssociation("project-uuid1", "sonar-way-xoo2-23456", dbSession); - qualityProfileDao.insertProjectProfileAssociation("project-uuid2", "sonar-way-xoo2-23456", dbSession); - db.commit(); + public void ws_returns_the_profiles_of_default_organization() throws Exception { + OrganizationDto organization = getDefaultOrganization(); + + QualityProfileDto defaultProfile = QualityProfileDto.createFor("sonar-way-xoo1-12345") + .setOrganizationUuid(organization.getUuid()) + .setLanguage(xoo1.getKey()) + .setName("Sonar way") + .setDefault(true); + QualityProfileDto parentProfile = QualityProfileDto + .createFor("sonar-way-xoo2-23456") + .setOrganizationUuid(organization.getUuid()) + .setLanguage(xoo2.getKey()) + .setName("Sonar way"); + QualityProfileDto childProfile = QualityProfileDto + .createFor("my-sonar-way-xoo2-34567") + .setOrganizationUuid(organization.getUuid()) + .setLanguage(xoo2.getKey()) + .setName("My Sonar way") + .setParentKee(parentProfile.getKey()); + QualityProfileDto profileOnUnknownLang = QualityProfileDto.createFor("sonar-way-other-666") + .setOrganizationUuid(organization.getUuid()) + .setLanguage("other").setName("Sonar way") + .setDefault(true); + qualityProfileDao.insert(dbSession, defaultProfile, parentProfile, childProfile, profileOnUnknownLang); + + ComponentDto project1 = db.components().insertProject(organization); + ComponentDto project2 = db.components().insertProject(organization); + db.qualityProfiles().associateProjectWithQualityProfile(project1, parentProfile); + db.qualityProfiles().associateProjectWithQualityProfile(project2, parentProfile); String result = ws.newRequest().execute().getInput(); - assertJson(result).isSimilarTo(getClass().getResource("SearchActionTest/search.json")); + assertJson(result).isSimilarTo("{" + + " \"profiles\": [" + + " {" + + " \"key\": \"sonar-way-xoo1-12345\"," + + " \"name\": \"Sonar way\"," + + " \"language\": \"xoo1\"," + + " \"languageName\": \"Xoo1\"," + + " \"isInherited\": false," + + " \"isDefault\": true," + + " \"activeRuleCount\": 0," + + " \"activeDeprecatedRuleCount\": 0," + + " \"organization\": \"" + organization.getKey() + "\"" + + " }," + + " {" + + " \"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\": 0," + + " \"activeDeprecatedRuleCount\": 0," + + " \"projectCount\": 0," + + " \"organization\": \"" + organization.getKey() + "\"" + + " }," + + " {" + + " \"key\": \"sonar-way-xoo2-23456\"," + + " \"name\": \"Sonar way\"," + + " \"language\": \"xoo2\"," + + " \"languageName\": \"Xoo2\"," + + " \"isInherited\": false," + + " \"isDefault\": false," + + " \"activeRuleCount\": 0," + + " \"activeDeprecatedRuleCount\": 0," + + " \"projectCount\": 2," + + " \"organization\": \"" + organization.getKey() + "\"" + + " }" + + " ]" + + "}"); + } + + @Test + public void response_contains_statistics_on_active_rules() { + QualityProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo1.getKey())); + RuleDto rule = db.rules().insertRule(r -> r.setLanguage(xoo1.getKey())); + RuleDto deprecatedRule1 = db.rules().insertRule(r -> r.setStatus(RuleStatus.DEPRECATED)); + RuleDto deprecatedRule2 = db.rules().insertRule(r -> r.setStatus(RuleStatus.DEPRECATED)); + db.qualityProfiles().activateRule(profile, rule); + db.qualityProfiles().activateRule(profile, deprecatedRule1); + db.qualityProfiles().activateRule(profile, deprecatedRule2); + + String result = ws.newRequest().execute().getInput(); + + assertJson(result).isSimilarTo("{\"profiles\":[" + + "{" + + " \"key\":\"" + profile.getKey() + "\"," + + " \"activeRuleCount\":3," + + " \"activeDeprecatedRuleCount\":2" + + "}]}"); + } @Test @@ -328,7 +373,6 @@ public class SearchActionTest { "ORG1-D"); } - @Test public void name_and_default_query_is_valid() throws Exception { minimalValidSetup(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java index acfb40a545b..5d5d05aa330 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java @@ -51,7 +51,6 @@ import org.sonar.server.platform.Platform; import org.sonar.server.qualityprofile.QProfileService; import org.sonar.server.qualityprofile.QProfileTesting; import org.sonar.server.qualityprofile.RuleActivation; -import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleQuery; import org.sonar.server.tester.ServerTester; @@ -81,8 +80,6 @@ public class RegisterRulesMediumTest { RuleIndex ruleIndex = TESTER.get(RuleIndex.class); RuleDao ruleDao = db.ruleDao(); - ActiveRuleIndex activeRuleIndex = TESTER.get(ActiveRuleIndex.class); - @Before public void before() { TESTER.clearDbAndIndexes(); @@ -135,7 +132,6 @@ public class RegisterRulesMediumTest { assertThat(ruleIndex.search(new RuleQuery().setKey(RuleTesting.XOO_X1.toString()), new SearchOptions()).getTotal()).isEqualTo(0); assertThat(ruleIndex.search(new RuleQuery().setKey(RuleTesting.XOO_X2.toString()), new SearchOptions()).getTotal()).isEqualTo(1); assertThat(ruleIndex.search(new RuleQuery().setActivation(true), new SearchOptions()).getIds()).isEmpty(); - assertThat(activeRuleIndex.countAllByQualityProfileKey()).isEmpty(); assertThat(db.activeRuleDao().selectByProfileKey(dbSession, QProfileTesting.XOO_P1_KEY)).isEmpty(); } |