
org.sonar.css.plugin.StylelintReportSensor Maven / Gradle / Ivy
The newest version!
/*
* SonarCSS
* Copyright (C) 2018-2021 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.css.plugin;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.input.BOMInputStream;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
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.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 {
public static final String STYLELINT = "stylelint";
private static final Logger LOG = Loggers.get(StylelintReportSensor.class);
private static final String FILE_EXCEPTION_MESSAGE = "No issues information will be saved as the report file can't be read.";
private static final ByteOrderMark[] BYTE_ORDER_MARKS = {ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE};
private final CssRules cssRules;
private ExternalRuleLoader stylelintRuleLoader = getStylelintRuleLoader();
public StylelintReportSensor(CheckFactory checkFactory) {
this.cssRules = new CssRules(checkFactory);
}
@Override
public void describe(SensorDescriptor descriptor) {
descriptor
.onlyOnLanguage(CssLanguage.KEY)
.onlyWhenConfiguration(conf -> conf.hasKey(CssPlugin.STYLELINT_REPORT_PATHS))
.name("Import of stylelint issues");
}
@Override
public void execute(SensorContext context) {
List reportFiles = ExternalReportProvider.getReportFiles(context, CssPlugin.STYLELINT_REPORT_PATHS);
reportFiles.forEach(report -> importReport(report, context));
}
private void importReport(File report, SensorContext context) {
LOG.info("Importing {}", report.getAbsoluteFile());
try (BOMInputStream bomInputStream = new BOMInputStream(Files.newInputStream(report.toPath()), BYTE_ORDER_MARKS)) {
String charsetName = bomInputStream.getBOMCharsetName();
if (charsetName == null) {
charsetName = StandardCharsets.UTF_8.name();
}
IssuesPerFile[] issues = new Gson().fromJson(new InputStreamReader(bomInputStream, charsetName), IssuesPerFile[].class);
for (IssuesPerFile issuesPerFile : issues) {
InputFile inputFile = getInputFile(context, issuesPerFile.source);
if (inputFile != null) {
for (Issue issue : issuesPerFile.warnings) {
saveStylelintIssue(context, issue, inputFile);
}
}
}
} catch (IOException e) {
LOG.error(FILE_EXCEPTION_MESSAGE, e);
} catch (JsonSyntaxException e) {
LOG.error("Failed to parse json stylelint report", e);
}
}
@Nullable
private static InputFile getInputFile(SensorContext context, String fileName) {
FilePredicates predicates = context.fileSystem().predicates();
InputFile inputFile = context.fileSystem().inputFile(predicates.or(predicates.hasRelativePath(fileName), predicates.hasAbsolutePath(fileName)));
if (inputFile == null) {
LOG.warn("No input file found for {}. No stylelint issues will be imported on this file.", fileName);
return null;
}
return inputFile;
}
private void saveStylelintIssue(SensorContext context, Issue issue, InputFile inputFile) {
String stylelintKey = issue.rule;
RuleKey sonarKey = cssRules.getActiveSonarKey(stylelintKey);
if (sonarKey != null) {
String message = "Stylelint issue for rule '{}' is skipped because this rule is activated in your SonarQube profile for CSS (rule key in SQ {})";
LOG.debug(message, stylelintKey, sonarKey.toString());
return;
}
NewExternalIssue newExternalIssue = context.newExternalIssue();
NewIssueLocation primaryLocation = newExternalIssue.newLocation()
.message(issue.text)
.on(inputFile)
.at(inputFile.selectLine(issue.line));
newExternalIssue
.at(primaryLocation)
.engineId(STYLELINT)
.ruleId(stylelintKey)
.type(stylelintRuleLoader.ruleType(stylelintKey))
.severity(stylelintRuleLoader.ruleSeverity(stylelintKey))
.remediationEffortMinutes(stylelintRuleLoader.ruleConstantDebtMinutes(stylelintKey))
.save();
}
public static ExternalRuleLoader getStylelintRuleLoader() {
return new ExternalRuleLoader(
StylelintReportSensor.STYLELINT,
StylelintReportSensor.STYLELINT,
RESOURCE_FOLDER + StylelintReportSensor.STYLELINT + "/rules.json",
CssLanguage.KEY);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy