From 4053336b49626d95e0f1917006038e3885c77be2 Mon Sep 17 00:00:00 2001
From: Elena Vilchik
Date: Fri, 29 Jun 2018 12:09:51 +0200
Subject: Factorize external report import (#100)
---
 pom.xml                                            |   2 +-
 .../org/sonar/css/plugin/CssRulesDefinition.java   |   8 +-
 .../java/org/sonar/css/plugin/SonarWayProfile.java |   2 +-
 .../sonar/css/plugin/StylelintReportSensor.java    | 112 ++++-----------------
 .../org/sonar/l10n/css/rules/stylelint/rules.json  |  42 +++++---
 5 files changed, 53 insertions(+), 113 deletions(-)
diff --git a/pom.xml b/pom.xml
index 9ee3a0c..e9fcce1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,7 +82,7 @@
         2.18.3
         2.5
         2.6.2
-        1.7.0.243
+        1.8.0.268
         1.22
         2.6
 
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRulesDefinition.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRulesDefinition.java
index 203dcb8..c03e261 100644
--- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRulesDefinition.java
+++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRulesDefinition.java
@@ -29,9 +29,9 @@ public class CssRulesDefinition implements RulesDefinition {
   public static final String REPOSITORY_KEY = "css";
   public static final String RULE_REPOSITORY_NAME = "SonarAnalyzer";
 
-  public static final String RESOURCE_FOLDER = "org/sonar/l10n/css/rules/css";
+  public static final String RESOURCE_FOLDER = "org/sonar/l10n/css/rules/";
 
-  private boolean externalIssuesSupported;
+  private final boolean externalIssuesSupported;
 
   public CssRulesDefinition(boolean externalIssuesSupported) {
     this.externalIssuesSupported = externalIssuesSupported;
@@ -43,12 +43,12 @@ public class CssRulesDefinition implements RulesDefinition {
       .createRepository(REPOSITORY_KEY, CssLanguage.KEY)
       .setName(RULE_REPOSITORY_NAME);
 
-    RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_FOLDER, PROFILE_PATH);
+    RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_FOLDER + REPOSITORY_KEY, PROFILE_PATH);
     ruleMetadataLoader.addRulesByAnnotatedClass(repository, CssRules.getRuleClasses());
     repository.done();
 
     if (externalIssuesSupported) {
-      StylelintReportSensor.createExternalRuleRepository(context);
+      StylelintReportSensor.getStylelintRuleLoader().createExternalRuleRepository(context);
     }
   }
 }
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/SonarWayProfile.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/SonarWayProfile.java
index 01f6d83..4784189 100644
--- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/SonarWayProfile.java
+++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/SonarWayProfile.java
@@ -28,7 +28,7 @@ import static org.sonar.css.plugin.CssRulesDefinition.RESOURCE_FOLDER;
 public class SonarWayProfile implements BuiltInQualityProfilesDefinition {
 
   public static final String PROFILE_NAME = "Sonar way";
-  public static final String PROFILE_PATH = RESOURCE_FOLDER + "/Sonar_way_profile.json";
+  public static final String PROFILE_PATH = RESOURCE_FOLDER + CssRulesDefinition.REPOSITORY_KEY + "/Sonar_way_profile.json";
 
   @Override
   public void define(Context context) {
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintReportSensor.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintReportSensor.java
index a2225b3..21e6aa2 100644
--- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintReportSensor.java
+++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintReportSensor.java
@@ -26,9 +26,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
 import javax.annotation.Nullable;
 import org.sonar.api.batch.fs.FilePredicates;
 import org.sonar.api.batch.fs.InputFile;
@@ -40,49 +38,30 @@ import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.api.batch.sensor.issue.NewExternalIssue;
 import org.sonar.api.batch.sensor.issue.NewIssueLocation;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleType;
-import org.sonar.api.server.rule.RulesDefinition.Context;
-import org.sonar.api.server.rule.RulesDefinition.NewRepository;
-import org.sonar.api.server.rule.RulesDefinition.NewRule;
-import org.sonar.api.utils.Version;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.css.plugin.StylelintReport.Issue;
 import org.sonar.css.plugin.StylelintReport.IssuesPerFile;
+import org.sonarsource.analyzer.commons.ExternalReportProvider;
+import org.sonarsource.analyzer.commons.ExternalRuleLoader;
+
+import static org.sonar.css.plugin.CssRulesDefinition.RESOURCE_FOLDER;
 
 public class StylelintReportSensor implements Sensor {
 
-  private static final Logger LOG = Loggers.get(StylelintReportSensor.class);
+  public static final String STYLELINT = "stylelint";
 
-  private static final String REPOSITORY = "stylelint";
 
+  private static final Logger LOG = Loggers.get(StylelintReportSensor.class);
   private static final long DEFAULT_REMEDIATION_COST = 5L;
   private static final Severity DEFAULT_SEVERITY = Severity.MAJOR;
   private static final String FILE_EXCEPTION_MESSAGE = "No issues information will be saved as the report file can't be read.";
 
-  private static final Set BUG_RULES = new HashSet<>(Arrays.asList(
-    "selector-type-no-unknown",
-    "no-invalid-double-slash-comments",
-    "no-descending-specificity",
-    "at-rule-no-unknown",
-    "selector-type-no-unknown",
-    "selector-pseudo-element-no-unknown",
-    "selector-pseudo-class-no-unknown",
-    "declaration-block-no-shorthand-property-overrides",
-    "declaration-block-no-duplicate-properties",
-    "keyframe-declaration-no-important",
-    "property-no-unknown",
-    "unit-no-unknown",
-    "function-linear-gradient-no-nonstandard-direction",
-    "function-calc-no-unspaced-operator",
-    "font-family-no-missing-generic-family-keyword",
-    "color-no-invalid-hex"
-  ));
-
   private final CssRules cssRules;
+  private ExternalRuleLoader stylelintRuleLoader = getStylelintRuleLoader();
 
   public StylelintReportSensor(CheckFactory checkFactory) {
-    cssRules = new CssRules(checkFactory);
+    this.cssRules = new CssRules(checkFactory);
   }
 
   @Override
@@ -94,22 +73,8 @@ public class StylelintReportSensor implements Sensor {
 
   @Override
   public void execute(SensorContext context) {
-    boolean externalIssuesSupported = context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2));
-    String[] reportPaths = context.config().getStringArray(CssPlugin.STYLELINT_REPORT_PATHS);
-
-    if (reportPaths.length == 0) {
-      return;
-    }
-
-    if (!externalIssuesSupported) {
-      LOG.error("Import of external issues requires SonarQube 7.2 or greater.");
-      return;
-    }
-
-    for (String reportPath : reportPaths) {
-      File report = getIOFile(context.fileSystem().baseDir(), reportPath);
-      importReport(report, context);
-    }
+    List reportFiles = ExternalReportProvider.getReportFiles(context, CssPlugin.STYLELINT_REPORT_PATHS);
+    reportFiles.forEach(report -> importReport(report, context));
   }
 
   private void importReport(File report, SensorContext context) {
@@ -162,58 +127,19 @@ public class StylelintReportSensor implements Sensor {
 
     newExternalIssue
       .at(primaryLocation)
-      .forRule(RuleKey.of(REPOSITORY, stylelintKey))
-      .type(ruleType(stylelintKey))
+      .forRule(RuleKey.of(STYLELINT, stylelintKey))
+      .type(stylelintRuleLoader.ruleType(stylelintKey))
       .severity(DEFAULT_SEVERITY)
       .remediationEffortMinutes(DEFAULT_REMEDIATION_COST)
       .save();
   }
 
-  private static RuleType ruleType(String stylelintKey) {
-    return BUG_RULES.contains(stylelintKey)
-      ? RuleType.BUG
-      : RuleType.CODE_SMELL;
+  public static ExternalRuleLoader getStylelintRuleLoader() {
+    return new ExternalRuleLoader(
+      StylelintReportSensor.STYLELINT,
+      StylelintReportSensor.STYLELINT,
+      RESOURCE_FOLDER + StylelintReportSensor.STYLELINT + "/rules.json",
+      CssLanguage.KEY);
   }
 
-  /**
-   * Returns a java.io.File for the given path.
-   * If path is not absolute, returns a File with module base directory as parent path.
-   */
-  private static File getIOFile(File baseDir, String path) {
-    File file = new File(path);
-    if (!file.isAbsolute()) {
-      file = new File(baseDir, path);
-    }
-
-    return file;
-  }
-
-  static void createExternalRuleRepository(Context context) {
-    NewRepository externalRepo = context.createExternalRepository(REPOSITORY, CssLanguage.KEY).setName(REPOSITORY);
-    String pathToRulesMeta = "org/sonar/l10n/css/rules/" + REPOSITORY + "/rules.json";
-    String description = "See the description of %s rule %s at %s website.";
-
-    try (InputStreamReader inputStreamReader = new InputStreamReader(StylelintReportSensor.class.getClassLoader().getResourceAsStream(pathToRulesMeta), StandardCharsets.UTF_8)) {
-      ExternalRule[] rules = new Gson().fromJson(inputStreamReader, ExternalRule[].class);
-      for (ExternalRule rule : rules) {
-        NewRule newRule = externalRepo.createRule(rule.key).setName(rule.name);
-        newRule.setHtmlDescription(String.format(description, REPOSITORY, rule.key, rule.url, REPOSITORY));
-        newRule.setDebtRemediationFunction(newRule.debtRemediationFunctions().constantPerIssue(DEFAULT_REMEDIATION_COST + "min"));
-        if (BUG_RULES.contains(rule.key)) {
-          newRule.setType(RuleType.BUG);
-        }
-      }
-
-    } catch (IOException e) {
-      throw new IllegalStateException("Can't read resource: " + pathToRulesMeta, e);
-    }
-
-    externalRepo.done();
-  }
-
-  private static class ExternalRule {
-    String url;
-    String key;
-    String name;
-  }
 }
diff --git a/sonar-css-plugin/src/main/resources/org/sonar/l10n/css/rules/stylelint/rules.json b/sonar-css-plugin/src/main/resources/org/sonar/l10n/css/rules/stylelint/rules.json
index 4cd1ee0..b927de5 100644
--- a/sonar-css-plugin/src/main/resources/org/sonar/l10n/css/rules/stylelint/rules.json
+++ b/sonar-css-plugin/src/main/resources/org/sonar/l10n/css/rules/stylelint/rules.json
@@ -2,7 +2,8 @@
   {
     "key": "color-no-invalid-hex",
     "url": "https://stylelint.io/user-guide/rules/color-no-invalid-hex/",
-    "name": "Disallow invalid hex colors"
+    "name": "Disallow invalid hex colors",
+    "type": "BUG"
   },
   {
     "key": "font-family-no-duplicate-names",
@@ -12,17 +13,20 @@
   {
     "key": "font-family-no-missing-generic-family-keyword",
     "url": "https://stylelint.io/user-guide/rules/font-family-no-missing-generic-family-keyword/",
-    "name": "Disallow missing generic families in lists of font family names"
+    "name": "Disallow missing generic families in lists of font family names",
+    "type": "BUG"
   },
   {
     "key": "function-calc-no-unspaced-operator",
     "url": "https://stylelint.io/user-guide/rules/function-calc-no-unspaced-operator/",
-    "name": "Disallow an unspaced operator within `calc` functions"
+    "name": "Disallow an unspaced operator within `calc` functions",
+    "type": "BUG"
   },
   {
     "key": "function-linear-gradient-no-nonstandard-direction",
     "url": "https://stylelint.io/user-guide/rules/function-linear-gradient-no-nonstandard-direction/",
-    "name": "Disallow direction values in `linear-gradient()` calls that are not valid according to the [standard syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient#Syntax)"
+    "name": "Disallow direction values in `linear-gradient()` calls that are not valid according to the [standard syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient#Syntax)",
+    "type": "BUG"
   },
   {
     "key": "string-no-newline",
@@ -32,27 +36,32 @@
   {
     "key": "unit-no-unknown",
     "url": "https://stylelint.io/user-guide/rules/unit-no-unknown/",
-    "name": "Disallow unknown units"
+    "name": "Disallow unknown units",
+    "type": "BUG"
   },
   {
     "key": "property-no-unknown",
     "url": "https://stylelint.io/user-guide/rules/property-no-unknown/",
-    "name": "Disallow unknown properties"
+    "name": "Disallow unknown properties",
+    "type": "BUG"
   },
   {
     "key": "keyframe-declaration-no-important",
     "url": "https://stylelint.io/user-guide/rules/keyframe-declaration-no-important/",
-    "name": "Disallow `!important` within keyframe declarations"
+    "name": "Disallow `!important` within keyframe declarations",
+    "type": "BUG"
   },
   {
     "key": "declaration-block-no-duplicate-properties",
     "url": "https://stylelint.io/user-guide/rules/declaration-block-no-duplicate-properties/",
-    "name": "Disallow duplicate properties within declaration blocks"
+    "name": "Disallow duplicate properties within declaration blocks",
+    "type": "BUG"
   },
   {
     "key": "declaration-block-no-shorthand-property-overrides",
     "url": "https://stylelint.io/user-guide/rules/declaration-block-no-shorthand-property-overrides/",
-    "name": "Disallow shorthand properties that override related longhand properties within declaration blocks"
+    "name": "Disallow shorthand properties that override related longhand properties within declaration blocks",
+    "type": "BUG"
   },
   {
     "key": "block-no-empty",
@@ -62,17 +71,20 @@
   {
     "key": "selector-pseudo-class-no-unknown",
     "url": "https://stylelint.io/user-guide/rules/selector-pseudo-class-no-unknown/",
-    "name": "Disallow unknown pseudo-class selectors"
+    "name": "Disallow unknown pseudo-class selectors",
+    "type": "BUG"
   },
   {
     "key": "selector-pseudo-element-no-unknown",
     "url": "https://stylelint.io/user-guide/rules/selector-pseudo-element-no-unknown/",
-    "name": "Disallow unknown pseudo-element selectors"
+    "name": "Disallow unknown pseudo-element selectors",
+    "type": "BUG"
   },
   {
     "key": "selector-type-no-unknown",
     "url": "https://stylelint.io/user-guide/rules/selector-type-no-unknown/",
-    "name": "Disallow unknown type selectors"
+    "name": "Disallow unknown type selectors",
+    "type": "BUG"
   },
   {
     "key": "media-feature-name-no-unknown",
@@ -82,7 +94,8 @@
   {
     "key": "at-rule-no-unknown",
     "url": "https://stylelint.io/user-guide/rules/at-rule-no-unknown/",
-    "name": "Disallow unknown at-rules"
+    "name": "Disallow unknown at-rules",
+    "type": "BUG"
   },
   {
     "key": "comment-no-empty",
@@ -117,7 +130,8 @@
   {
     "key": "no-invalid-double-slash-comments",
     "url": "https://stylelint.io/user-guide/rules/no-invalid-double-slash-comments/",
-    "name": "Disallow double-slash comments (`//...`) which are not supported by CSS"
+    "name": "Disallow double-slash comments (`//...`) which are not supported by CSS",
+    "type": "BUG"
   },
   {
     "key": "color-named",
-- 
cgit v1.2.3