Explorar el Código

SONAR-12736 Fix loading of QProfile using deprecated rule keys

tags/8.1.0.31237
Julien Lancelot hace 4 años
padre
commit
0dcc152f68

+ 24
- 11
server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java Ver fichero

@@ -22,10 +22,15 @@ package org.sonar.server.qualityprofile;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;

import static org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule;
import static org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.OverriddenParam;

/**
* Represent a Quality Profile as computed from {@link BuiltInQualityProfilesDefinition} provided by installed plugins.
*/
@@ -63,11 +68,19 @@ public final class BuiltInQProfile {

static final class ActiveRule {
private final int ruleId;
private final BuiltInQualityProfilesDefinition.BuiltInActiveRule builtIn;
private final RuleKey ruleKey;
private final String severity;
private final List<OverriddenParam> params;

ActiveRule(int ruleId, BuiltInActiveRule builtIn) {
this(ruleId, RuleKey.of(builtIn.repoKey(), builtIn.ruleKey()), builtIn.overriddenSeverity(), builtIn.overriddenParams());
}

ActiveRule(int ruleId, BuiltInQualityProfilesDefinition.BuiltInActiveRule builtIn) {
ActiveRule(int ruleId, RuleKey ruleKey, @Nullable String severity, List<OverriddenParam> params) {
this.ruleId = ruleId;
this.builtIn = builtIn;
this.ruleKey = ruleKey;
this.severity = severity;
this.params = params;
}

public int getRuleId() {
@@ -75,11 +88,16 @@ public final class BuiltInQProfile {
}

public RuleKey getRuleKey() {
return RuleKey.of(builtIn.repoKey(), builtIn.ruleKey());
return ruleKey;
}

@CheckForNull
public String getSeverity() {
return severity;
}

public BuiltInQualityProfilesDefinition.BuiltInActiveRule getBuiltIn() {
return builtIn;
public List<OverriddenParam> getParams() {
return params;
}
}

@@ -118,11 +136,6 @@ public final class BuiltInQProfile {
return this;
}

Builder addRule(BuiltInQualityProfilesDefinition.BuiltInActiveRule rule, int ruleId) {
this.activeRules.add(new ActiveRule(ruleId, rule));
return this;
}

Builder addRule(ActiveRule activeRule) {
this.activeRules.add(activeRule);
return this;

+ 2
- 2
server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java Ver fichero

@@ -148,7 +148,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
dto.setProfileId(rulesProfileDto.getId());
dto.setRuleId(ruleDefinitionDto.getId());
dto.setKey(ActiveRuleKey.of(rulesProfileDto, ruleDefinitionDto.getKey()));
dto.setSeverity(firstNonNull(activeRule.getBuiltIn().overriddenSeverity(), ruleDefinitionDto.getSeverityString()));
dto.setSeverity(firstNonNull(activeRule.getSeverity(), ruleDefinitionDto.getSeverityString()));
dto.setUpdatedAt(now);
dto.setCreatedAt(now);
dbClient.activeRuleDao().insert(dbSession, dto);
@@ -163,7 +163,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {

private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, BuiltInQProfile.ActiveRule activeRule,
ActiveRuleDto activeRuleDto) {
Map<String, String> valuesByParamKey = activeRule.getBuiltIn().overriddenParams()
Map<String, String> valuesByParamKey = activeRule.getParams()
.stream()
.collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue));
return ruleRepository.getRuleParams(activeRule.getRuleKey())

+ 31
- 18
server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java Ver fichero

@@ -20,9 +20,11 @@
package org.sonar.server.qualityprofile;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -42,6 +44,7 @@ import org.sonar.api.utils.log.Profiler;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDefinitionDto;

import static com.google.common.base.Preconditions.checkState;
@@ -98,7 +101,7 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository
.collect(Collectors.toSet());

checkState(languagesWithoutBuiltInQProfiles.isEmpty(), "The following languages have no built-in quality profiles: %s",
languagesWithoutBuiltInQProfiles.stream().collect(Collectors.joining()));
String.join("", languagesWithoutBuiltInQProfiles));
}

private Map<String, Map<String, BuiltInQualityProfile>> validateAndClean(BuiltInQualityProfilesDefinition.Context context) {
@@ -120,24 +123,33 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository
if (rulesProfilesByLanguage.isEmpty()) {
return Collections.emptyList();
}
Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = loadRuleDefinitionsByRuleKey();
Map<String, List<BuiltInQProfile.Builder>> buildersByLanguage = rulesProfilesByLanguage
.entrySet()
.stream()
.collect(MoreCollectors.uniqueIndex(
Map.Entry::getKey,
rulesProfilesByLanguageAndName -> toQualityProfileBuilders(rulesProfilesByLanguageAndName, rulesByRuleKey)));
return buildersByLanguage
.entrySet()
.stream()
.filter(BuiltInQProfileRepositoryImpl::ensureAtMostOneDeclaredDefault)
.map(entry -> toQualityProfiles(entry.getValue()))
.flatMap(Collection::stream)
.collect(MoreCollectors.toList());
}

private Map<RuleKey, RuleDefinitionDto> loadRuleDefinitionsByRuleKey() {
try (DbSession dbSession = dbClient.openSession(false)) {
Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = dbClient.ruleDao().selectAllDefinitions(dbSession)
.stream()
.collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getKey));
Map<String, List<BuiltInQProfile.Builder>> buildersByLanguage = rulesProfilesByLanguage
.entrySet()
.stream()
.collect(MoreCollectors.uniqueIndex(
Map.Entry::getKey,
rulesProfilesByLanguageAndName -> toQualityProfileBuilders(rulesProfilesByLanguageAndName, rulesByRuleKey)));
return buildersByLanguage
.entrySet()
.stream()
.filter(BuiltInQProfileRepositoryImpl::ensureAtMostOneDeclaredDefault)
.map(entry -> toQualityProfiles(entry.getValue()))
.flatMap(Collection::stream)
.collect(MoreCollectors.toList());
List<RuleDefinitionDto> ruleDefinitions = dbClient.ruleDao().selectAllDefinitions(dbSession);
Multimap<Integer, DeprecatedRuleKeyDto> deprecatedRuleKeysByRuleId = dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbSession).stream()
.collect(MoreCollectors.index(DeprecatedRuleKeyDto::getRuleId));
Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = new HashMap<>();
for (RuleDefinitionDto ruleDefinition : ruleDefinitions) {
rulesByRuleKey.put(ruleDefinition.getKey(), ruleDefinition);
deprecatedRuleKeysByRuleId.get(ruleDefinition.getId()).forEach(t -> rulesByRuleKey.put(RuleKey.of(t.getOldRepositoryKey(), t.getOldRuleKey()), ruleDefinition));
}
return rulesByRuleKey;
}
}

@@ -186,7 +198,8 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository
RuleKey ruleKey = RuleKey.of(builtInActiveRule.repoKey(), builtInActiveRule.ruleKey());
RuleDefinitionDto ruleDefinition = rulesByRuleKey.get(ruleKey);
checkState(ruleDefinition != null, "Rule with key '%s' not found", ruleKey);
builder.addRule(builtInActiveRule, ruleDefinition.getId());
builder.addRule(new BuiltInQProfile.ActiveRule(ruleDefinition.getId(), ruleDefinition.getKey(),
builtInActiveRule.overriddenSeverity(), builtInActiveRule.overriddenParams()));
});
return builder;
}

+ 2
- 2
server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java Ver fichero

@@ -81,9 +81,9 @@ public class BuiltInQProfileUpdateImpl implements BuiltInQProfileUpdate {
}

private static RuleActivation convert(BuiltInQProfile.ActiveRule ar) {
Map<String, String> params = ar.getBuiltIn().overriddenParams().stream()
Map<String, String> params = ar.getParams().stream()
.collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue));
return RuleActivation.create(ar.getRuleId(), ar.getBuiltIn().overriddenSeverity(), params);
return RuleActivation.create(ar.getRuleId(), ar.getSeverity(), params);
}

}

+ 125
- 59
server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java Ver fichero

@@ -20,23 +20,27 @@
package org.sonar.server.qualityprofile;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.language.LanguageTesting;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static org.mockito.Mockito.mock;
import static org.sonar.db.rule.RuleTesting.EXTERNAL_XOO;
import static org.sonar.server.qualityprofile.BuiltInQProfile.ActiveRule;

public class BuiltInQProfileRepositoryImplTest {
private static final Language FOO_LANGUAGE = LanguageTesting.newLanguage("foo", "foo", "foo");
@@ -45,52 +49,34 @@ public class BuiltInQProfileRepositoryImplTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
public DbTester db = DbTester.create();

private DbClient dbClient = dbTester.getDbClient();
private DbClient dbClient = db.getDbClient();

@Test
public void get_throws_ISE_if_called_before_initialize() {
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages());

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("initialize must be called first");

underTest.get();
}

@Test
public void initialize_throws_ISE_if_called_twice() {
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages());
underTest.initialize();

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("initialize must be called only once");

underTest.initialize();
}

@Test
public void initialize_throws_ISE_if_language_has_no_builtin_qp() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(FOO_LANGUAGE));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("The following languages have no built-in quality profiles: foo");
underTest.initialize();
}

@Test
public void initialize_creates_no_BuiltInQProfile_when_all_definitions_apply_to_non_defined_languages() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(), new DummyProfileDefinition("foo", "P1", false));
public void create_qprofile_with_rule() {
RuleDefinitionDto rule1 = db.rules().insert();
RuleDefinitionDto rule2 = db.rules().insert();
RuleDefinitionDto ruleNotToBeActivated = db.rules().insert();
List<DummyProfileDefinition> definitions = singletonList(new DummyProfileDefinition("foo", "foo", false,
asList(rule1.getKey(), rule2.getKey())));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));

underTest.initialize();

assertThat(underTest.get()).isEmpty();
assertThat(underTest.get())
.extracting(BuiltInQProfile::getName)
.containsExactlyInAnyOrder("foo");
assertThat(underTest.get().get(0).getActiveRules())
.extracting(ActiveRule::getRuleId, ActiveRule::getRuleKey)
.containsExactlyInAnyOrder(
tuple(rule1.getId(), rule1.getKey()),
tuple(rule2.getId(), rule2.getKey())
);
}

@Test
public void initialize_makes_single_profile_of_a_language_default_even_if_not_flagged_as_so() {
public void make_single_profile_of_a_language_default_even_if_not_flagged_as_so() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), new DummyProfileDefinition("foo", "foo1", false));

underTest.initialize();
@@ -101,7 +87,7 @@ public class BuiltInQProfileRepositoryImplTest {
}

@Test
public void initialize_makes_single_profile_of_a_language_default_even_if_flagged_as_so() {
public void make_single_profile_of_a_language_default_even_if_flagged_as_so() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), new DummyProfileDefinition("foo", "foo1", true));

underTest.initialize();
@@ -112,10 +98,9 @@ public class BuiltInQProfileRepositoryImplTest {
}

@Test
public void initialize_makes_first_profile_of_a_language_default_when_none_flagged_as_so() {
public void make_first_profile_of_a_language_default_when_none_flagged_as_so() {
List<DummyProfileDefinition> definitions = new ArrayList<>(
asList(new DummyProfileDefinition("foo", "foo1", false), new DummyProfileDefinition("foo", "foo2", false)));
Collections.shuffle(definitions);
String firstName = definitions.get(0).getName();
String secondName = definitions.get(1).getName();
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));
@@ -128,18 +113,7 @@ public class BuiltInQProfileRepositoryImplTest {
}

@Test
public void initialize_fails_with_ISE_when_two_profiles_with_different_name_are_default_for_the_same_language() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "foo1", true), new DummyProfileDefinition("foo", "foo2", true));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Several Quality profiles are flagged as default for the language foo: [foo1, foo2]");

underTest.initialize();
}

@Test
public void initialize_creates_profile_Sonar_Way_as_default_if_none_other_is_defined_default_for_a_given_language() {
public void create_profile_Sonar_Way_as_default_if_none_other_is_defined_default_for_a_given_language() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(
dbClient, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "doh", false), new DummyProfileDefinition("foo", "boo", false),
@@ -155,7 +129,7 @@ public class BuiltInQProfileRepositoryImplTest {
}

@Test
public void initialize_does_not_create_Sonar_Way_as_default_if_other_profile_is_defined_as_default() {
public void do_not_create_Sonar_Way_as_default_if_other_profile_is_defined_as_default() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(
dbClient, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", SONAR_WAY_QP_NAME, false), new DummyProfileDefinition("foo", "goo", true));
@@ -170,7 +144,7 @@ public class BuiltInQProfileRepositoryImplTest {
}

@Test
public void initialize_matches_Sonar_Way_default_with_case_sensitivity() {
public void match_Sonar_Way_default_with_case_sensitivity() {
String sonarWayInOtherCase = SONAR_WAY_QP_NAME.toUpperCase();
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(
dbClient, new Languages(FOO_LANGUAGE),
@@ -185,21 +159,113 @@ public class BuiltInQProfileRepositoryImplTest {
.containsExactly("goo");
}

@Test
public void create_no_BuiltInQProfile_when_all_definitions_apply_to_non_defined_languages() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(), new DummyProfileDefinition("foo", "P1", false));

underTest.initialize();

assertThat(underTest.get()).isEmpty();
}

@Test
public void create_qprofile_with_deprecated_rule() {
RuleDefinitionDto rule1 = db.rules().insert();
db.rules().insertDeprecatedKey(d -> d.setRuleId(rule1.getId()).setOldRepositoryKey("oldRepo").setOldRuleKey("oldKey"));
RuleDefinitionDto rule2 = db.rules().insert();
List<DummyProfileDefinition> definitions = singletonList(new DummyProfileDefinition("foo", "foo", false,
asList(RuleKey.of("oldRepo", "oldKey"), rule2.getKey())));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));

underTest.initialize();

assertThat(underTest.get())
.extracting(BuiltInQProfile::getName)
.containsExactlyInAnyOrder("foo");
assertThat(underTest.get().get(0).getActiveRules())
.extracting(ActiveRule::getRuleId, ActiveRule::getRuleKey)
.containsExactlyInAnyOrder(
tuple(rule1.getId(), rule1.getKey()),
tuple(rule2.getId(), rule2.getKey())
);
}

@Test
public void fail_with_ISE_when_rule_does_not_exist() {
List<DummyProfileDefinition> definitions = singletonList(new DummyProfileDefinition("foo", "foo", false, singletonList(EXTERNAL_XOO)));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(String.format("Rule with key '%s' not found", EXTERNAL_XOO.toString()));

underTest.initialize();
}

@Test
public void fail_with_ISE_when_two_profiles_with_different_name_are_default_for_the_same_language() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "foo1", true), new DummyProfileDefinition("foo", "foo2", true));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Several Quality profiles are flagged as default for the language foo: [foo1, foo2]");

underTest.initialize();
}

@Test
public void get_throws_ISE_if_called_before_initialize() {
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages());

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("initialize must be called first");

underTest.get();
}

@Test
public void initialize_throws_ISE_if_called_twice() {
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages());
underTest.initialize();

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("initialize must be called only once");

underTest.initialize();
}

@Test
public void initialize_throws_ISE_if_language_has_no_builtin_qp() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(FOO_LANGUAGE));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("The following languages have no built-in quality profiles: foo");

underTest.initialize();
}

private static final class DummyProfileDefinition implements BuiltInQualityProfilesDefinition {
private final String language;
private final String name;
private final boolean defaultProfile;
private final List<RuleKey> activeRuleKeys;

private DummyProfileDefinition(String language, String name, boolean defaultProfile) {
private DummyProfileDefinition(String language, String name, boolean defaultProfile, List<RuleKey> activeRuleKeys) {
this.language = language;
this.name = name;
this.defaultProfile = defaultProfile;
this.activeRuleKeys = activeRuleKeys;
}

private DummyProfileDefinition(String language, String name, boolean defaultProfile) {
this(language, name, defaultProfile, emptyList());
}

@Override
public void define(Context context) {
context.createBuiltInQualityProfile(name, language)
.setDefault(defaultProfile).done();
NewBuiltInQualityProfile builtInQualityProfile = context.createBuiltInQualityProfile(name, language);
activeRuleKeys.stream().forEach(activeRuleKey -> builtInQualityProfile.activateRule(activeRuleKey.repository(), activeRuleKey.rule()));
builtInQualityProfile.setDefault(defaultProfile);
builtInQualityProfile.done();
}

String getName() {

+ 1
- 1
server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java Ver fichero

@@ -95,7 +95,7 @@ public class BuiltInQProfileRepositoryRule extends ExternalResource implements B
RuleKey ruleKey = RuleKey.of(rule.repoKey(), rule.ruleKey());
RuleDefinitionDto ruleDefinition = rulesByRuleKey.get(ruleKey);
Preconditions.checkState(ruleDefinition != null, "Rule '%s' not found", ruleKey);
builder.addRule(rule, ruleDefinition.getId());
builder.addRule(new BuiltInQProfile.ActiveRule(ruleDefinition.getId(), rule));
});
return builder
.build();

Cargando…
Cancelar
Guardar