From 1daf83302783077e02b624ce2f06bdde8a89a343 Mon Sep 17 00:00:00 2001 From: Eric Hartmann Date: Thu, 1 Feb 2018 10:07:09 +0100 Subject: [PATCH] SONAR-10311 Store deprecated keys of rule at startup --- .../java/org/sonar/db/version/SqTables.java | 1 + .../sonar/db/rule/DeprecatedRuleKeyDto.java | 141 +++++++++++++ .../main/java/org/sonar/db/rule/RuleDao.java | 16 ++ .../java/org/sonar/db/rule/RuleMapper.java | 7 + .../org/sonar/db/rule/RuleMapper.xml | 40 ++++ .../java/org/sonar/db/rule/RuleDaoTest.java | 107 +++++++++- .../java/org/sonar/db/rule/RuleDbTester.java | 20 +- .../java/org/sonar/db/rule/RuleTesting.java | 13 ++ .../org/sonar/server/rule/RegisterRules.java | 103 ++++++--- .../server/rule/SingleDeprecatedRuleKey.java | 150 +++++++++++++ .../sonar/server/rule/RegisterRulesTest.java | 199 ++++++++++++------ .../rule/SingleDeprecatedRuleKeyTest.java | 129 ++++++++++++ 12 files changed, 826 insertions(+), 100 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/rule/DeprecatedRuleKeyDto.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/rule/SingleDeprecatedRuleKey.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/rule/SingleDeprecatedRuleKeyTest.java diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index 37755361fa7..1843f327d76 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -59,6 +59,7 @@ public final class SqTables { "ce_task_input", "ce_scanner_context", "default_qprofiles", + "deprecated_rule_keys", "duplications_index", "es_queue", "events", diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/DeprecatedRuleKeyDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/DeprecatedRuleKeyDto.java new file mode 100644 index 00000000000..e18c6fdeece --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/DeprecatedRuleKeyDto.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.rule; + +import java.util.Objects; +import javax.annotation.CheckForNull; + +/** + * Map the table "deprecated_rule_keys" + */ +public class DeprecatedRuleKeyDto { + /** + * Uuid of the deprecated key + */ + private String uuid; + /** + * the id of the current rule for this deprecated key + */ + private Integer ruleId; + /** + * repository key that was deprecated + */ + private String oldRepositoryKey; + /** + * rule key that was deprecated, not nullable + */ + private String oldRuleKey; + /** + * creation date of the row + */ + private Long createdAt; + + /** + * current repository key retrieved from an external join on rule_id + */ + private String newRepositoryKey; + /** + * current rule key retrieved from an external join on rule_id + */ + private String newRuleKey; + + public String getUuid() { + return uuid; + } + + public DeprecatedRuleKeyDto setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public Integer getRuleId() { + return ruleId; + } + + public DeprecatedRuleKeyDto setRuleId(Integer ruleId) { + this.ruleId = ruleId; + return this; + } + + public String getOldRepositoryKey() { + return oldRepositoryKey; + } + + public DeprecatedRuleKeyDto setOldRepositoryKey(String oldRepositoryKey) { + this.oldRepositoryKey = oldRepositoryKey; + return this; + } + + public String getOldRuleKey() { + return oldRuleKey; + } + + public DeprecatedRuleKeyDto setOldRuleKey(String oldRuleKey) { + this.oldRuleKey = oldRuleKey; + return this; + } + + /** + * This value may be null if the rule has been deleted + * + * @return the current repository key + */ + @CheckForNull + public String getNewRepositoryKey() { + return newRepositoryKey; + } + + /** + * This value may be null if the rule has been deleted + * + * @return the current rule key + */ + @CheckForNull + public String getNewRuleKey() { + return newRuleKey; + } + + public long getCreatedAt() { + return createdAt; + } + + public DeprecatedRuleKeyDto setCreatedAt(long createdAt) { + this.createdAt = createdAt; + return this; + } + + @Override + public int hashCode() { + return Objects.hash(uuid); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final DeprecatedRuleKeyDto other = (DeprecatedRuleKeyDto) obj; + return Objects.equals(this.uuid, other.uuid); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java index 96d540e8579..3ee2b77d40a 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java @@ -22,6 +22,7 @@ package org.sonar.db.rule; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; import javax.annotation.Nullable; import org.apache.ibatis.session.ResultHandler; @@ -38,6 +39,7 @@ import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static org.sonar.db.DatabaseUtils.executeLargeInputs; import static org.sonar.db.DatabaseUtils.executeLargeInputsWithoutOutput; +import static org.sonar.db.DatabaseUtils.executeLargeUpdates; public class RuleDao implements Dao { @@ -238,4 +240,18 @@ public class RuleDao implements Dao { mapper(session).deleteParameter(ruleParameterId); } + public Set selectAllDeprecatedRuleKeys(DbSession session) { + return mapper(session).selectAllDeprecatedRuleKeys(); + } + + public void deleteDeprecatedRuleKeys(DbSession dbSession, Collection uuids) { + if (uuids.isEmpty()) { + return; + } + executeLargeUpdates(uuids, mapper(dbSession)::deleteDeprecatedRuleKeys); + } + + public void insert(DbSession dbSession, DeprecatedRuleKeyDto deprecatedRuleKey) { + mapper(dbSession).insertDeprecatedRuleKey(deprecatedRuleKey); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java index 09a7c498169..f3c689e33a4 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java @@ -20,6 +20,7 @@ package org.sonar.db.rule; import java.util.List; +import java.util.Set; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.session.ResultHandler; import org.sonar.db.es.RuleExtensionId; @@ -83,4 +84,10 @@ public interface RuleMapper { void updateParameter(RuleParamDto param); void deleteParameter(Integer paramId); + + Set selectAllDeprecatedRuleKeys(); + + void deleteDeprecatedRuleKeys(@Param("uuids") List uuids); + + void insertDeprecatedRuleKey(DeprecatedRuleKeyDto deprecatedRuleKeyDto); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml index 6cfde04506c..37ed0bf0628 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml @@ -497,5 +497,45 @@ where id=#{id,jdbcType=INTEGER} + + + + + DELETE FROM + deprecated_rule_keys + WHERE + + uuid=#{uuid,jdbcType=INTEGER} + + + + + INSERT INTO deprecated_rule_keys ( + uuid, + rule_id, + old_repository_key, + old_rule_key, + created_at + ) + values ( + #{uuid,jdbcType=VARCHAR}, + #{ruleId,jdbcType=INTEGER}, + #{oldRepositoryKey,jdbcType=VARCHAR}, + #{oldRuleKey,jdbcType=VARCHAR}, + #{createdAt,jdbcType=BIGINT} + ) + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java index 0cd2d997ee9..dd698f8eca3 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java @@ -25,7 +25,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; +import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.session.ResultHandler; import org.junit.Before; import org.junit.Rule; @@ -48,7 +50,9 @@ import org.sonar.db.rule.RuleDto.Scope; import static com.google.common.collect.Sets.newHashSet; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @@ -747,7 +751,7 @@ public class RuleDaoTest { @Test public void scrollIndexingRulesByKeys_scrolls_nothing_if_key_does_not_exist() { Accumulator accumulator = new Accumulator<>(); - RuleDefinitionDto r1 = db.rules().insert(); + db.rules().insert(); underTest.scrollIndexingRulesByKeys(db.getSession(), singletonList(RuleKey.of("does", "not exist")), accumulator); @@ -778,7 +782,7 @@ public class RuleDaoTest { RuleMetadataDto r1Extension = db.rules().insertOrUpdateMetadata(r1, organization, r -> r.setTagsField("t1,t2")); RuleExtensionId r1ExtensionId = new RuleExtensionId(organization.getUuid(), r1.getRepositoryKey(), r1.getRuleKey()); RuleDefinitionDto r2 = db.rules().insert(); - RuleMetadataDto r2Extension = db.rules().insertOrUpdateMetadata(r2, organization, r -> r.setTagsField("t1,t3")); + db.rules().insertOrUpdateMetadata(r2, organization, r -> r.setTagsField("t1,t3")); underTest.scrollIndexingRuleExtensionsByIds(db.getSession(), singletonList(r1ExtensionId), accumulator); @@ -788,7 +792,104 @@ public class RuleDaoTest { tuple(r1.getKey(), organization.getUuid(), r1Extension.getTagsAsString())); } - private static class Accumulator implements Consumer { + @Test + public void selectAllDeprecatedRuleKeys() { + RuleDefinitionDto r1 = db.rules().insert(); + RuleDefinitionDto r2 = db.rules().insert(); + + db.rules().insertDeprecatedKey(r -> r.setRuleId(r1.getId())); + db.rules().insertDeprecatedKey(r -> r.setRuleId(r2.getId())); + + db.getSession().commit(); + + Set deprecatedRuleKeyDtos = underTest.selectAllDeprecatedRuleKeys(db.getSession()); + assertThat(deprecatedRuleKeyDtos).hasSize(2); + } + + @Test + public void selectAllDeprecatedRuleKeys_return_values_even_if_there_is_no_rule() { + db.rules().insertDeprecatedKey(); + db.rules().insertDeprecatedKey(); + + Set deprecatedRuleKeyDtos = underTest.selectAllDeprecatedRuleKeys(db.getSession()); + assertThat(deprecatedRuleKeyDtos).hasSize(2); + assertThat(deprecatedRuleKeyDtos) + .extracting(DeprecatedRuleKeyDto::getNewRepositoryKey, DeprecatedRuleKeyDto::getNewRuleKey) + .containsExactly( + tuple(null, null), + tuple(null, null) + ); + } + + @Test + public void deleteDeprecatedRuleKeys_with_empty_list_has_no_effect() { + db.rules().insertDeprecatedKey(); + db.rules().insertDeprecatedKey(); + + assertThat(underTest.selectAllDeprecatedRuleKeys(db.getSession())).hasSize(2); + + underTest.deleteDeprecatedRuleKeys(db.getSession(), emptyList()); + + assertThat(underTest.selectAllDeprecatedRuleKeys(db.getSession())).hasSize(2); + } + + @Test + public void deleteDeprecatedRuleKeys_with_non_existing_uuid_has_no_effect() { + db.rules().insertDeprecatedKey(d -> d.setUuid("A1")); + db.rules().insertDeprecatedKey(d -> d.setUuid("A2")); + + assertThat(underTest.selectAllDeprecatedRuleKeys(db.getSession())).hasSize(2); + + underTest.deleteDeprecatedRuleKeys(db.getSession(), asList("B1", "B2")); + + assertThat(underTest.selectAllDeprecatedRuleKeys(db.getSession())).hasSize(2); + } + + @Test + public void deleteDeprecatedRuleKeys() { + DeprecatedRuleKeyDto deprecatedRuleKeyDto1 = db.rules().insertDeprecatedKey(); + db.rules().insertDeprecatedKey();; + + assertThat(underTest.selectAllDeprecatedRuleKeys(db.getSession())).hasSize(2); + + underTest.deleteDeprecatedRuleKeys(db.getSession(), singletonList(deprecatedRuleKeyDto1.getUuid())); + assertThat(underTest.selectAllDeprecatedRuleKeys(db.getSession())).hasSize(1); + } + + @Test + public void insertDeprecatedRuleKey() { + RuleDefinitionDto r1 = db.rules().insert(); + DeprecatedRuleKeyDto deprecatedRuleKeyDto = db.rules().insertDeprecatedKey(d -> d.setRuleId(r1.getId())); + + db.getSession().commit(); + + Set deprecatedRuleKeyDtos = underTest.selectAllDeprecatedRuleKeys(db.getSession()); + assertThat(deprecatedRuleKeyDtos).hasSize(1); + + DeprecatedRuleKeyDto deprecatedRuleKeyDto1 = deprecatedRuleKeyDtos.iterator().next(); + assertThat(deprecatedRuleKeyDto1.getOldRepositoryKey()).isEqualTo(deprecatedRuleKeyDto.getOldRepositoryKey()); + assertThat(deprecatedRuleKeyDto1.getOldRuleKey()).isEqualTo(deprecatedRuleKeyDto.getOldRuleKey()); + assertThat(deprecatedRuleKeyDto1.getNewRepositoryKey()).isEqualTo(r1.getRepositoryKey()); + assertThat(deprecatedRuleKeyDto1.getNewRuleKey()).isEqualTo(r1.getRuleKey()); + assertThat(deprecatedRuleKeyDto1.getUuid()).isEqualTo(deprecatedRuleKeyDto.getUuid()); + assertThat(deprecatedRuleKeyDto1.getCreatedAt()).isEqualTo(deprecatedRuleKeyDto.getCreatedAt()); + assertThat(deprecatedRuleKeyDto1.getRuleId()).isEqualTo(r1.getId()); + } + + @Test + public void insertDeprecatedRuleKey_with_same_RuleKey_should_fail() { + String repositoryKey = randomAlphanumeric(50); + String ruleKey = randomAlphanumeric(50); + db.rules().insertDeprecatedKey(d -> d.setOldRepositoryKey(repositoryKey) + .setOldRuleKey(ruleKey)); + + thrown.expect(PersistenceException.class); + + db.rules().insertDeprecatedKey(d -> d.setOldRepositoryKey(repositoryKey) + .setOldRuleKey(ruleKey)); + } + + private static class Accumulator implements Consumer { private final List list = new ArrayList<>(); @Override 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 d816f4fe0fb..6bfaf0d2831 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 @@ -19,14 +19,15 @@ */ package org.sonar.db.rule; -import java.util.Arrays; import java.util.function.Consumer; import org.sonar.api.rule.RuleKey; import org.sonar.api.server.rule.RuleParamType; import org.sonar.db.DbTester; import org.sonar.db.organization.OrganizationDto; +import static java.util.Arrays.asList; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.sonar.db.rule.RuleTesting.newDeprecatedRuleKey; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.db.rule.RuleTesting.newRuleDto; @@ -49,7 +50,7 @@ public class RuleDbTester { @SafeVarargs public final RuleDefinitionDto insert(Consumer... populaters) { RuleDefinitionDto rule = newRule(); - Arrays.asList(populaters).forEach(populater -> populater.accept(rule)); + asList(populaters).forEach(populater -> populater.accept(rule)); return insert(rule); } @@ -74,7 +75,7 @@ public class RuleDbTester { @SafeVarargs public final RuleMetadataDto insertOrUpdateMetadata(RuleDefinitionDto rule, OrganizationDto organization, Consumer... populaters) { RuleMetadataDto dto = RuleTesting.newRuleMetadata(rule, organization); - Arrays.asList(populaters).forEach(populater -> populater.accept(dto)); + asList(populaters).forEach(populater -> populater.accept(dto)); return insertOrUpdateMetadata(dto); } @@ -91,7 +92,7 @@ public class RuleDbTester { @SafeVarargs public final RuleParamDto insertRuleParam(RuleDefinitionDto rule, Consumer... populaters) { RuleParamDto param = RuleTesting.newRuleParam(rule); - Arrays.asList(populaters).forEach(populater -> populater.accept(param)); + asList(populaters).forEach(populater -> populater.accept(param)); db.getDbClient().ruleDao().insertRuleParam(db.getSession(), rule, param); db.commit(); return param; @@ -127,7 +128,7 @@ public class RuleDbTester { @SafeVarargs public final RuleDto insertRule(OrganizationDto organization, Consumer... populaters) { RuleDto ruleDto = newRuleDto(organization); - Arrays.asList(populaters).forEach(populater -> populater.accept(ruleDto)); + asList(populaters).forEach(populater -> populater.accept(ruleDto)); return insertRule(ruleDto); } @@ -137,6 +138,15 @@ public class RuleDbTester { return insertRule(ruleDto); } + @SafeVarargs + public final DeprecatedRuleKeyDto insertDeprecatedKey(Consumer... deprecatedRuleKeyDtoConsumers) { + DeprecatedRuleKeyDto deprecatedRuleKeyDto = newDeprecatedRuleKey(); + asList(deprecatedRuleKeyDtoConsumers).forEach(c -> c.accept(deprecatedRuleKeyDto)); + db.getDbClient().ruleDao().insert(db.getSession(), deprecatedRuleKeyDto); + return deprecatedRuleKeyDto; + } + + public RuleParamDto insertRuleParam(RuleDto rule) { RuleParamDto param = new RuleParamDto(); param.setRuleId(rule.getId()); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java index 7256c275bd8..3339cc08120 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java @@ -28,6 +28,8 @@ import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; import org.sonar.api.server.rule.RuleParamType; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryFast; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.rule.RuleDto.Format; import org.sonar.db.rule.RuleDto.Scope; @@ -49,6 +51,8 @@ public class RuleTesting { public static final RuleKey XOO_X2 = RuleKey.of("xoo", "x2"); public static final RuleKey XOO_X3 = RuleKey.of("xoo", "x3"); + private static final UuidFactory uuidFactory = UuidFactoryFast.getInstance(); + private RuleTesting() { // only static helpers } @@ -112,6 +116,15 @@ public class RuleTesting { .setType(RuleParamType.STRING.type()); } + public static DeprecatedRuleKeyDto newDeprecatedRuleKey() { + return new DeprecatedRuleKeyDto() + .setUuid(uuidFactory.create()) + .setOldRepositoryKey(randomAlphanumeric(50)) + .setOldRuleKey(randomAlphanumeric(50)) + .setRuleId(nextInt(100_000)) + .setCreatedAt(System.currentTimeMillis()); + } + /** * @deprecated use newRule(...) */ diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index 3920e843356..8f82e6ac4c3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -21,13 +21,14 @@ package org.sonar.server.rule; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.apache.commons.lang.ObjectUtils; @@ -44,11 +45,13 @@ import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; +import org.sonar.core.util.UuidFactory; import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; +import org.sonar.db.rule.DeprecatedRuleKeyDto; import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.rule.RuleDto.Format; import org.sonar.db.rule.RuleDto.Scope; @@ -62,7 +65,10 @@ import org.sonar.server.rule.index.RuleIndexer; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.difference; import static java.lang.String.format; +import static java.util.Collections.emptySet; +import static java.util.stream.Collectors.toSet; /** * Register rules at server startup @@ -80,10 +86,11 @@ public class RegisterRules implements Startable { private final System2 system2; private final OrganizationFlags organizationFlags; private final WebServerRuleFinder webServerRuleFinder; + private final UuidFactory uuidFactory; public RegisterRules(RuleDefinitionsLoader defLoader, QProfileRules qProfileRules, DbClient dbClient, RuleIndexer ruleIndexer, ActiveRuleIndexer activeRuleIndexer, Languages languages, System2 system2, OrganizationFlags organizationFlags, - WebServerRuleFinder webServerRuleFinder) { + WebServerRuleFinder webServerRuleFinder, UuidFactory uuidFactory) { this.defLoader = defLoader; this.qProfileRules = qProfileRules; this.dbClient = dbClient; @@ -93,6 +100,7 @@ public class RegisterRules implements Startable { this.system2 = system2; this.organizationFlags = organizationFlags; this.webServerRuleFinder = webServerRuleFinder; + this.uuidFactory = uuidFactory; } @Override @@ -100,14 +108,20 @@ public class RegisterRules implements Startable { Profiler profiler = Profiler.create(LOG).startInfo("Register rules"); try (DbSession dbSession = dbClient.openSession(false)) { Map allRules = loadRules(dbSession); - List keysToIndex = new ArrayList<>(); + Map> existingDeprecatedRuleKeys = loadDeprecatedRuleKeys(dbSession); RulesDefinition.Context context = defLoader.load(); + + List repositories = getRepositories(context); + + List keysToIndex = new ArrayList<>(); boolean orgsEnabled = organizationFlags.isEnabled(dbSession); - for (RulesDefinition.ExtendedRepository repoDef : getRepositories(context)) { + + for (RulesDefinition.ExtendedRepository repoDef : repositories) { if (languages.get(repoDef.language()) != null) { for (RulesDefinition.Rule ruleDef : repoDef.rules()) { RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key()); + if (ruleDef.template() && orgsEnabled) { RuleDefinitionDto ruleDefinition = allRules.get(ruleKey); if (ruleDefinition != null && ruleDefinition.getStatus() == RuleStatus.REMOVED) { @@ -118,7 +132,7 @@ public class RegisterRules implements Startable { } continue; } - boolean relevantForIndex = registerRule(ruleDef, allRules, dbSession); + boolean relevantForIndex = registerRule(ruleDef, allRules, existingDeprecatedRuleKeys, dbSession); if (relevantForIndex) { keysToIndex.add(ruleKey); } @@ -126,6 +140,7 @@ public class RegisterRules implements Startable { dbSession.commit(); } } + List removedRules = processRemainingDbRules(allRules.values(), dbSession); List changes = removeActiveRulesOnStillExistingRepositories(dbSession, removedRules, context); dbSession.commit(); @@ -142,6 +157,11 @@ public class RegisterRules implements Startable { } } + @Override + public void stop() { + // nothing + } + private void persistRepositories(DbSession dbSession, List repositories) { dbClient.ruleRepositoryDao().truncate(dbSession); List dtos = repositories @@ -152,12 +172,8 @@ public class RegisterRules implements Startable { dbSession.commit(); } - @Override - public void stop() { - // nothing - } - - private boolean registerRule(RulesDefinition.Rule ruleDef, Map allRules, DbSession session) { + private boolean registerRule(RulesDefinition.Rule ruleDef, Map allRules, Map> existingDeprecatedRuleKeys, DbSession session) { RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key()); RuleDefinitionDto existingRule = allRules.remove(ruleKey); @@ -171,24 +187,16 @@ public class RegisterRules implements Startable { newRule = false; } - boolean executeUpdate = false; - if (mergeRule(ruleDef, rule)) { - executeUpdate = true; - } - - if (mergeDebtDefinitions(ruleDef, rule)) { - executeUpdate = true; - } - - if (mergeTags(ruleDef, rule)) { - executeUpdate = true; - } + boolean executeUpdate = mergeRule(ruleDef, rule); + executeUpdate |= mergeDebtDefinitions(ruleDef, rule); + executeUpdate |= mergeTags(ruleDef, rule); if (executeUpdate) { update(session, rule); } mergeParams(ruleDef, rule, session); + updateDeprecatedKeys(ruleDef, rule, existingDeprecatedRuleKeys, session); return newRule || executeUpdate; } @@ -200,6 +208,14 @@ public class RegisterRules implements Startable { return rules; } + private Map> loadDeprecatedRuleKeys(DbSession dbSession) { + return dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbSession) + .stream() + .map(SingleDeprecatedRuleKey::from) + .collect(Collectors.groupingBy(SingleDeprecatedRuleKey::getRuleId, toSet())); + } + + private List getRepositories(RulesDefinition.Context context) { List repositories = new ArrayList<>(); for (RulesDefinition.Repository repoDef : context.repositories()) { @@ -256,7 +272,7 @@ public class RegisterRules implements Startable { } } - private boolean mergeRule(RulesDefinition.Rule def, RuleDefinitionDto dto) { + private static boolean mergeRule(RulesDefinition.Rule def, RuleDefinitionDto dto) { boolean changed = false; if (!StringUtils.equals(dto.getName(), def.name())) { dto.setName(def.name()); @@ -303,7 +319,7 @@ public class RegisterRules implements Startable { return changed; } - private boolean mergeDescription(RulesDefinition.Rule def, RuleDefinitionDto dto) { + private static boolean mergeDescription(RulesDefinition.Rule def, RuleDefinitionDto dto) { boolean changed = false; if (def.htmlDescription() != null && !StringUtils.equals(dto.getDescription(), def.htmlDescription())) { dto.setDescription(def.htmlDescription()); @@ -317,7 +333,7 @@ public class RegisterRules implements Startable { return changed; } - private boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDefinitionDto dto) { + private static boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDefinitionDto dto) { // Debt definitions are set to null if the sub-characteristic and the remediation function are null DebtRemediationFunction debtRemediationFunction = def.debtRemediationFunction(); boolean hasDebt = debtRemediationFunction != null; @@ -331,7 +347,7 @@ public class RegisterRules implements Startable { return mergeDebtDefinitions(dto, null, null, null, null); } - private boolean mergeDebtDefinitions(RuleDefinitionDto dto, @Nullable String remediationFunction, + private static boolean mergeDebtDefinitions(RuleDefinitionDto dto, @Nullable String remediationFunction, @Nullable String remediationCoefficient, @Nullable String remediationOffset, @Nullable String effortToFixDescription) { boolean changed = false; @@ -399,7 +415,7 @@ public class RegisterRules implements Startable { } } - private boolean mergeParam(RuleParamDto paramDto, RulesDefinition.Param paramDef) { + private static boolean mergeParam(RuleParamDto paramDto, RulesDefinition.Param paramDef) { boolean changed = false; if (!StringUtils.equals(paramDto.getType(), paramDef.type().toString())) { paramDto.setType(paramDef.type().toString()); @@ -420,7 +436,7 @@ public class RegisterRules implements Startable { boolean changed = false; if (RuleStatus.REMOVED == ruleDef.status()) { - dto.setSystemTags(Collections.emptySet()); + dto.setSystemTags(emptySet()); changed = true; } else if (dto.getSystemTags().size() != ruleDef.tags().size() || !dto.getSystemTags().containsAll(ruleDef.tags())) { @@ -432,6 +448,33 @@ public class RegisterRules implements Startable { return changed; } + private void updateDeprecatedKeys(RulesDefinition.Rule ruleDef, RuleDefinitionDto rule, + Map> existingDeprecatedKeysById, DbSession dbSession) { + + Set deprecatedRuleKeysFromDefinition = SingleDeprecatedRuleKey.from(ruleDef); + Set deprecatedRuleKeysFromDB = existingDeprecatedKeysById.getOrDefault(rule.getId(), emptySet()); + + // DeprecatedKeys that must be deleted + List uuidsToBeDeleted = difference(deprecatedRuleKeysFromDB, deprecatedRuleKeysFromDefinition).stream() + .map(SingleDeprecatedRuleKey::getUuid) + .collect(MoreCollectors.toList()); + + dbClient.ruleDao().deleteDeprecatedRuleKeys(dbSession, uuidsToBeDeleted); + + // DeprecatedKeys that must be created + Sets.SetView deprecatedRuleKeysToBeCreated = difference(deprecatedRuleKeysFromDefinition, deprecatedRuleKeysFromDB); + + deprecatedRuleKeysToBeCreated + .forEach(r -> dbClient.ruleDao().insert(dbSession, new DeprecatedRuleKeyDto() + .setUuid(uuidFactory.create()) + .setRuleId(rule.getId()) + .setOldRepositoryKey(r.getOldRepositoryKey()) + .setOldRuleKey(r.getOldRuleKey()) + .setCreatedAt(system2.now()) + ) + ); + } + private List processRemainingDbRules(Collection existingRules, DbSession session) { // custom rules check status of template, so they must be processed at the end List customRules = newArrayList(); @@ -465,7 +508,7 @@ public class RegisterRules implements Startable { private void removeRule(DbSession session, List removedRules, RuleDefinitionDto rule) { LOG.info(format("Disable rule %s", rule.getKey())); rule.setStatus(RuleStatus.REMOVED); - rule.setSystemTags(Collections.emptySet()); + rule.setSystemTags(emptySet()); update(session, rule); // FIXME resetting the tags for all organizations must be handled a different way // rule.setTags(Collections.emptySet()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/SingleDeprecatedRuleKey.java b/server/sonar-server/src/main/java/org/sonar/server/rule/SingleDeprecatedRuleKey.java new file mode 100644 index 00000000000..fdfc5b93c56 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/SingleDeprecatedRuleKey.java @@ -0,0 +1,150 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.rule; + +import java.util.Objects; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.db.rule.DeprecatedRuleKeyDto; + +@Immutable +class SingleDeprecatedRuleKey { + private String oldRuleKey; + private String oldRepositoryKey; + private String newRuleKey; + private String newRepositoryKey; + private String uuid; + private Integer ruleId; + + /** + * static methods {@link #from(RulesDefinition.Rule)} and {@link #from(DeprecatedRuleKeyDto)} must be used + */ + private SingleDeprecatedRuleKey() { + // empty + } + + public static Set from(RulesDefinition.Rule rule) { + return rule.deprecatedRuleKeys().stream() + .map(r -> new SingleDeprecatedRuleKey() + .setNewRepositoryKey(rule.repository().key()) + .setNewRuleKey(rule.key()) + .setOldRepositoryKey(r.repository()) + .setOldRuleKey(r.rule())) + .collect(MoreCollectors.toSet(rule.deprecatedRuleKeys().size())); + } + + public static SingleDeprecatedRuleKey from(DeprecatedRuleKeyDto rule) { + return new SingleDeprecatedRuleKey() + .setUuid(rule.getUuid()) + .setRuleId(rule.getRuleId()) + .setNewRepositoryKey(rule.getNewRepositoryKey()) + .setNewRuleKey(rule.getNewRuleKey()) + .setOldRepositoryKey(rule.getOldRepositoryKey()) + .setOldRuleKey(rule.getOldRuleKey()); + } + + public String getOldRuleKey() { + return oldRuleKey; + } + + public String getOldRepositoryKey() { + return oldRepositoryKey; + } + + public RuleKey getOldRuleKeyAsRuleKey() { + return RuleKey.of(oldRepositoryKey, oldRuleKey); + } + + @CheckForNull + public String getNewRuleKey() { + return newRuleKey; + } + + @CheckForNull + public String getNewRepositoryKey() { + return newRepositoryKey; + } + + @CheckForNull + public String getUuid() { + return uuid; + } + + @CheckForNull + public Integer getRuleId() { + return ruleId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SingleDeprecatedRuleKey)) { + return false; + } + SingleDeprecatedRuleKey that = (SingleDeprecatedRuleKey) o; + return Objects.equals(oldRuleKey, that.oldRuleKey) && + Objects.equals(oldRepositoryKey, that.oldRepositoryKey) && + Objects.equals(newRuleKey, that.newRuleKey) && + Objects.equals(newRepositoryKey, that.newRepositoryKey); + } + + @Override + public int hashCode() { + return Objects.hash(oldRuleKey, oldRepositoryKey, newRuleKey, newRepositoryKey); + } + + private SingleDeprecatedRuleKey setRuleId(Integer ruleId) { + this.ruleId = ruleId; + return this; + } + + private SingleDeprecatedRuleKey setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + private SingleDeprecatedRuleKey setOldRuleKey(String oldRuleKey) { + this.oldRuleKey = oldRuleKey; + return this; + } + + private SingleDeprecatedRuleKey setOldRepositoryKey(String oldRepositoryKey) { + this.oldRepositoryKey = oldRepositoryKey; + return this; + } + + private SingleDeprecatedRuleKey setNewRuleKey(@Nullable String newRuleKey) { + this.newRuleKey = newRuleKey; + return this; + } + + private SingleDeprecatedRuleKey setNewRepositoryKey(@Nullable String newRepositoryKey) { + this.newRepositoryKey = newRepositoryKey; + return this; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java index 4bf6459bf56..5be05c9d9dc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java @@ -21,6 +21,7 @@ package org.sonar.server.rule; import java.util.Date; import java.util.List; +import java.util.Set; import java.util.stream.IntStream; import org.junit.Before; import org.junit.Test; @@ -37,10 +38,13 @@ import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryFast; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.rule.DeprecatedRuleKeyDto; import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleDto.Scope; @@ -69,6 +73,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.api.rule.Severity.BLOCKER; import static org.sonar.api.rule.Severity.INFO; +import static org.sonar.api.server.rule.RulesDefinition.*; public class RegisterRulesTest { @@ -98,6 +103,7 @@ public class RegisterRulesTest { private RuleIndex ruleIndex; private OrganizationDto defaultOrganization; private OrganizationFlags organizationFlags = TestOrganizationFlags.standalone(); + private UuidFactory uuidFactory = UuidFactoryFast.getInstance(); @Before public void before() { @@ -150,7 +156,7 @@ public class RegisterRulesTest { // register one rule execute(context -> { - RulesDefinition.NewRepository repo = context.createRepository("fake", "java"); + NewRepository repo = context.createRepository("fake", "java"); repo.createRule(ruleKey) .setName(randomAlphanumeric(5)) .setHtmlDescription(randomAlphanumeric(20)); @@ -192,7 +198,7 @@ public class RegisterRulesTest { // register many rules execute(context -> { - RulesDefinition.NewRepository repo = context.createRepository("fake", "java"); + NewRepository repo = context.createRepository("fake", "java"); IntStream.range(0, numberOfRules) .mapToObj(i -> "rule-" + i) .forEach(ruleKey -> repo.createRule(ruleKey) @@ -298,32 +304,26 @@ public class RegisterRulesTest { @Test public void add_new_tag() { - execute(new RulesDefinition() { - @Override - public void define(RulesDefinition.Context context) { - RulesDefinition.NewRepository repo = context.createRepository("fake", "java"); - repo.createRule("rule1") - .setName("Rule One") - .setHtmlDescription("Description of Rule One") - .setTags("tag1"); - repo.done(); - } + execute((RulesDefinition) context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule1") + .setName("Rule One") + .setHtmlDescription("Description of Rule One") + .setTags("tag1"); + repo.done(); }); OrganizationDto defaultOrganization = dbTester.getDefaultOrganization(); RuleDto rule = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY1); assertThat(rule.getSystemTags()).containsOnly("tag1"); - execute(new RulesDefinition() { - @Override - public void define(RulesDefinition.Context context) { - RulesDefinition.NewRepository repo = context.createRepository("fake", "java"); - repo.createRule("rule1") - .setName("Rule One") - .setHtmlDescription("Description of Rule One") - .setTags("tag1", "tag2"); - repo.done(); - } + execute((RulesDefinition) context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule1") + .setName("Rule One") + .setHtmlDescription("Description of Rule One") + .setTags("tag1", "tag2"); + repo.done(); }); rule = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY1); @@ -333,27 +333,21 @@ public class RegisterRulesTest { @Test public void update_only_rule_name() { when(system.now()).thenReturn(DATE1.getTime()); - execute(new RulesDefinition() { - @Override - public void define(Context context) { - NewRepository repo = context.createRepository("fake", "java"); - repo.createRule("rule") - .setName("Name1") - .setHtmlDescription("Description"); - repo.done(); - } + execute((RulesDefinition) context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule") + .setName("Name1") + .setHtmlDescription("Description"); + repo.done(); }); when(system.now()).thenReturn(DATE2.getTime()); - execute(new RulesDefinition() { - @Override - public void define(Context context) { - NewRepository repo = context.createRepository("fake", "java"); - repo.createRule("rule") - .setName("Name2") - .setHtmlDescription("Description"); - repo.done(); - } + execute((RulesDefinition) context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule") + .setName("Name2") + .setHtmlDescription("Description"); + repo.done(); }); // rule1 has been updated @@ -368,27 +362,21 @@ public class RegisterRulesTest { @Test public void update_only_rule_description() { when(system.now()).thenReturn(DATE1.getTime()); - execute(new RulesDefinition() { - @Override - public void define(Context context) { - NewRepository repo = context.createRepository("fake", "java"); - repo.createRule("rule") - .setName("Name") - .setHtmlDescription("Desc1"); - repo.done(); - } + execute((RulesDefinition) context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule") + .setName("Name") + .setHtmlDescription("Desc1"); + repo.done(); }); when(system.now()).thenReturn(DATE2.getTime()); - execute(new RulesDefinition() { - @Override - public void define(Context context) { - NewRepository repo = context.createRepository("fake", "java"); - repo.createRule("rule") - .setName("Name") - .setHtmlDescription("Desc2"); - repo.done(); - } + execute((RulesDefinition) context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule") + .setName("Name") + .setHtmlDescription("Desc2"); + repo.done(); }); // rule1 has been updated @@ -431,7 +419,6 @@ public class RegisterRulesTest { when(system.now()).thenReturn(DATE2.getTime()); execute(new FakeRepositoryV1()); - String organizationUuid = dbTester.getDefaultOrganization().getUuid(); RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY1); assertThat(rule1.getCreatedAt()).isEqualTo(DATE1.getTime()); assertThat(rule1.getUpdatedAt()).isEqualTo(DATE1.getTime()); @@ -443,7 +430,6 @@ public class RegisterRulesTest { assertThat(dbClient.ruleDao().selectAllDefinitions(dbTester.getSession())).hasSize(2); assertThat(esTester.getIds(RuleIndexDefinition.INDEX_TYPE_RULE)).containsOnly(RULE_KEY1.toString(), RULE_KEY2.toString()); - String organizationUuid = dbTester.getDefaultOrganization().getUuid(); RuleDto rule2 = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY2); assertThat(rule2.getStatus()).isEqualTo(RuleStatus.READY); @@ -530,6 +516,94 @@ public class RegisterRulesTest { assertThat(logTester.logs(LoggerLevel.INFO)).contains("Template rule test:rule1 will not be imported, because organizations are enabled."); } + @Test + public void rules_that_deprecate_previous_rule_must_be_recorded() { + execute(context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule1") + .setName("One") + .setHtmlDescription("Description of One") + .setSeverity(BLOCKER) + .setInternalKey("config1") + .setTags("tag1", "tag2", "tag3") + .setType(RuleType.CODE_SMELL) + .setStatus(RuleStatus.BETA); + repo.done(); + }); + + execute(context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("newKey") + .setName("One") + .setHtmlDescription("Description of One") + .setSeverity(BLOCKER) + .setInternalKey("config1") + .setTags("tag1", "tag2", "tag3") + .setType(RuleType.CODE_SMELL) + .setStatus(RuleStatus.BETA) + .addDeprecatedRuleKey("fake", "rule1") + .addDeprecatedRuleKey("fake", "rule2"); + repo.done(); + }); + + List rules = dbClient.ruleDao().selectAllDefinitions(dbTester.getSession()); + Set deprecatedRuleKeys = dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbTester.getSession()); + //assertThat(rules).hasSize(1); FIXME this must be true when renaming is done + assertThat(deprecatedRuleKeys).hasSize(2); + } + + @Test + public void rules_that_remove_deprecated_key_must_remove_records() { + execute(context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("rule1") + .setName("One") + .setHtmlDescription("Description of One") + .setSeverity(BLOCKER) + .setInternalKey("config1") + .setTags("tag1", "tag2", "tag3") + .setType(RuleType.CODE_SMELL) + .setStatus(RuleStatus.BETA); + repo.done(); + }); + + execute(context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("newKey") + .setName("One") + .setHtmlDescription("Description of One") + .setSeverity(BLOCKER) + .setInternalKey("config1") + .setTags("tag1", "tag2", "tag3") + .setType(RuleType.CODE_SMELL) + .setStatus(RuleStatus.BETA) + .addDeprecatedRuleKey("fake", "rule1") + .addDeprecatedRuleKey("fake", "rule2"); + repo.done(); + }); + + //assertThat(dbClient.ruleDao().selectAllDefinitions(dbTester.getSession())).hasSize(1); FIXME this must be true when renaming is done + Set deprecatedRuleKeys = dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbTester.getSession()); + assertThat(deprecatedRuleKeys).hasSize(2); + + execute(context -> { + NewRepository repo = context.createRepository("fake", "java"); + repo.createRule("newKey") + .setName("One") + .setHtmlDescription("Description of One") + .setSeverity(BLOCKER) + .setInternalKey("config1") + .setTags("tag1", "tag2", "tag3") + .setType(RuleType.CODE_SMELL) + .setStatus(RuleStatus.BETA); + repo.done(); + }); + + //assertThat(dbClient.ruleDao().selectAllDefinitions(dbTester.getSession())).hasSize(1); FIXME this must be true when renaming is done + deprecatedRuleKeys = dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbTester.getSession()); + assertThat(deprecatedRuleKeys).hasSize(0); + } + private void execute(RulesDefinition... defs) { ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); when(pluginRepository.getPluginKey(any(RulesDefinition.class))).thenReturn(FAKE_PLUGIN_KEY); @@ -539,7 +613,8 @@ public class RegisterRulesTest { when(languages.get("java")).thenReturn(mock(Language.class)); reset(webServerRuleFinder); - RegisterRules task = new RegisterRules(loader, qProfileRules, dbClient, ruleIndexer, activeRuleIndexer, languages, system, organizationFlags, webServerRuleFinder); + RegisterRules task = new RegisterRules(loader, qProfileRules, dbClient, ruleIndexer, activeRuleIndexer, + languages, system, organizationFlags, webServerRuleFinder, uuidFactory); task.start(); // Execute a commit to refresh session state as the task is using its own session dbTester.getSession().commit(); @@ -658,7 +733,7 @@ public class RegisterRulesTest { static class RepositoryWithOneTemplateRule implements RulesDefinition { @Override public void define(Context context) { - RulesDefinition.NewRepository repo = context.createRepository("test", "java"); + NewRepository repo = context.createRepository("test", "java"); repo.createRule("rule1") .setName("Rule One") .setHtmlDescription("Description of Rule One") diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/SingleDeprecatedRuleKeyTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/SingleDeprecatedRuleKeyTest.java new file mode 100644 index 00000000000..0311797284b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/SingleDeprecatedRuleKeyTest.java @@ -0,0 +1,129 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.rule; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import org.assertj.core.groups.Tuple; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.db.rule.DeprecatedRuleKeyDto; + +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.apache.commons.lang.math.RandomUtils.nextInt; +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.mockito.Mockito.when; + +public class SingleDeprecatedRuleKeyTest { + + @Test + public void test_creation_from_DeprecatedRuleKeyDto() { + // Creation from DeprecatedRuleKeyDto + DeprecatedRuleKeyDto deprecatedRuleKeyDto = new DeprecatedRuleKeyDto() + .setOldRuleKey(randomAlphanumeric(50)) + .setOldRepositoryKey(randomAlphanumeric(50)) + .setRuleId(nextInt(1000)) + .setUuid(randomAlphanumeric(40)); + + SingleDeprecatedRuleKey singleDeprecatedRuleKey = SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto); + + assertThat(singleDeprecatedRuleKey.getOldRepositoryKey()).isEqualTo(deprecatedRuleKeyDto.getOldRepositoryKey()); + assertThat(singleDeprecatedRuleKey.getOldRuleKey()).isEqualTo(deprecatedRuleKeyDto.getOldRuleKey()); + assertThat(singleDeprecatedRuleKey.getNewRepositoryKey()).isEqualTo(deprecatedRuleKeyDto.getNewRepositoryKey()); + assertThat(singleDeprecatedRuleKey.getNewRuleKey()).isEqualTo(deprecatedRuleKeyDto.getNewRuleKey()); + assertThat(singleDeprecatedRuleKey.getUuid()).isEqualTo(deprecatedRuleKeyDto.getUuid()); + assertThat(singleDeprecatedRuleKey.getRuleId()).isEqualTo(deprecatedRuleKeyDto.getRuleId()); + assertThat(singleDeprecatedRuleKey.getOldRuleKeyAsRuleKey()) + .isEqualTo(RuleKey.of(deprecatedRuleKeyDto.getOldRepositoryKey(), deprecatedRuleKeyDto.getOldRuleKey())); + } + + @Test + public void test_creation_from_RulesDefinitionRule() { + // Creation from RulesDefinition.Rule + ImmutableSet deprecatedRuleKeys = ImmutableSet.of( + RuleKey.of(randomAlphanumeric(50), randomAlphanumeric(50)), + RuleKey.of(randomAlphanumeric(50), randomAlphanumeric(50)), + RuleKey.of(randomAlphanumeric(50), randomAlphanumeric(50)) + ); + + RulesDefinition.Repository repository = mock(RulesDefinition.Repository.class); + when(repository.key()).thenReturn(randomAlphanumeric(50)); + + RulesDefinition.Rule rule = mock(RulesDefinition.Rule.class); + when(rule.key()).thenReturn(randomAlphanumeric(50)); + when(rule.deprecatedRuleKeys()).thenReturn(deprecatedRuleKeys); + when(rule.repository()).thenReturn(repository); + + Set singleDeprecatedRuleKeys = SingleDeprecatedRuleKey.from(rule); + assertThat(singleDeprecatedRuleKeys).hasSize(deprecatedRuleKeys.size()); + assertThat(singleDeprecatedRuleKeys) + .extracting(SingleDeprecatedRuleKey::getUuid, SingleDeprecatedRuleKey::getOldRepositoryKey, SingleDeprecatedRuleKey::getOldRuleKey, + SingleDeprecatedRuleKey::getNewRepositoryKey, SingleDeprecatedRuleKey::getNewRuleKey, SingleDeprecatedRuleKey::getOldRuleKeyAsRuleKey) + .containsExactlyInAnyOrder( + deprecatedRuleKeys.stream().map( + r -> tuple(null, r.repository(), r.rule(), rule.repository().key(), rule.key(), RuleKey.of(r.repository(), r.rule())) + ).collect(MoreCollectors.toArrayList(deprecatedRuleKeys.size())).toArray(new Tuple[deprecatedRuleKeys.size()]) + ); + } + + @Test + public void test_equality() { + DeprecatedRuleKeyDto deprecatedRuleKeyDto1 = new DeprecatedRuleKeyDto() + .setOldRuleKey(randomAlphanumeric(50)) + .setOldRepositoryKey(randomAlphanumeric(50)) + .setUuid(randomAlphanumeric(40)) + .setRuleId(1); + + DeprecatedRuleKeyDto deprecatedRuleKeyDto1WithoutUuid = new DeprecatedRuleKeyDto() + .setOldRuleKey(deprecatedRuleKeyDto1.getOldRuleKey()) + .setOldRepositoryKey(deprecatedRuleKeyDto1.getOldRepositoryKey()); + + DeprecatedRuleKeyDto deprecatedRuleKeyDto2 = new DeprecatedRuleKeyDto() + .setOldRuleKey(randomAlphanumeric(50)) + .setOldRepositoryKey(randomAlphanumeric(50)) + .setUuid(randomAlphanumeric(40)); + + SingleDeprecatedRuleKey singleDeprecatedRuleKey1 = SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto1); + SingleDeprecatedRuleKey singleDeprecatedRuleKey2 = SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto2); + + assertThat(singleDeprecatedRuleKey1).isEqualTo(singleDeprecatedRuleKey1); + assertThat(singleDeprecatedRuleKey1).isEqualTo(SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto1)); + assertThat(singleDeprecatedRuleKey1).isEqualTo(SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto1WithoutUuid)); + assertThat(singleDeprecatedRuleKey2).isEqualTo(SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto2)); + + assertThat(singleDeprecatedRuleKey1.hashCode()).isEqualTo(singleDeprecatedRuleKey1.hashCode()); + assertThat(singleDeprecatedRuleKey1.hashCode()).isEqualTo(SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto1).hashCode()); + assertThat(singleDeprecatedRuleKey1.hashCode()).isEqualTo(SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto1WithoutUuid).hashCode()); + assertThat(singleDeprecatedRuleKey2.hashCode()).isEqualTo(SingleDeprecatedRuleKey.from(deprecatedRuleKeyDto2).hashCode()); + + assertThat(singleDeprecatedRuleKey1).isNotEqualTo(null); + assertThat(singleDeprecatedRuleKey1).isNotEqualTo(""); + assertThat(singleDeprecatedRuleKey1).isNotEqualTo(singleDeprecatedRuleKey2); + assertThat(singleDeprecatedRuleKey2).isNotEqualTo(singleDeprecatedRuleKey1); + + assertThat(singleDeprecatedRuleKey1.hashCode()).isNotEqualTo(singleDeprecatedRuleKey2.hashCode()); + assertThat(singleDeprecatedRuleKey2.hashCode()).isNotEqualTo(singleDeprecatedRuleKey1.hashCode()); + } +} -- 2.39.5