@@ -35,6 +35,9 @@ import org.sonar.db.DbSession; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.qualityprofile.ActiveRuleCountQuery; | |||
import org.sonar.db.qualityprofile.QProfileDto; | |||
import org.sonar.server.es.SearchOptions; | |||
import org.sonar.server.rule.index.RuleIndex; | |||
import org.sonar.server.rule.index.RuleQuery; | |||
import org.sonarqube.ws.QualityProfiles.ShowWsResponse; | |||
import org.sonarqube.ws.QualityProfiles.ShowWsResponse.CompareToSonarWay; | |||
import org.sonarqube.ws.QualityProfiles.ShowWsResponse.QualityProfile; | |||
@@ -57,11 +60,13 @@ public class ShowAction implements QProfileWsAction { | |||
private final DbClient dbClient; | |||
private final QProfileWsSupport qProfileWsSupport; | |||
private final Languages languages; | |||
private final RuleIndex ruleIndex; | |||
public ShowAction(DbClient dbClient, QProfileWsSupport qProfileWsSupport, Languages languages) { | |||
public ShowAction(DbClient dbClient, QProfileWsSupport qProfileWsSupport, Languages languages, RuleIndex ruleIndex) { | |||
this.dbClient = dbClient; | |||
this.qProfileWsSupport = qProfileWsSupport; | |||
this.languages = languages; | |||
this.ruleIndex = ruleIndex; | |||
} | |||
@Override | |||
@@ -132,9 +137,15 @@ public class ShowAction implements QProfileWsAction { | |||
return null; | |||
} | |||
long missingRuleCount = ruleIndex.search( | |||
new RuleQuery().setQProfile(profile).setActivation(false).setCompareToQProfile(sonarWay), | |||
new SearchOptions().setLimit(1)) | |||
.getTotal(); | |||
return CompareToSonarWay.newBuilder() | |||
.setProfile(sonarWay.getKee()) | |||
.setProfileName(sonarWay.getName()) | |||
.setMissingRuleCount(missingRuleCount) | |||
.build(); | |||
} | |||
@@ -20,8 +20,6 @@ | |||
package org.sonar.server.rule.index; | |||
import com.google.common.base.Joiner; | |||
import com.google.common.collect.Collections2; | |||
import com.google.common.collect.ImmutableList; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
@@ -56,6 +54,7 @@ import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.qualityprofile.QProfileDto; | |||
import org.sonar.server.es.EsClient; | |||
@@ -65,6 +64,8 @@ import org.sonar.server.es.SearchOptions; | |||
import org.sonar.server.es.StickyFacetBuilder; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.lang.Boolean.FALSE; | |||
import static java.lang.Boolean.TRUE; | |||
import static java.util.Optional.ofNullable; | |||
import static org.elasticsearch.index.query.QueryBuilders.boolQuery; | |||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; | |||
@@ -114,10 +115,11 @@ public class RuleIndex { | |||
public static final String FACET_TYPES = "types"; | |||
public static final String FACET_OLD_DEFAULT = "true"; | |||
public static final List<String> ALL_STATUSES_EXCEPT_REMOVED = ImmutableList.copyOf( | |||
Collections2.filter( | |||
Collections2.transform(Arrays.asList(RuleStatus.values()), RuleStatus::toString), | |||
input -> !RuleStatus.REMOVED.toString().equals(input))); | |||
public static final List<String> ALL_STATUSES_EXCEPT_REMOVED = Arrays.stream(RuleStatus.values()) | |||
.filter(status -> !RuleStatus.REMOVED.equals(status)) | |||
.map(RuleStatus::toString) | |||
.collect(MoreCollectors.toList()); | |||
private static final String AGGREGATION_NAME = "_ref"; | |||
private static final String AGGREGATION_NAME_FOR_TAGS = "tagsAggregation"; | |||
private final EsClient client; | |||
@@ -296,29 +298,35 @@ public class RuleIndex { | |||
if (query.getActivation() != null && profile != null) { | |||
// ActiveRule Filter (profile and inheritance) | |||
BoolQueryBuilder childrenFilter = boolQuery(); | |||
addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_PROFILE_UUID, profile.getRulesProfileUuid()); | |||
addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance()); | |||
addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities()); | |||
BoolQueryBuilder activeRuleFilter = boolQuery(); | |||
addTermFilter(activeRuleFilter, FIELD_ACTIVE_RULE_PROFILE_UUID, profile.getRulesProfileUuid()); | |||
addTermFilter(activeRuleFilter, FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance()); | |||
addTermFilter(activeRuleFilter, FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities()); | |||
// ChildQuery | |||
QueryBuilder childQuery; | |||
if (childrenFilter.hasClauses()) { | |||
childQuery = childrenFilter; | |||
if (activeRuleFilter.hasClauses()) { | |||
childQuery = activeRuleFilter; | |||
} else { | |||
childQuery = matchAllQuery(); | |||
} | |||
if (Boolean.TRUE.equals(query.getActivation())) { | |||
if (TRUE.equals(query.getActivation())) { | |||
filters.put("activation", | |||
QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(), | |||
childQuery)); | |||
} else if (Boolean.FALSE.equals(query.getActivation())) { | |||
} else if (FALSE.equals(query.getActivation())) { | |||
filters.put("activation", | |||
boolQuery().mustNot( | |||
QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(), | |||
childQuery))); | |||
} | |||
QProfileDto compareToQProfile = query.getCompareToQProfile(); | |||
if (compareToQProfile != null) { | |||
filters.put("comparison", | |||
QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(), | |||
boolQuery().must(QueryBuilders.termQuery(FIELD_ACTIVE_RULE_PROFILE_UUID, compareToQProfile.getRulesProfileUuid())))); | |||
} | |||
} | |||
return filters; |
@@ -43,6 +43,7 @@ public class RuleQuery { | |||
private Collection<RuleType> types; | |||
private Boolean activation; | |||
private QProfileDto profile; | |||
private QProfileDto compareToQProfile; | |||
private Collection<String> inheritance; | |||
private Collection<String> activeSeverities; | |||
private String templateKey; | |||
@@ -272,4 +273,14 @@ public class RuleQuery { | |||
this.organization = o; | |||
return this; | |||
} | |||
@CheckForNull | |||
public QProfileDto getCompareToQProfile() { | |||
return compareToQProfile; | |||
} | |||
public RuleQuery setCompareToQProfile(@Nullable QProfileDto compareToQProfile) { | |||
this.compareToQProfile = compareToQProfile; | |||
return this; | |||
} | |||
} |
@@ -12,10 +12,5 @@ | |||
"projectCount": 7, | |||
"rulesUpdatedAt": "2016-12-22T19:10:03+0100", | |||
"lastUsed": "2016-12-01T19:10:03+0100" | |||
}, | |||
"compareToSonarWay": { | |||
"profile": "iU5OvuD2FLz", | |||
"profileName": "Sonar way", | |||
"missingRuleCount": 4 | |||
} | |||
} |
@@ -23,14 +23,22 @@ package org.sonar.server.qualityprofile.ws; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.config.MapSettings; | |||
import org.sonar.api.resources.Language; | |||
import org.sonar.api.resources.Languages; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.qualityprofile.QProfileDto; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import org.sonar.server.es.EsTester; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.organization.TestDefaultOrganizationProvider; | |||
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; | |||
import org.sonar.server.qualityprofile.index.ActiveRuleIteratorFactory; | |||
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.tester.UserSessionRule; | |||
import org.sonar.server.ws.TestRequest; | |||
import org.sonar.server.ws.WsActionTester; | |||
@@ -54,6 +62,8 @@ public class ShowActionTest { | |||
private static Language XOO2 = newLanguage("xoo2"); | |||
private static Languages LANGUAGES = new Languages(XOO1, XOO2); | |||
@Rule | |||
public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings())); | |||
@Rule | |||
public DbTester db = DbTester.create(); | |||
@Rule | |||
@@ -61,8 +71,12 @@ public class ShowActionTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient()); | |||
private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client(), new ActiveRuleIteratorFactory(db.getDbClient())); | |||
private RuleIndex ruleIndex = new RuleIndex(es.client()); | |||
private WsActionTester ws = new WsActionTester( | |||
new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), LANGUAGES)); | |||
new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), LANGUAGES, ruleIndex)); | |||
@Test | |||
public void test_definition() { | |||
@@ -161,6 +175,41 @@ public class ShowActionTest { | |||
public void compare_to_sonar_way_profile() { | |||
QProfileDto sonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey())); | |||
QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); | |||
RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
RuleDefinitionDto sonarWayRule1 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
RuleDefinitionDto sonarWayRule2 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
RuleDefinitionDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
RuleDefinitionDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
RuleDefinitionDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
db.qualityProfiles().activateRule(profile, commonRule); | |||
db.qualityProfiles().activateRule(profile, profileRule1); | |||
db.qualityProfiles().activateRule(profile, profileRule2); | |||
db.qualityProfiles().activateRule(profile, profileRule3); | |||
db.qualityProfiles().activateRule(sonarWayProfile, commonRule); | |||
db.qualityProfiles().activateRule(sonarWayProfile, sonarWayRule1); | |||
db.qualityProfiles().activateRule(sonarWayProfile, sonarWayRule2); | |||
ruleIndexer.indexOnStartup(ruleIndexer.getIndexTypes()); | |||
activeRuleIndexer.indexOnStartup(activeRuleIndexer.getIndexTypes()); | |||
CompareToSonarWay result = call(ws.newRequest() | |||
.setParam(PARAM_PROFILE, profile.getKee()) | |||
.setParam(PARAM_COMPARE_TO_SONAR_WAY, "true")) | |||
.getCompareToSonarWay(); | |||
assertThat(result) | |||
.extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName, CompareToSonarWay::getMissingRuleCount) | |||
.containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName(), 2L); | |||
} | |||
@Test | |||
public void compare_to_sonar_way_profile_when_same_active_rules() { | |||
QProfileDto sonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey())); | |||
QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); | |||
RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); | |||
db.qualityProfiles().activateRule(profile, commonRule); | |||
db.qualityProfiles().activateRule(sonarWayProfile, commonRule); | |||
ruleIndexer.indexOnStartup(ruleIndexer.getIndexTypes()); | |||
activeRuleIndexer.indexOnStartup(activeRuleIndexer.getIndexTypes()); | |||
CompareToSonarWay result = call(ws.newRequest() | |||
.setParam(PARAM_PROFILE, profile.getKee()) | |||
@@ -168,13 +217,14 @@ public class ShowActionTest { | |||
.getCompareToSonarWay(); | |||
assertThat(result) | |||
.extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName) | |||
.containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName()); | |||
.extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName, CompareToSonarWay::getMissingRuleCount) | |||
.containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName(), 0L); | |||
} | |||
@Test | |||
public void no_comparison_when_sonar_way_does_not_exist() { | |||
QProfileDto anotherSonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setIsBuiltIn(true).setName("Another Sonar way").setLanguage(XOO1.getKey())); | |||
QProfileDto anotherSonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), | |||
p -> p.setIsBuiltIn(true).setName("Another Sonar way").setLanguage(XOO1.getKey())); | |||
QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); | |||
ShowWsResponse result = call(ws.newRequest() | |||
@@ -228,7 +278,7 @@ public class ShowActionTest { | |||
CompareToSonarWay result = call(ws.newRequest() | |||
.setParam(PARAM_PROFILE, profile.getKee()) | |||
.setParam(PARAM_COMPARE_TO_SONAR_WAY, "true")) | |||
.getCompareToSonarWay(); | |||
.getCompareToSonarWay(); | |||
assertThat(result) | |||
.extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName) | |||
@@ -244,7 +294,7 @@ public class ShowActionTest { | |||
CompareToSonarWay result = call(ws.newRequest() | |||
.setParam(PARAM_PROFILE, profile.getKee()) | |||
.setParam(PARAM_COMPARE_TO_SONAR_WAY, "true")) | |||
.getCompareToSonarWay(); | |||
.getCompareToSonarWay(); | |||
assertThat(result) | |||
.extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName) | |||
@@ -292,10 +342,11 @@ public class ShowActionTest { | |||
.mapToObj(i -> db.components().insertPrivateProject()) | |||
.forEach(project -> db.qualityProfiles().associateWithProject(project, profile)); | |||
ws = new WsActionTester(new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), new Languages(cs))); | |||
ws = new WsActionTester( | |||
new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), new Languages(cs), ruleIndex)); | |||
String result = ws.newRequest().setParam(PARAM_PROFILE, profile.getKee()).execute().getInput(); | |||
assertJson(result).ignoreFields("rulesUpdatedAt", "lastUsed", "userUpdatedAt", "compareToSonarWay").isSimilarTo(ws.getDef().responseExampleAsString()); | |||
assertJson(result).ignoreFields("rulesUpdatedAt", "lastUsed", "userUpdatedAt").isSimilarTo(ws.getDef().responseExampleAsString()); | |||
} | |||
private ShowWsResponse call(TestRequest request) { |
@@ -383,6 +383,32 @@ public class RuleIndexTest { | |||
assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void compare_to_another_profile() { | |||
String xoo = "xoo"; | |||
QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo)); | |||
QProfileDto anotherProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo)); | |||
RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); | |||
RuleDefinitionDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); | |||
RuleDefinitionDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); | |||
RuleDefinitionDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); | |||
RuleDefinitionDto anotherProfileRule1 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); | |||
RuleDefinitionDto anotherProfileRule2 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); | |||
db.qualityProfiles().activateRule(profile, commonRule); | |||
db.qualityProfiles().activateRule(profile, profileRule1); | |||
db.qualityProfiles().activateRule(profile, profileRule2); | |||
db.qualityProfiles().activateRule(profile, profileRule3); | |||
db.qualityProfiles().activateRule(anotherProfile, commonRule); | |||
db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule1); | |||
db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2); | |||
index(); | |||
verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1, anotherProfileRule2); | |||
verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(anotherProfile), commonRule); | |||
verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1, profileRule2, profileRule3); | |||
verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(profile)); | |||
} | |||
@SafeVarargs | |||
private final RuleDefinitionDto createRule(Consumer<RuleDefinitionDto>... consumers) { | |||
return db.rules().insert(consumers); |