]> source.dussan.org Git - sonarqube.git/blob
d3b81b9321832077a3a485e0238f9a2a2a84e13b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.scanner.externalissue;
21
22 import java.nio.file.Path;
23 import javax.annotation.Nullable;
24 import org.apache.commons.lang.StringUtils;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27 import org.sonar.api.scanner.ScannerSide;
28 import org.sonar.core.documentation.DocumentationLinkGenerator;
29
30 @ScannerSide
31 public class ExternalIssueReportValidator {
32   private static final Logger LOGGER = LoggerFactory.getLogger(ExternalIssueReportValidator.class);
33   private static final String RULE_ID = "ruleId";
34   private static final String SEVERITY = "severity";
35   private static final String TYPE = "type";
36   private static final String DOCUMENTATION_SUFFIX = "/analyzing-source-code/importing-external-issues/generic-issue-import-format/";
37   private final DocumentationLinkGenerator documentationLinkGenerator;
38
39   ExternalIssueReportValidator(DocumentationLinkGenerator documentationLinkGenerator) {
40     this.documentationLinkGenerator = documentationLinkGenerator;
41   }
42
43   public void validate(ExternalIssueReport report, Path reportPath) {
44     if (report.rules != null) {
45       checkNoField(report.issues, "issues", reportPath);
46       validateRules(report.rules, reportPath);
47     } else if (report.issues != null) {
48       String documentationLink = documentationLinkGenerator.getDocumentationLink(DOCUMENTATION_SUFFIX);
49       LOGGER.warn("External issues were imported with a deprecated format which will be removed soon. " +
50         "Please switch to the newest format to fully benefit from Clean Code: {}", documentationLink);
51       validateIssues(report.issues, reportPath);
52     } else {
53       throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field 'rules'", reportPath));
54     }
55   }
56
57   private static void validateIssues(ExternalIssueReport.Issue[] issues, Path reportPath) {
58     for (ExternalIssueReport.Issue issue : issues) {
59       mandatoryField(issue.ruleId, RULE_ID, reportPath);
60       mandatoryField(issue.severity, SEVERITY, reportPath);
61       mandatoryField(issue.type, TYPE, reportPath);
62       mandatoryField(issue.engineId, "engineId", reportPath);
63       validateAlwaysRequiredIssueFields(issue, reportPath);
64     }
65   }
66
67   private static void validateRules(ExternalIssueReport.Rule[] rules, Path reportPath) {
68     for (ExternalIssueReport.Rule rule : rules) {
69       mandatoryField(rule.ruleId, RULE_ID, reportPath);
70       mandatoryField(rule.name, "name", reportPath);
71       mandatoryField(rule.engineId, "engineId", reportPath);
72       mandatoryField(rule.cleanCodeAttribute, "cleanCodeAttribute", reportPath);
73       mandatoryArray(rule.impacts, "impacts", reportPath);
74       validateIssuesAsPartOfARule(rule.issues, reportPath);
75     }
76   }
77
78   private static void validateIssuesAsPartOfARule(ExternalIssueReport.Issue[] issues, Path reportPath) {
79     for (ExternalIssueReport.Issue issue : issues) {
80       validateAlwaysRequiredIssueFields(issue, reportPath);
81       checkNoField(issue.severity, SEVERITY, reportPath);
82       checkNoField(issue.type, TYPE, reportPath);
83       checkNoField(issue.ruleId, RULE_ID, reportPath);
84     }
85   }
86
87   private static void checkNoField(@Nullable Object value, String fieldName, Path reportPath) {
88     if (value != null) {
89       throw new IllegalStateException(String.format("Deprecated '%s' field found in the following report: '%s'.", fieldName, reportPath));
90     }
91   }
92
93   private static void validateAlwaysRequiredIssueFields(ExternalIssueReport.Issue issue, Path reportPath) {
94     mandatoryField(issue.primaryLocation, "primaryLocation", reportPath);
95     mandatoryFieldPrimaryLocation(issue.primaryLocation.filePath, "filePath", reportPath);
96     mandatoryFieldPrimaryLocation(issue.primaryLocation.message, "message", reportPath);
97
98     if (issue.primaryLocation.textRange != null) {
99       mandatoryFieldPrimaryLocation(issue.primaryLocation.textRange.startLine, "startLine of the text range", reportPath);
100     }
101
102     if (issue.secondaryLocations != null) {
103       for (ExternalIssueReport.Location l : issue.secondaryLocations) {
104         mandatoryFieldSecondaryLocation(l.filePath, "filePath", reportPath);
105         mandatoryFieldSecondaryLocation(l.textRange, "textRange", reportPath);
106         mandatoryFieldSecondaryLocation(l.textRange.startLine, "startLine of the text range", reportPath);
107       }
108     }
109   }
110
111   private static void mandatoryFieldPrimaryLocation(@Nullable Object value, String fieldName, Path reportPath) {
112     if (value == null) {
113       throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field '%s' in the primary location of" +
114         " the issue.", reportPath, fieldName));
115     }
116   }
117
118   private static void mandatoryFieldSecondaryLocation(@Nullable Object value, String fieldName, Path reportPath) {
119     if (value == null) {
120       throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field '%s' in a secondary location of" +
121         " the issue.", reportPath, fieldName));
122     }
123   }
124
125   private static void mandatoryField(@Nullable Object value, String fieldName, Path reportPath) {
126     if (value == null || (value instanceof String && ((String) value).isEmpty())) {
127       throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field '%s'.", reportPath, fieldName));
128     }
129   }
130
131   private static void mandatoryArray(@Nullable Object[] value, String fieldName, Path reportPath) {
132     mandatoryField(value, fieldName, reportPath);
133     if (value.length == 0) {
134       throw new IllegalStateException(String.format("Failed to parse report '%s': mandatory array '%s' not populated.", reportPath,
135         fieldName));
136     }
137   }
138
139   private static void mandatoryField(@Nullable String value, String fieldName, Path reportPath) {
140     if (StringUtils.isBlank(value)) {
141       throw new IllegalStateException(String.format("Failed to parse report '%s': missing mandatory field '%s'.", reportPath, fieldName));
142     }
143   }
144 }