]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10321 Store rule scope in DB
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Thu, 25 Jan 2018 15:43:00 +0000 (16:43 +0100)
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>
Wed, 7 Feb 2018 10:32:38 +0000 (11:32 +0100)
35 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/insert_parameter.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/selectEnabled.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/selectNonManual.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/select_parameters_by_rule_key.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/shared.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/update.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/update_parameter-result.xml
server/sonar-db-dao/src/test/resources/org/sonar/db/rule/RuleDaoTest/update_parameter.xml
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddRuleScope.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRules.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMain.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddRuleScopeTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRulesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMainTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddRuleScopeTest/rules.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRulesTest/rules.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMainTest/rules.sql [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/DefaultRuleFinderTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/extract_directory_path.xml
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/extract_file_path.xml
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/many_projects.xml
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/one_issue.xml
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/shared.xml
server/sonar-server/src/test/resources/org/sonar/server/platform/BackendCleanupTest/shared.xml

index 05d3d7872bb410c2c87a168d5dc5ad0253ec7298..175424d028c2f1a0c8a310da36a1569faec21dd3 100644 (file)
@@ -186,6 +186,7 @@ CREATE TABLE "RULES" (
   "NAME" VARCHAR(200),
   "STATUS" VARCHAR(40),
   "LANGUAGE" VARCHAR(20),
+  "SCOPE" VARCHAR(20) NOT NULL,
   "DEF_REMEDIATION_FUNCTION" VARCHAR(20),
   "DEF_REMEDIATION_GAP_MULT" VARCHAR(20),
   "DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20),
index cdd0b07a7af4e6b1d8514dfe2742ab39f7bcff19..0321e74d4309e6add903c7a103d1b42bcf4aa284 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rules.RuleType;
+import org.sonar.db.rule.RuleDto.Scope;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -54,6 +55,7 @@ public class RuleDefinitionDto {
   private String gapDescription;
   private String systemTags;
   private int type;
+  private Scope scope;
 
   private RuleKey key;
 
@@ -295,6 +297,15 @@ public class RuleDefinitionDto {
     return this;
   }
 
+  public Scope getScope() {
+    return this.scope;
+  }
+
+  public RuleDefinitionDto setScope(Scope scope) {
+    this.scope = scope;
+    return this;
+  }
+
   @CheckForNull
   public String getPluginKey() {
     return pluginKey;
@@ -358,6 +369,7 @@ public class RuleDefinitionDto {
       ", key=" + key +
       ", createdAt=" + createdAt +
       ", updatedAt=" + updatedAt +
+      ", scope=" + scope +
       '}';
   }
 }
index 46b36b523aa783fcf1214efb200d5f02adc1d7bb..86afcb2bed12073fa491fa4beb6c4427c7549fe9 100644 (file)
@@ -33,6 +33,10 @@ public class RuleDto {
   public enum Format {
     HTML, MARKDOWN
   }
+  
+  public enum Scope {
+    MAIN, TEST, ALL;
+  }
 
   private final RuleDefinitionDto definition;
   private final RuleMetadataDto metadata;
@@ -138,11 +142,20 @@ public class RuleDto {
   public String getConfigKey() {
     return definition.getConfigKey();
   }
-
+  
   public RuleDto setConfigKey(@Nullable String configKey) {
     definition.setConfigKey(configKey);
     return this;
   }
+  
+  public Scope getScope() {
+    return definition.getScope();
+  }
+  
+  public RuleDto setScope(Scope scope) {
+    definition.setScope(scope);
+    return this;
+  }
 
   @CheckForNull
   public Integer getSeverity() {
index d1a161504b437dd57e3043247c7b8413ba3b43f9..6cfde04506cf1c955abb09efa2028400558a2c76 100644 (file)
@@ -23,6 +23,7 @@
     r.system_tags as "systemTagsField",
     r.rule_type as "type",
     r.plugin_key as "pluginKey",
+    r.scope,
   </sql>
 
   <sql id="selectRuleTableColumns">
       gap_description,
       system_tags,
       rule_type,
+      scope,
       created_at,
       updated_at
     )
       #{gapDescription,jdbcType=VARCHAR},
       #{systemTagsField,jdbcType=VARCHAR},
       #{type,jdbcType=TINYINT},
+      #{scope,jdbcType=VARCHAR},
       #{createdAt,jdbcType=BIGINT},
       #{updatedAt,jdbcType=BIGINT}
     )
       def_remediation_base_effort=#{defRemediationBaseEffort,jdbcType=VARCHAR},
       gap_description=#{gapDescription,jdbcType=VARCHAR},
       system_tags=#{systemTagsField,jdbcType=VARCHAR},
+      scope=#{scope,jdbcType=VARCHAR},
       rule_type=#{type,jdbcType=TINYINT},
       updated_at=#{updatedAt,jdbcType=BIGINT}
     where
index c96f3c36e1b31047cdaf8b9ceef1b665a1ab2223..0cd2d997ee93252f48bb9c0d3147d6a24bd883d1 100644 (file)
@@ -44,6 +44,7 @@ import org.sonar.db.RowNotFoundException;
 import org.sonar.db.es.RuleExtensionId;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.organization.OrganizationTesting;
+import org.sonar.db.rule.RuleDto.Scope;
 
 import static com.google.common.collect.Sets.newHashSet;
 import static java.util.Arrays.asList;
@@ -329,6 +330,7 @@ public class RuleDaoTest {
       .setGapDescription("squid.S115.effortToFix")
       .setSystemTags(newHashSet("systag1", "systag2"))
       .setType(RuleType.BUG)
+      .setScope(Scope.ALL)
       .setCreatedAt(1_500_000_000_000L)
       .setUpdatedAt(2_000_000_000_000L);
     underTest.insert(db.getSession(), newRule);
@@ -352,6 +354,7 @@ public class RuleDaoTest {
     assertThat(ruleDto.getDefRemediationBaseEffort()).isEqualTo("10h");
     assertThat(ruleDto.getGapDescription()).isEqualTo("squid.S115.effortToFix");
     assertThat(ruleDto.getSystemTags()).containsOnly("systag1", "systag2");
+    assertThat(ruleDto.getScope()).isEqualTo(Scope.ALL);
     assertThat(ruleDto.getType()).isEqualTo(RuleType.BUG.getDbConstant());
     assertThat(ruleDto.getCreatedAt()).isEqualTo(1_500_000_000_000L);
     assertThat(ruleDto.getUpdatedAt()).isEqualTo(2_000_000_000_000L);
@@ -379,6 +382,7 @@ public class RuleDaoTest {
       .setDefRemediationBaseEffort("10h")
       .setGapDescription("squid.S115.effortToFix")
       .setSystemTags(newHashSet("systag1", "systag2"))
+      .setScope(Scope.ALL)
       .setType(RuleType.BUG)
       .setUpdatedAt(2_000_000_000_000L);
 
@@ -402,6 +406,7 @@ public class RuleDaoTest {
     assertThat(ruleDto.getDefRemediationBaseEffort()).isEqualTo("10h");
     assertThat(ruleDto.getGapDescription()).isEqualTo("squid.S115.effortToFix");
     assertThat(ruleDto.getSystemTags()).containsOnly("systag1", "systag2");
+    assertThat(ruleDto.getScope()).isEqualTo(Scope.ALL);
     assertThat(ruleDto.getType()).isEqualTo(RuleType.BUG.getDbConstant());
     assertThat(ruleDto.getCreatedAt()).isEqualTo(1_500_000_000_000L);
     assertThat(ruleDto.getUpdatedAt()).isEqualTo(2_000_000_000_000L);
@@ -783,8 +788,9 @@ public class RuleDaoTest {
         tuple(r1.getKey(), organization.getUuid(), r1Extension.getTagsAsString()));
   }
 
-  private static class Accumulator<T>  implements Consumer<T> {
+  private static class Accumulator<T> implements Consumer<T> {
     private final List<T> list = new ArrayList<>();
+
     @Override
     public void accept(T dto) {
       list.add(dto);
index bf6dd1ccf8ee2db0cd5df4cb0fcb2ade14d3b4da..8c39a2d111c6fc6300b97deffbc4188d2c87d999 100644 (file)
@@ -15,6 +15,7 @@
          def_remediation_gap_mult="5d"
          def_remediation_base_effort="10h"
          gap_description="squid.S115.effortToFix"
+         scope="MAIN"
          created_at="[null]"
          updated_at="[null]"
   />
index 66427866042d5d4658a70ff1841d8c60dcb2c1ac..593a6a4a0427fd7d9e1007ad1ca8305dd94892ff 100644 (file)
@@ -12,6 +12,7 @@
          def_remediation_gap_mult="5d"
          def_remediation_base_effort="10h"
          gap_description="squid.S115.effortToFix"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"
   />
@@ -27,6 +28,7 @@
          def_remediation_function="[null]"
          def_remediation_gap_mult="[null]"
          def_remediation_base_effort="[null]"
+         scope="MAIN"
          gap_description="[null]"
          created_at="1500000000000"
          updated_at="1600000000000"
index 3d49bf1ac0a870db202eb1de240b7260c22afc0e..faebb444a42331f13d5f07a3d8507ee6d562c2d1 100644 (file)
@@ -8,6 +8,7 @@
          description="Should avoid NULL"
          status="READY"
          description_format="HTML"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"/>
   <rules system_tags="[null]"
@@ -18,6 +19,7 @@
          description="Should not appear"
          status="READY"
          description_format="HTML"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"/>
 
index f96c135914798359259d63c4ea48729a1e4425e1..49b9773832a4c30f84695fee8520153d403e4b72 100644 (file)
@@ -7,6 +7,7 @@
          name="Avoid Null"
          description="Should avoid NULL"
          status="READY"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"/>
   <rules_parameters id="1"
@@ -23,6 +24,7 @@
          name="Unused Rule"
          description="Not used"
          status="REMOVED"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"/>
   <rules_parameters id="2"
index 2619953014a126a3804cdeb79f3220f1bf71750e..5014ce59d649624ff7f3d362310b619ca6ef0272 100644 (file)
@@ -13,6 +13,7 @@
          system_tags="cwe"
          created_at="1500000000000"
          updated_at="1600000000000"
+         scope="ALL"
   />
 
   <rules id="2"
@@ -28,6 +29,7 @@
          system_tags="[null]"
          created_at="1500000000000"
          updated_at="1600000000000"
+         scope="ALL"
   />
 
   <rules id="10"
@@ -43,6 +45,7 @@
          system_tags="[null]"
          created_at="1500000000000"
          updated_at="1600000000000"
+         scope="ALL"
   />
 
 </dataset>
index eed68c92290efad893b6f1981e799e9921b4e7be..b0050d14b7f11229be393aad512f353e0ecd358f 100644 (file)
@@ -10,6 +10,7 @@
          plugin_config_key="AvoidNull"
          priority="2"
          is_template="[false]"
+         scope="MAIN"
          language="golo"
          created_at="1500000000000"
          updated_at="1600000000000"/>
index 6f7b4c7f679848e03008dd2f10f64a22d6145b46..bb3801df484cf0183a18681c6d7b9506f2eeb300 100644 (file)
@@ -6,6 +6,7 @@
          name="Avoid Null"
          description="Should avoid NULL"
          status="READY"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"/>
 
index 26bc299306ce8cc6509a1cb6ff54578911258a94..484f86c62c505b2525ff8ccd649054a619ca822d 100644 (file)
@@ -6,6 +6,7 @@
          name="Avoid Null"
          description="Should avoid NULL"
          status="READY"
+         scope="MAIN"
          created_at="1500000000000"
          updated_at="1600000000000"/>
 
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddRuleScope.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddRuleScope.java
new file mode 100644 (file)
index 0000000..8bb0fc1
--- /dev/null
@@ -0,0 +1,26 @@
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddRuleScope extends DdlChange {
+
+  public AddRuleScope(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AddColumnsBuilder(getDialect(), "rules")
+      .addColumn(VarcharColumnDef.newVarcharColumnDefBuilder()
+        .setColumnName("scope")
+        .setIsNullable(true)
+        .setLimit(20)
+        .build())
+      .build());    
+  }
+
+}
index 2705f099de4d95a975c50e756ab20e12308ca538..bb67a4d88ed50c042f90eb9c0b7af2ed351057d2 100644 (file)
@@ -27,6 +27,9 @@ public class DbVersion71 implements DbVersion {
   @Override
   public void addSteps(MigrationStepRegistry registry) {
     registry
-      .add(2000, "Delete settings defined in sonar.properties from PROPERTIES table", DeleteSettingsDefinedInSonarDotProperties.class);
+      .add(2000, "Delete settings defined in sonar.properties from PROPERTIES table", DeleteSettingsDefinedInSonarDotProperties.class)
+      .add(2001, "Add scope to rules", AddRuleScope.class)
+      .add(2002, "Set rules scope to MAIN", SetRuleScopeToMain.class)
+      .add(2003, "Make scope not nullable in rules", MakeScopeNotNullableInRules.class);
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRules.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRules.java
new file mode 100644 (file)
index 0000000..9bd0777
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeScopeNotNullableInRules extends DdlChange {
+  private static final String TABLE_NAME = "rules";
+
+  public MakeScopeNotNullableInRules(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AlterColumnsBuilder(getDialect(), TABLE_NAME)
+      .updateColumn(newVarcharColumnDefBuilder()
+        .setColumnName("scope")
+        .setLimit(20)
+        .setIsNullable(false)
+        .build())
+      .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMain.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMain.java
new file mode 100644 (file)
index 0000000..b2fb71e
--- /dev/null
@@ -0,0 +1,31 @@
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.api.rule.RuleScope;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class SetRuleScopeToMain extends DataChange {
+  private final System2 system2;
+
+  public SetRuleScopeToMain(Database db, System2 system2) {
+    super(db);
+    this.system2 = system2;
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    long now = system2.now();
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select id from rules where scope is NULL");
+    massUpdate.rowPluralName("rules");
+    massUpdate.update("update rules set scope=?, updated_at=? where scope is NULL");
+    massUpdate.execute((row, update) -> {
+      update.setString(1, RuleScope.MAIN.name());
+      update.setLong(2, now);
+      return true;
+    });
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddRuleScopeTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddRuleScopeTest.java
new file mode 100644 (file)
index 0000000..30856ed
--- /dev/null
@@ -0,0 +1,35 @@
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddRuleScopeTest {
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddRuleScopeTest.class, "rules.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private AddRuleScope underTest = new AddRuleScope(dbTester.database());
+
+  @Test
+  public void column_is_added_to_table() throws SQLException {
+    underTest.execute();
+
+    dbTester.assertColumnDefinition("rules", "scope", VARCHAR, null, true);
+  }
+
+  @Test
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+}
index 47b980bb2fad8191d219b861252afe18643e7159..c65e93d43a8366d48da8a574e70095d368459c49 100644 (file)
@@ -36,7 +36,7 @@ public class DbVersion71Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 1);
+    verifyMigrationCount(underTest, 4);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRulesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRulesTest.java
new file mode 100644 (file)
index 0000000..192abdc
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleScope;
+import org.sonar.db.CoreDbTester;
+
+public class MakeScopeNotNullableInRulesTest {
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(MakeScopeNotNullableInRulesTest.class, "rules.sql");
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private MakeScopeNotNullableInRules underTest = new MakeScopeNotNullableInRules(db.database());
+
+  @Test
+  public void execute_makes_column_not_null() throws SQLException {
+    db.assertColumnDefinition("rules", "scope", Types.VARCHAR, null, true);
+    insertRow(1);
+    insertRow(2);
+
+    underTest.execute();
+
+    db.assertColumnDefinition("rules", "scope", Types.VARCHAR, null, false);
+  }
+
+  private void insertRow(int id) {
+    db.executeInsert(
+      "RULES",
+      "PLUGIN_RULE_KEY", "key_" + id,
+      "PLUGIN_NAME", "name_" + id,
+      "SCOPE", RuleScope.MAIN.name());
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMainTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMainTest.java
new file mode 100644 (file)
index 0000000..0a8c8b1
--- /dev/null
@@ -0,0 +1,88 @@
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleScope;
+import org.sonar.api.utils.System2;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SetRuleScopeToMainTest {
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(SetRuleScopeToMainTest.class, "rules.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private System2 system = new System2();
+
+  private SetRuleScopeToMain underTest = new SetRuleScopeToMain(dbTester.database(), system);
+
+  @Test
+  public void has_no_effect_if_table_rules_is_empty() throws SQLException {
+    underTest.execute();
+
+    assertThat(dbTester.countRowsOfTable("rules")).isEqualTo(0);
+  }
+
+  @Test
+  public void updates_rows_with_null_is_build_in_column_to_false() throws SQLException {
+    insertRow(1, null);
+    insertRow(2, null);
+
+    assertThat(countRowsWithValue(null)).isEqualTo(2);
+    assertThat(countRowsWithValue(RuleScope.MAIN)).isEqualTo(0);
+
+    underTest.execute();
+
+    assertThat(countRowsWithValue(null)).isEqualTo(0);
+    assertThat(countRowsWithValue(RuleScope.MAIN)).isEqualTo(2);
+  }
+
+  @Test
+  public void support_large_number_of_rows() throws SQLException {
+    for (int i = 0; i < 2_000; i++) {
+      insertRow(i, null);
+    }
+
+    assertThat(countRowsWithValue(null)).isEqualTo(2000);
+    assertThat(countRowsWithValue(RuleScope.MAIN)).isZero();
+
+    underTest.execute();
+
+    assertThat(countRowsWithValue(RuleScope.MAIN)).isEqualTo(2_000);
+    assertThat(countRowsWithValue(null)).isEqualTo(0);
+  }
+
+  @Test
+  public void execute_is_reentreant() throws SQLException {
+    insertRow(1, null);
+    insertRow(2, RuleScope.MAIN);
+
+    underTest.execute();
+
+    underTest.execute();
+
+    assertThat(countRowsWithValue(null)).isEqualTo(0);
+    assertThat(countRowsWithValue(RuleScope.MAIN)).isEqualTo(2);
+  }
+
+  private int countRowsWithValue(@Nullable RuleScope value) {
+    if (value == null) {
+      return dbTester.countSql("select count(1) from rules where scope is null");
+    }
+    return dbTester.countSql("select count(1) from rules where scope='" + value + "'");
+  }
+
+  private void insertRow(int id, @Nullable RuleScope scope) {
+    dbTester.executeInsert(
+      "RULES",
+      "PLUGIN_RULE_KEY", "key_" + id,
+      "PLUGIN_NAME", "name_" + id,
+      "SCOPE", scope == null ? null : scope.name());
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddRuleScopeTest/rules.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddRuleScopeTest/rules.sql
new file mode 100644 (file)
index 0000000..7bc032c
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE TABLE "RULES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "PLUGIN_KEY" VARCHAR(200),
+  "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+  "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+  "DESCRIPTION" VARCHAR(16777215),
+  "DESCRIPTION_FORMAT" VARCHAR(20),
+  "PRIORITY" INTEGER,
+  "IS_TEMPLATE" BOOLEAN DEFAULT FALSE,
+  "TEMPLATE_ID" INTEGER,
+  "PLUGIN_CONFIG_KEY" VARCHAR(200),
+  "NAME" VARCHAR(200),
+  "STATUS" VARCHAR(40),
+  "LANGUAGE" VARCHAR(20),
+  "DEF_REMEDIATION_FUNCTION" VARCHAR(20),
+  "DEF_REMEDIATION_GAP_MULT" VARCHAR(20),
+  "DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20),
+  "GAP_DESCRIPTION" VARCHAR(4000),
+  "SYSTEM_TAGS" VARCHAR(4000),
+  "RULE_TYPE" TINYINT,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT
+);
+CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRulesTest/rules.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeScopeNotNullableInRulesTest/rules.sql
new file mode 100644 (file)
index 0000000..1708db8
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE TABLE "RULES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "PLUGIN_KEY" VARCHAR(200),
+  "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+  "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+  "DESCRIPTION" VARCHAR(16777215),
+  "DESCRIPTION_FORMAT" VARCHAR(20),
+  "PRIORITY" INTEGER,
+  "IS_TEMPLATE" BOOLEAN DEFAULT FALSE,
+  "TEMPLATE_ID" INTEGER,
+  "PLUGIN_CONFIG_KEY" VARCHAR(200),
+  "NAME" VARCHAR(200),
+  "STATUS" VARCHAR(40),
+  "LANGUAGE" VARCHAR(20),
+  "SCOPE" VARCHAR(20),
+  "DEF_REMEDIATION_FUNCTION" VARCHAR(20),
+  "DEF_REMEDIATION_GAP_MULT" VARCHAR(20),
+  "DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20),
+  "GAP_DESCRIPTION" VARCHAR(4000),
+  "SYSTEM_TAGS" VARCHAR(4000),
+  "RULE_TYPE" TINYINT,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT
+);
+CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMainTest/rules.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetRuleScopeToMainTest/rules.sql
new file mode 100644 (file)
index 0000000..1708db8
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE TABLE "RULES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "PLUGIN_KEY" VARCHAR(200),
+  "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+  "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+  "DESCRIPTION" VARCHAR(16777215),
+  "DESCRIPTION_FORMAT" VARCHAR(20),
+  "PRIORITY" INTEGER,
+  "IS_TEMPLATE" BOOLEAN DEFAULT FALSE,
+  "TEMPLATE_ID" INTEGER,
+  "PLUGIN_CONFIG_KEY" VARCHAR(200),
+  "NAME" VARCHAR(200),
+  "STATUS" VARCHAR(40),
+  "LANGUAGE" VARCHAR(20),
+  "SCOPE" VARCHAR(20),
+  "DEF_REMEDIATION_FUNCTION" VARCHAR(20),
+  "DEF_REMEDIATION_GAP_MULT" VARCHAR(20),
+  "DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20),
+  "GAP_DESCRIPTION" VARCHAR(4000),
+  "SYSTEM_TAGS" VARCHAR(4000),
+  "RULE_TYPE" TINYINT,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT
+);
+CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY");
index dcea28c7cb1b7da9afcb15fe9f0e9c905b9db842..b41ec39163bb355b11cb08be5dee8687d65c54ca 100644 (file)
@@ -50,6 +50,7 @@ import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto.Format;
+import org.sonar.db.rule.RuleDto.Scope;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleRepositoryDto;
 import org.sonar.server.organization.OrganizationFlags;
@@ -226,6 +227,7 @@ public class RegisterRules implements Startable {
       .setGapDescription(ruleDef.gapDescription())
       .setSystemTags(ruleDef.tags())
       .setType(RuleType.valueOf(ruleDef.type().name()))
+      .setScope(toDtoScope(ruleDef.scope()))
       .setCreatedAt(system2.now())
       .setUpdatedAt(system2.now());
     if (ruleDef.htmlDescription() != null) {
@@ -240,6 +242,19 @@ public class RegisterRules implements Startable {
     return ruleDto;
   }
 
+  private static Scope toDtoScope(RulesDefinition.Scope scope) {
+    switch (scope) {
+      case ALL:
+        return Scope.ALL;
+      case MAIN:
+        return Scope.MAIN;
+      case TEST:
+        return Scope.TEST;
+      default:
+        throw new IllegalArgumentException("Unknown rule scope: " + scope);
+    }
+  }
+
   private boolean mergeRule(RulesDefinition.Rule def, RuleDefinitionDto dto) {
     boolean changed = false;
     if (!StringUtils.equals(dto.getName(), def.name())) {
@@ -271,6 +286,10 @@ public class RegisterRules implements Startable {
       dto.setStatus(def.status());
       changed = true;
     }
+    if (!StringUtils.equals(dto.getScope().name(), def.scope().name())) {
+      dto.setScope(toDtoScope(def.scope()));
+      changed = true;
+    }
     if (!StringUtils.equals(dto.getLanguage(), def.repository().language())) {
       dto.setLanguage(def.repository().language());
       changed = true;
index f6fcf657708860899637b2793ee84998da8ce8d0..9027791549f73f9d04917a2ab237f4d511a4f9aa 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleRepositoryDto;
+import org.sonar.db.rule.RuleDto.Scope;
 import org.sonar.server.language.LanguageTesting;
 import org.sonar.server.qualityprofile.QProfileComparison;
 import org.sonar.server.qualityprofile.QProfileName;
@@ -203,6 +204,7 @@ public class CompareActionTest {
       .setName(StringUtils.capitalize(id))
       .setLanguage(lang)
       .setSeverity(Severity.BLOCKER)
+      .setScope(Scope.MAIN)
       .setStatus(RuleStatus.READY);
     RuleDefinitionDto ruleDefinition = rule.getDefinition();
     db.ruleDao().insert(session, ruleDefinition);
index 6519fbc74c5e9460a2dadbff5a05648a96543449..3727eea253e94dfa007cc86e487e9144001eca2b 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDto.Scope;
 import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.organization.TestDefaultOrganizationProvider;
 
@@ -49,6 +50,7 @@ public class DefaultRuleFinderTest {
     .setRuleKey("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck")
     .setRepositoryKey("checkstyle")
     .setSeverity(4)
+    .setScope(Scope.MAIN)
     .setStatus(RuleStatus.READY);
 
   private RuleDto rule2 = new RuleDto()
@@ -57,6 +59,7 @@ public class DefaultRuleFinderTest {
     .setRuleKey("DisabledCheck")
     .setRepositoryKey("checkstyle")
     .setSeverity(4)
+    .setScope(Scope.MAIN)
     .setStatus(RuleStatus.REMOVED);
 
   private RuleDto rule3 = new RuleDto()
@@ -65,6 +68,7 @@ public class DefaultRuleFinderTest {
     .setRuleKey("com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck")
     .setRepositoryKey("checkstyle")
     .setSeverity(4)
+    .setScope(Scope.MAIN)
     .setStatus(RuleStatus.READY);
 
   private RuleDto rule4 = new RuleDto()
@@ -73,6 +77,7 @@ public class DefaultRuleFinderTest {
     .setRuleKey("CallSuperFirst")
     .setRepositoryKey("pmd")
     .setSeverity(2)
+    .setScope(Scope.MAIN)
     .setStatus(RuleStatus.READY);
 
   private DefaultRuleFinder underTest = new DefaultRuleFinder(dbClient, defaultOrganizationProvider);
index fe64919612f14a2344ac65a133a22874d1cfaffa..ba26b7075ebf9163852f72c69b7662055d0485fb 100644 (file)
@@ -42,6 +42,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDto.Scope;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleRepositoryDto;
 import org.sonar.server.es.EsTester;
@@ -121,6 +122,7 @@ public class RegisterRulesTest {
     assertThat(rule1.getConfigKey()).isEqualTo("config1");
     assertThat(rule1.getStatus()).isEqualTo(RuleStatus.BETA);
     assertThat(rule1.getCreatedAt()).isEqualTo(DATE1.getTime());
+    assertThat(rule1.getScope()).isEqualTo(Scope.ALL);
     assertThat(rule1.getUpdatedAt()).isEqualTo(DATE1.getTime());
     assertThat(rule1.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
     assertThat(rule1.getDefRemediationGapMultiplier()).isEqualTo("5d");
@@ -494,6 +496,7 @@ public class RegisterRulesTest {
       .setRuleKey("rule1")
       .setRepositoryKey("findbugs")
       .setName("Rule One")
+      .setScope(Scope.ALL)
       .setDescription("Rule one description")
       .setDescriptionFormat(RuleDto.Format.HTML)
       .setSystemTags(newHashSet("tag1", "tag2")));
@@ -562,6 +565,7 @@ public class RegisterRulesTest {
         .setSeverity(BLOCKER)
         .setInternalKey("config1")
         .setTags("tag1", "tag2", "tag3")
+        .setScope(Scope.ALL)
         .setType(RuleType.CODE_SMELL)
         .setStatus(RuleStatus.BETA)
         .setGapDescription("squid.S115.effortToFix");
index ac26dc44b5beb600359bbeb8a84cca0ee50e633d..8c791c43a52788b783dfae4e3340a10a54da2329 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleTesting;
+import org.sonar.db.rule.RuleDto.Scope;
 import org.sonar.server.es.EsTester;
 
 import static com.google.common.collect.Sets.newHashSet;
@@ -66,6 +67,7 @@ public class RuleIndexerTest {
     .setIsTemplate(true)
     .setSystemTags(newHashSet("cwe"))
     .setType(RuleType.BUG)
+    .setScope(Scope.ALL)
     .setCreatedAt(1500000000000L)
     .setUpdatedAt(1600000000000L);
 
index d914cc3a7766f599583254c6ed011b0d5fa353f5..5af7683e24730e14e78fe4fb662076b0568986c9 100644 (file)
@@ -4,7 +4,8 @@
          name="Avoid Cycles"
          plugin_rule_key="AvoidCycles"
          plugin_config_key="[null]"
-         plugin_name="squid"/>
+         plugin_name="squid"
+         scope="MAIN"/>
 
   <projects organization_uuid="org1"
             uuid="PROJECT"
index cfd138bcab1f91ad18a024f2ac3003be5c306a98..bd3b3d209ad0626f2b9785a82e2fcb20eb1678e1 100644 (file)
@@ -4,7 +4,8 @@
          name="Avoid Cycles"
          plugin_rule_key="AvoidCycles"
          plugin_config_key="[null]"
-         plugin_name="squid"/>
+         plugin_name="squid"
+         scope="MAIN"/>
 
   <projects organization_uuid="org1"
             uuid="PROJECT"
index 7f9dd0ba1e0ca1bb7cda5a79516d148bd11a4d8a..d7ab806a6208e3d6c43085d4d54a1e2ba1efb4d3 100644 (file)
@@ -4,7 +4,8 @@
          name="Avoid Cycles"
          plugin_rule_key="AvoidCycles"
          plugin_config_key="[null]"
-         plugin_name="squid"/>
+         plugin_name="squid"
+         scope="MAIN"/>
 
   <!-- Project 1 -->
   <projects organization_uuid="org1"
index aba2b242e359390a5e732e47a5a940f03fa0f519..4b3c283601bc40d25baa6640e688a28065d495f0 100644 (file)
@@ -4,7 +4,8 @@
          name="Avoid Cycles"
          plugin_rule_key="AvoidCycles"
          plugin_config_key="[null]"
-         plugin_name="squid"/>
+         plugin_name="squid"
+         scope="MAIN"/>
 
   <projects organization_uuid="org1"
             uuid="PROJECT1"
index 9b1d90ed52d60cd8aa1d26d91274d0932308923f..cefe5342d78f5d4f05aa8d393c12b18e4860bb87 100644 (file)
@@ -4,7 +4,8 @@
          name="Avoid Cycles"
          plugin_rule_key="AvoidCycles"
          plugin_config_key="[null]"
-         plugin_name="squid"/>
+         plugin_name="squid"
+         scope="MAIN"/>
 
   <projects organization_uuid="org1"
             uuid="PROJECT1"
index f43e80a4c8ff099cacd8499b7efbfd3998210f22..2607915bf612489720ba38b77f4e3ca62ce3ce59 100644 (file)
@@ -44,6 +44,7 @@
          description_format="MARKDOWN"
          created_at="150000"
          updated_at="150000"
+         scope="MAIN"
   />
 
   <properties id="1"