public static final String FACET_OWASP_TOP_10 = "owaspTop10";
public static final String FACET_OWASP_TOP_10_2021 = "owaspTop10-2021";
public static final String FACET_SONARSOURCE_SECURITY = "sonarsourceSecurity";
+ public static final String FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY = "cleanCodeAttributeCategories";
+ public static final String FACET_IMPACT_SOFTWARE_QUALITY = "impactSoftwareQualities";
+ public static final String FACET_IMPACT_SEVERITY = "impactSeverities";
private static final int MAX_FACET_SIZE = 100;
.setParam("params", "regex=a.*")
.execute().getInput();
- assertJson(result).isSimilarTo("{\n" +
- " \"rule\": {\n" +
- " \"key\": \"java:MY_CUSTOM\",\n" +
- " \"repo\": \"java\",\n" +
- " \"name\": \"My custom rule\",\n" +
- " \"htmlDesc\": \"Description\",\n" +
- " \"severity\": \"MAJOR\",\n" +
- " \"status\": \"BETA\",\n" +
- " \"type\": \"BUG\",\n" +
- " \"internalKey\": \"configKey_S001\",\n" +
- " \"isTemplate\": false,\n" +
- " \"templateKey\": \"java:S001\",\n" +
- " \"sysTags\": [\"systag1\", \"systag2\"],\n" +
- " \"lang\": \"js\",\n" +
- " \"params\": [\n" +
- " {\n" +
- " \"key\": \"regex\",\n" +
- " \"htmlDesc\": \"Reg ex\",\n" +
- " \"defaultValue\": \"a.*\",\n" +
- " \"type\": \"STRING\"\n" +
- " }\n" +
- " ]\n" +
- " }\n" +
- "}\n");
+ String expetedResult = """
+ {
+ "rule": {
+ "key": "java:MY_CUSTOM",
+ "repo": "java",
+ "name": "My custom rule",
+ "htmlDesc": "Description",
+ "severity": "MAJOR",
+ "status": "BETA",
+ "type": "BUG",
+ "internalKey": "configKey_S001",
+ "isTemplate": false,
+ "templateKey": "java:S001",
+ "sysTags": [
+ "systag1",
+ "systag2"
+ ],
+ "lang": "js",
+ "params": [
+ {
+ "key": "regex",
+ "htmlDesc": "Reg ex",
+ "defaultValue": "a.*",
+ "type": "STRING"
+ }
+ ],
+ "cleanCodeAttribute": "CONVENTIONAL",
+ "cleanCodeAttributeCategory": "CONSISTENT",
+ "impacts": [
+ {
+ "softwareQuality": "RELIABILITY",
+ "severity": "MEDIUM"
+ }
+ ]
+ }
+ }
+ """;
+
+ assertJson(result).isSimilarTo(expetedResult);
}
@Test
import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection;
import static org.sonar.db.rule.RuleTesting.setSystemTags;
import static org.sonar.db.rule.RuleTesting.setTags;
+import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CLEAN_CODE_ATTRIBUTE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE;
r -> r.setName("Name"),
r -> r.setRepositoryKey("repo_key"),
r -> r.setSeverity("MINOR"),
- r -> r.setLanguage("java")
- );
+ r -> r.setLanguage("java"));
indexRules();
Rules.SearchResponse response = ws.newRequest().executeProtobuf(Rules.SearchResponse.class);
.containsExactly(rule.getTags().toArray(new String[0]));
}
+ @Test
+ public void returnRuleCleanCodeFields_whenEndpointIsCalled() {
+ RuleDto rule = db.rules()
+ .insert();
+ indexRules();
+
+ SearchResponse result = ws.newRequest()
+ .setParam(WebService.Param.FIELDS, FIELD_CLEAN_CODE_ATTRIBUTE)
+ .executeProtobuf(SearchResponse.class);
+
+ // mandatory fields
+ assertThat(result.getRulesList())
+ .extracting(r -> r.getImpacts().getImpactsList().stream().findFirst().orElseThrow(() -> new IllegalStateException("Impact is a mandatory field in the response.")))
+ .extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity)
+ .containsExactly(tuple(Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH));
+
+ // selected fields
+ assertThat(result.getRulesList()).extracting(Rule::getCleanCodeAttribute).containsExactly(Common.CleanCodeAttribute.CLEAR);
+ assertThat(result.getRulesList()).extracting(Rule::getCleanCodeAttributeCategory).containsExactly(Common.CleanCodeAttributeCategory.INTENTIONAL);
+ }
+
@Test
public void should_return_specified_fields() {
when(macroInterpreter.interpret(anyString())).thenAnswer(invocation -> invocation.getArgument(0));
checkField(rule, "gapDescription", Rule::getGapDescription, rule.getGapDescription());
checkDescriptionSections(rule, rule.getRuleDescriptionSectionDtos().stream()
.map(SearchActionIT::toProtobufDto)
- .collect(Collectors.toSet())
- );
+ .collect(Collectors.toSet()));
}
private RuleDescriptionSectionDto createRuleDescriptionSectionWithContext(String key, String content, @Nullable String contextKey) {
indexRules();
ws.newRequest()
- .setParam(WebService.Param.PAGE, "2")
- .setParam(WebService.Param.PAGE_SIZE, "9")
- .execute()
- .assertJson(this.getClass(), "paging.json");
+ .setParam(WebService.Param.PAGE, "2")
+ .setParam(WebService.Param.PAGE_SIZE, "9")
+ .execute()
+ .assertJson(this.getClass(), "paging.json");
}
@Test
assertThat(actualSections).hasSameElementsAs(expected);
}
-
private void verifyNoResults(Consumer<TestRequest> requestPopulator) {
verify(requestPopulator);
}
assertThat(resultRule.getParams().getParamsList())
.extracting(Rule.Param::getKey, Rule.Param::getHtmlDesc, Rule.Param::getDefaultValue)
.containsExactlyInAnyOrder(tuple(ruleParam.getName(), ruleParam.getDescription(), ruleParam.getDefaultValue()));
+ assertThat(resultRule.getImpacts().getImpactsList())
+ .extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity)
+ .containsExactly(tuple(Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH));
+
assertThat(resultRule.getEducationPrinciples().getEducationPrinciplesList()).containsExactlyElementsOf(rule.getEducationPrinciples());
}
.containsExactly(rule.getTags().toArray(new String[0]));
}
+ //<test case name>_when<conditionInCamelCase>_should<assertionInCamelCase>
+ @Test
+ public void returnRuleCleanCodeFields_whenEndpointIsCalled() {
+ RuleDto rule = db.rules().insert(setTags("tag1", "tag2"), r -> r.setNoteData(null).setNoteUserUuid(null));
+
+ ShowResponse result = ws.newRequest()
+ .setParam(PARAM_KEY, rule.getKey().toString())
+ .executeProtobuf(ShowResponse.class);
+
+ assertThat(result.getRule().getCleanCodeAttribute()).isEqualTo(Common.CleanCodeAttribute.CLEAR);
+ assertThat(result.getRule().getCleanCodeAttributeCategory()).isEqualTo(Common.CleanCodeAttributeCategory.INTENTIONAL);
+ }
+
@Test
public void show_rule_with_note_login() {
UserDto user = db.users().insertUser();
@Test
public void show_adhoc_rule() {
- //Ad-hoc description has no description sections defined
+ // Ad-hoc description has no description sections defined
RuleDto externalRule = db.rules().insert(newRuleWithoutDescriptionSection()
.setIsExternal(true)
.setIsAdHoc(true)
tuple(ASSESS_THE_PROBLEM_SECTION_KEY, "<div>This is not a problem</div>", "", ""),
tuple(HOW_TO_FIX_SECTION_KEY, "<div>I don't want to fix</div>", "", ""),
tuple(RESOURCES_SECTION_KEY, "<div>I want to fix with Spring</div>", section4context1.getContext().getKey(), section4context1.getContext().getDisplayName()),
- tuple(RESOURCES_SECTION_KEY, "<div>I want to fix with Servlet</div>", section4context2.getContext().getKey(), section4context2.getContext().getDisplayName())
- );
+ tuple(RESOURCES_SECTION_KEY, "<div>I want to fix with Servlet</div>", section4context2.getContext().getKey(), section4context2.getContext().getDisplayName()));
}
@Test
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsAction;
import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.Common;
import org.sonarqube.ws.Rules;
import static org.assertj.core.api.Assertions.assertThat;
.isInstanceOf(UnauthorizedException.class);
}
+ @Test
+ public void returnRuleCleanCodeFields_whenEndpointIsCalled() {
+ UserDto userAuthenticated = db.users().insertUser();
+ userSession.logIn(userAuthenticated).addPermission(ADMINISTER_QUALITY_PROFILES);
+
+ RuleDto rule = db.rules()
+ .insert();
+
+ Rules.UpdateResponse updateResponse = ws.newRequest().setMethod("POST")
+ .setParam("key", rule.getKey().toString())
+ .executeProtobuf(Rules.UpdateResponse.class);
+
+ // mandatory fields
+ assertThat(updateResponse.getRule())
+ .extracting(r -> r.getImpacts().getImpactsList().stream().findFirst().orElseThrow(() -> new IllegalStateException("Impact is a mandatory field in the response.")))
+ .extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity)
+ .containsExactly(Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH);
+
+ // selected fields
+ assertThat(updateResponse.getRule()).extracting(Rules.Rule::getCleanCodeAttribute).isEqualTo(Common.CleanCodeAttribute.CLEAR);
+ assertThat(updateResponse.getRule()).extracting(Rules.Rule::getCleanCodeAttributeCategory).isEqualTo(Common.CleanCodeAttributeCategory.INTENTIONAL);
+ }
+
private void logInAsQProfileAdministrator() {
userSession
.logIn()
.setResponseExample(Resources.getResource(getClass(), "create-example.json"))
.setSince("4.4")
.setChangelog(
+ new Change("5.5", "Creating manual rule is not more possible"),
new Change("10.0","Drop deprecated keys: 'custom_key', 'template_key', 'markdown_description', 'prevent_reactivation'"),
- new Change("5.5", "Creating manual rule is not more possible"))
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response")
+ )
.setHandler(this);
action
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDescriptionSectionContextDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.db.rule.RuleDto.Format.MARKDOWN;
+import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CLEAN_CODE_ATTRIBUTE;
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CREATED_AT;
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_DEBT_REM_FUNCTION;
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_DEFAULT_DEBT_REM_FUNCTION;
// Mandatory fields
ruleResponse.setKey(ruleDto.getKey().toString());
ruleResponse.setType(Common.RuleType.forNumber(ruleDto.getType()));
+ setImpacts(ruleResponse, ruleDto);
// Optional fields
setName(ruleResponse, ruleDto, fieldsToReturn);
setAdHocType(ruleResponse, ruleDto);
}
setEducationPrinciples(ruleResponse, ruleDto, fieldsToReturn);
+ setCleanCodeAttributes(ruleResponse, ruleDto, fieldsToReturn);
+
return ruleResponse;
}
+ private static void setImpacts(Rules.Rule.Builder ruleResponse, RuleDto ruleDto) {
+ Rules.Impacts.Builder impactsBuilder = Rules.Impacts.newBuilder();
+ ruleDto.getDefaultImpacts().forEach(impactDto -> impactsBuilder.addImpacts(toImpact(impactDto)));
+ ruleResponse.setImpacts(impactsBuilder.build());
+ }
+
+ private static Common.Impact toImpact(ImpactDto impactDto) {
+ Common.ImpactSeverity severity = Common.ImpactSeverity.valueOf(impactDto.getSeverity().name());
+ Common.SoftwareQuality softwareQuality = Common.SoftwareQuality.valueOf(impactDto.getSoftwareQuality().name());
+ return Common.Impact.newBuilder().setSeverity(severity).setSoftwareQuality(softwareQuality).build();
+ }
+
private static void setAdHocName(Rules.Rule.Builder ruleResponse, RuleDto ruleDto, Set<String> fieldsToReturn) {
String adHocName = ruleDto.getAdHocName();
if (adHocName != null && shouldReturnField(fieldsToReturn, FIELD_NAME)) {
}
}
+ private static void setCleanCodeAttributes(Rules.Rule.Builder ruleResponse, RuleDto ruleDto, Set<String> fieldsToReturn) {
+ if(shouldReturnField(fieldsToReturn, FIELD_CLEAN_CODE_ATTRIBUTE)){
+ ruleResponse.setCleanCodeAttribute(Common.CleanCodeAttribute.valueOf(ruleDto.getCleanCodeAttribute().name()));
+ ruleResponse.setCleanCodeAttributeCategory(Common.CleanCodeAttributeCategory.valueOf(ruleDto.getCleanCodeAttribute().getAttributeCategory().name()));
+ }
+ }
+
private static void setDeprecatedKeys(Rules.Rule.Builder ruleResponse, RuleDto ruleDto, Set<String> fieldsToReturn,
Map<String, List<DeprecatedRuleKeyDto>> deprecatedRuleKeysByRuleUuid) {
if (shouldReturnField(fieldsToReturn, FIELD_DEPRECATED_KEYS)) {
public static final String FIELD_NOTE_LOGIN = "noteLogin";
public static final String FIELD_MARKDOWN_NOTE = "mdNote";
public static final String FIELD_HTML_NOTE = "htmlNote";
+ public static final String FIELD_CLEAN_CODE_ATTRIBUTE = "cleanCodeAttribute";
/**
* Value for 'f' parameter which is used to return all the "defaultDebtRemFn" fields.
FIELD_MARKDOWN_DESCRIPTION, FIELD_DESCRIPTION_SECTIONS, FIELD_NOTE_LOGIN, FIELD_MARKDOWN_NOTE, FIELD_HTML_NOTE,
FIELD_DEFAULT_DEBT_REM_FUNCTION, FIELD_DEBT_REM_FUNCTION,
FIELD_DEFAULT_REM_FUNCTION, FIELD_GAP_DESCRIPTION, FIELD_REM_FUNCTION_OVERLOADED, FIELD_REM_FUNCTION,
- FIELD_PARAMS, FIELD_ACTIVES, FIELD_SCOPE, FIELD_DEPRECATED_KEYS, FIELD_EDUCATION_PRINCIPLES);
+ FIELD_PARAMS, FIELD_ACTIVES, FIELD_SCOPE, FIELD_DEPRECATED_KEYS, FIELD_EDUCATION_PRINCIPLES, FIELD_CLEAN_CODE_ATTRIBUTE);
private RulesWsParameters() {
// prevent instantiation
import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.rule.index.RuleIndex.ALL_STATUSES_EXCEPT_REMOVED;
import static org.sonar.server.rule.index.RuleIndex.FACET_ACTIVE_SEVERITIES;
+import static org.sonar.server.rule.index.RuleIndex.FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY;
import static org.sonar.server.rule.index.RuleIndex.FACET_CWE;
+import static org.sonar.server.rule.index.RuleIndex.FACET_IMPACT_SEVERITY;
+import static org.sonar.server.rule.index.RuleIndex.FACET_IMPACT_SOFTWARE_QUALITY;
import static org.sonar.server.rule.index.RuleIndex.FACET_LANGUAGES;
import static org.sonar.server.rule.index.RuleIndex.FACET_OLD_DEFAULT;
import static org.sonar.server.rule.index.RuleIndex.FACET_OWASP_TOP_10;
FACET_OWASP_TOP_10,
FACET_OWASP_TOP_10_2021,
FACET_SANS_TOP_25,
- FACET_SONARSOURCE_SECURITY};
+ FACET_SONARSOURCE_SECURITY,
+ FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY,
+ FACET_IMPACT_SEVERITY,
+ FACET_IMPACT_SOFTWARE_QUALITY
+ };
private final RuleQueryFactory ruleQueryFactory;
private final DbClient dbClient;
new Change("10.0", "The value 'defaultDebtRemFn' for the 'f' parameter has been deprecated, use 'defaultRemFn' instead"),
new Change("10.0", "The value 'sansTop25' for the parameter 'facets' has been deprecated"),
new Change("10.0", "Parameter 'sansTop25' is deprecated"),
- new Change("10.2", format("Parameters '%s', '%s', and '%s' are now deprecated.", PARAM_SEVERITIES, PARAM_TYPES, PARAM_ACTIVE_SEVERITIES)));
+ new Change("10.2", format("Parameters '%s', '%s', and '%s' are now deprecated.", PARAM_SEVERITIES, PARAM_TYPES, PARAM_ACTIVE_SEVERITIES)),
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
+ new Change("10.2", "The field 'cleanCodeAttribute' has been added to the 'f' parameter"),
+ new Change("10.2", format("add '%s', '%s' and '%s' to the 'facets' parameter.",FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY, FACET_IMPACT_SOFTWARE_QUALITY, FACET_IMPACT_SEVERITY))
+ );
action.createParam(FACETS)
.setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
new Change("10.0", "The deprecated field 'defaultDebtRemFnOffset' has been removed, use 'defaultRemFnBaseEffort' instead."),
new Change("10.0", "The deprecated field 'debtOverloaded' has been removed, use 'remFnOverloaded' instead."),
new Change("10.0", "The field 'defaultDebtRemFnType' has been deprecated, use 'defaultRemFnType' instead"),
- new Change("10.0", "The field 'debtRemFnType' has been deprecated, use 'remFnType' instead")
- );
+ new Change("10.0", "The field 'debtRemFnType' has been deprecated, use 'remFnType' instead"),
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"));
action
.createParam(PARAM_KEY)
import org.sonar.api.rule.Severity;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
.setDescription("Update an existing rule.<br>" +
"Requires the 'Administer Quality Profiles' permission")
.setSince("4.4")
+ .setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"))
.setHandler(this);
action.createParam(PARAM_KEY)
{
- "rule":{
- "key":"squid:forbidSonar",
- "repo":"squid",
- "name":"forbidSonar",
- "createdAt":"2018-06-06T16:04:28+0200",
- "htmlDesc":"Forbid classes with name starting with Sonar",
- "mdDesc":"Forbid classes with name starting with Sonar",
- "severity":"MAJOR",
- "status":"READY",
- "isTemplate":false,
- "templateKey":"squid:S3688",
- "sysTags":[],
- "lang":"java",
- "langName":"Java",
- "params":[
- {
- "key":"className",
- "htmlDesc":"Fully qualified name of the forbidden class. Use a regex to forbid a package.",
- "defaultValue":"**/Sonar*",
- "type":"STRING"
- }
+ "rule": {
+ "key": "squid:forbidSonar",
+ "repo": "squid",
+ "name": "forbidSonar",
+ "createdAt": "2018-06-06T16:04:28+0200",
+ "htmlDesc": "Forbid classes with name starting with Sonar",
+ "mdDesc": "Forbid classes with name starting with Sonar",
+ "severity": "MAJOR",
+ "status": "READY",
+ "isTemplate": false,
+ "templateKey": "squid:S3688",
+ "sysTags": [],
+ "lang": "java",
+ "langName": "Java",
+ "params": [
+ {
+ "key": "className",
+ "htmlDesc": "Fully qualified name of the forbidden class. Use a regex to forbid a package.",
+ "defaultValue": "**/Sonar*",
+ "type": "STRING"
+ }
],
- "scope":"MAIN",
- "isExternal":false,
- "type":"CODE_SMELL"
+ "scope": "MAIN",
+ "isExternal": false,
+ "type": "CODE_SMELL",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "MAINTAINABILITY",
+ "severity": "HIGH"
+ }
+ ]
}
}
\ No newline at end of file
"internalKey": "S1067",
"isTemplate": false,
"tags": [],
- "sysTags": ["brain-overload"],
+ "sysTags": [
+ "brain-overload"
+ ],
"lang": "java",
"langName": "Java",
"scope": "MAIN",
"isExternal": false,
"type": "CODE_SMELL",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "MAINTAINABILITY",
+ "severity": "HIGH"
+ }
+ ],
"descriptionSections": [
{
"key": "root_cause",
"internalKey": "ClassCyclomaticComplexity",
"isTemplate": false,
"tags": [],
- "sysTags": ["brain-overload"],
+ "sysTags": [
+ "brain-overload"
+ ],
"lang": "java",
"langName": "Java",
"scope": "MAIN",
"isExternal": false,
"type": "BUG",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "RELIABILITY",
+ "severity": "HIGH"
+ }
+ ],
"params": [
{
"key": "max",
"internalKey": "MethodCyclomaticComplexity",
"isTemplate": false,
"tags": [],
- "sysTags": ["brain-overload"],
+ "sysTags": [
+ "brain-overload"
+ ],
"lang": "java",
"langName": "Java",
"scope": "MAIN",
"isExternal": false,
"type": "VULNERABILITY",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"params": [
{
"key": "max",
"status": "READY",
"internalKey": "XPath",
"isTemplate": true,
- "tags": [ ],
- "sysTags": [ ],
+ "tags": [],
+ "sysTags": [],
"mdNote": "<p>\nThe tree produced by the <code>firstOf()</code> matcher is hard to work with from checks when alternatives are not named.\n</p>\n\n<p>\nConsider the following rule:\n</p>\n\n<pre>\nb.rule(COMPILATION_UNIT).is(\n b.firstOf( /* Non-Compliant */\n \"FOO\",\n \"BAR\"));\n</pre>\n\n<p>\nIf, from a check, one wants to forbid the usage of the \"BAR\" alternative,\nthe easiest option will be to verify that the value of the first token is \"BAR\",\ni.e. <code>\"BAR\".equals(compilationUnitNode.getTokenValue())</code>.\n</p>\n\n<p>\nThis is not maintainable, for at least two reasons:\n</p>\n\n<ul>\n <li>The grammar might evolve to also accept \"bar\" in lowercase, which will break <code>\"BAR\".equals(...)</code></li>\n <li>The grammar might evolve to optionally accept \"hello\" before the <code>firstOf()</code>, which will break <code>compilationUnitNode.getTokenValue()</code></li>\n</ul>\n\n<p>\nInstead, it is much better to rewrite the grammar as:\n</p>\n\n<pre>\nb.rule(COMPILATION_UNIT).is(\n firstOf( /* Compliant */\n FOO,\n BAR));\nb.rule(FOO).is(\"FOO\");\nb.rule(BAR).is(\"BAR\");\n</pre>\n\n<p>\nThe same check which forbids \"BAR\" would be written as: <code>compilationUnitNode.hasDirectChildren(BAR)</code>.\nThis allows both of the previous grammar evolutions to be made without impacting the check at all.\n</p>",
"htmlNote": "<p><br/>The tree produced by the <code>firstOf()</code> matcher is hard to work with from checks when alternatives are not named.<br/></p><br/><br/><p><br/>Consider the following rule:<br/></p><br/><br/><pre><br/>b.rule(COMPILATION_UNIT).is(<br/> b.firstOf( /* Non-Compliant */<br/> "FOO",<br/> "BAR"));<br/></pre><br/><br/><p><br/>If, from a check, one wants to forbid the usage of the "BAR" alternative,<br/>the easiest option will be to verify that the value of the first token is "BAR",<br/>i.e. <code>"BAR".equals(compilationUnitNode.getTokenValue())</code>.<br/></p><br/><br/><p><br/>This is not maintainable, for at least two reasons:<br/></p><br/><br/><ul><br/> <li>The grammar might evolve to also accept "bar" in lowercase, which will break <code>"BAR".equals(...)</code></li><br/> <li>The grammar might evolve to optionally accept "hello" before the <code>firstOf()</code>, which will break <code>compilationUnitNode.getTokenValue()</code></li><br/></ul><br/><br/><p><br/>Instead, it is much better to rewrite the grammar as:<br/></p><br/><br/><pre><br/>b.rule(COMPILATION_UNIT).is(<br/> firstOf( /* Compliant */<br/> FOO,<br/> BAR));<br/>b.rule(FOO).is("FOO");<br/>b.rule(BAR).is("BAR");<br/></pre><br/><br/><p><br/>The same check which forbids "BAR" would be written as: <code>compilationUnitNode.hasDirectChildren(BAR)</code>.<br/>This allows both of the previous grammar evolutions to be made without impacting the check at all.<br/></p>",
"noteLogin": "eric.hartmann",
"internalKey": "XPath",
"isTemplate": false,
"templateKey": "squid:XPath",
- "tags": [ ],
- "sysTags": [ ],
+ "tags": [],
+ "sysTags": [],
"lang": "java",
"langName": "Java",
"scope": "MAIN",
"isExternal": false,
"type": "CODE_SMELL",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "MAINTAINABILITY",
+ "severity": "HIGH"
+ }
+ ],
"params": [
{
"key": "xpathQuery",
}
]
}
-
]
}
-{"rule": {
+{
+ "rule": {
"key": "squid:ClassCyclomaticComplexity",
"repo": "squid",
"name": "Avoid too complex class",
"internalKey": "ClassCyclomaticComplexity",
"template": false,
"tags": [],
- "sysTags": ["brain-overload"],
+ "sysTags": [
+ "brain-overload"
+ ],
"remFnType": "LINEAR_OFFSET",
"remFnGapMultiplier": "5d",
"remFnBaseEffort": "10h",
"scope": "MAIN",
"isExternal": false,
"type": "CODE_SMELL",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "MAINTAINABILITY",
+ "severity": "HIGH"
+ }
+ ],
"descriptionSections": [
- {
- "key": "root_cause",
- "content": "<h3 class=\"page-title coding-rules-detail-header\"><big>Unnecessary imports should be removed</big></h3>"
- },
- {
- "key": "how_to_fix",
- "content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate Spring Security's CSRF protection. </li></ul>",
- "context": {
- "displayName": "Spring",
- "key": "spring"
- }
- },
- {
- "key": "how_to_fix",
- "content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate hibernate protection. </li></ul>",
- "context": {
- "displayName": "Hibernate",
- "key": "hibernate"
- }
+ {
+ "key": "root_cause",
+ "content": "<h3 class=\"page-title coding-rules-detail-header\"><big>Unnecessary imports should be removed</big></h3>"
+ },
+ {
+ "key": "how_to_fix",
+ "content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate Spring Security's CSRF protection. </li></ul>",
+ "context": {
+ "displayName": "Spring",
+ "key": "spring"
+ }
+ },
+ {
+ "key": "how_to_fix",
+ "content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate hibernate protection. </li></ul>",
+ "context": {
+ "displayName": "Hibernate",
+ "key": "hibernate"
}
+ }
],
"params": [
- {
- "key": "max",
- "desc": "Maximum complexity allowed.",
- "defaultValue": "200"
- }
+ {
+ "key": "max",
+ "desc": "Maximum complexity allowed.",
+ "defaultValue": "200"
+ }
]
-}, "actives": [
+ },
+ "actives": [
{
- "qProfile": "Sonar way with Findbugs:java",
- "inherit": "NONE",
- "severity": "MAJOR",
- "params": [
- {
- "key": "max",
- "value": "200"
- }
- ]
+ "qProfile": "Sonar way with Findbugs:java",
+ "inherit": "NONE",
+ "severity": "MAJOR",
+ "params": [
+ {
+ "key": "max",
+ "value": "200"
+ }
+ ]
},
{
- "qProfile": "Sonar way:java",
- "inherit": "NONE",
- "severity": "MAJOR",
- "params": [
- {
- "key": "max",
- "value": "200"
- }
- ]
+ "qProfile": "Sonar way:java",
+ "inherit": "NONE",
+ "severity": "MAJOR",
+ "params": [
+ {
+ "key": "max",
+ "value": "200"
+ }
+ ]
}
-]}
+ ]
+}
{
- "rule":{
- "key":"squid:forbidSonar",
- "repo":"squid",
- "name":"forbidSonar",
- "createdAt":"2018-06-06T16:09:09+0200",
- "htmlDesc":"Forbid classes with name starting with Sonar",
- "mdDesc":"Forbid classes with name starting with Sonar",
- "severity":"MAJOR",
- "status":"READY",
- "isTemplate":false,
- "templateKey":"squid:S3688",
- "tags":[],
- "sysTags":[],
- "lang":"java",
- "langName":"Java",
- "params":[
- {
- "key":"className",
- "htmlDesc":"Fully qualified name of the forbidden class. Use a regex to forbid a package.",
- "defaultValue":"**/Sonar*","type":"STRING"
- }
+ "rule": {
+ "key": "squid:forbidSonar",
+ "repo": "squid",
+ "name": "forbidSonar",
+ "createdAt": "2018-06-06T16:09:09+0200",
+ "htmlDesc": "Forbid classes with name starting with Sonar",
+ "mdDesc": "Forbid classes with name starting with Sonar",
+ "severity": "MAJOR",
+ "status": "READY",
+ "isTemplate": false,
+ "templateKey": "squid:S3688",
+ "tags": [],
+ "sysTags": [],
+ "lang": "java",
+ "langName": "Java",
+ "params": [
+ {
+ "key": "className",
+ "htmlDesc": "Fully qualified name of the forbidden class. Use a regex to forbid a package.",
+ "defaultValue": "**/Sonar*",
+ "type": "STRING"
+ }
],
- "remFnOverloaded":false,
- "scope":"MAIN",
- "isExternal":false,
- "type":"CODE_SMELL"
+ "remFnOverloaded": false,
+ "scope": "MAIN",
+ "isExternal": false,
+ "type": "CODE_SMELL",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "cleanCodeAttribute": "CLEAR",
+ "impacts": [
+ {
+ "softwareQuality": "MAINTAINABILITY",
+ "severity": "HIGH"
+ }
+ ]
}
}
\ No newline at end of file
// WS api/rules/search
message SearchResponse {
- optional int64 total = 1 [deprecated=true];
- optional int32 p = 2 [deprecated=true];
- optional int64 ps = 3 [deprecated=true];
+ optional int64 total = 1 [deprecated = true];
+ optional int32 p = 2 [deprecated = true];
+ optional int64 ps = 3 [deprecated = true];
repeated Rule rules = 4;
optional Actives actives = 5;
optional string repo = 2;
optional string name = 3;
optional string createdAt = 4;
- optional string htmlDesc = 5 [deprecated=true];
+ optional string htmlDesc = 5 [deprecated = true];
optional string htmlNote = 6;
optional string mdDesc = 7;
optional string mdNote = 8;
optional string noteLogin = 9;
- optional string severity = 10;
+ // Deprecated since 10.2, replace by impacts
+ optional string severity = 10 [deprecated = true];
optional sonarqube.ws.commons.RuleStatus status = 11;
optional string internalKey = 12;
optional bool isTemplate = 13;
optional string unusedDebtSubCharName = 28;
// Deprecated since 10.0, replaced by defaultRemFnType
- optional string defaultDebtRemFnType = 29 [deprecated=true];
+ optional string defaultDebtRemFnType = 29 [deprecated = true];
reserved 30;
reserved 31;
reserved 32;
reserved 33;
// Deprecated since 10.0, replaced by remFnType
- optional string debtRemFnType = 34 [deprecated=true];
+ optional string debtRemFnType = 34 [deprecated = true];
reserved 35;
reserved 36;
- optional sonarqube.ws.commons.RuleType type = 37;
+ // Deprecated since 10.2, replace by impacts
+ optional sonarqube.ws.commons.RuleType type = 37 [deprecated = true];
optional string defaultRemFnType = 38;
optional string defaultRemFnGapMultiplier = 39;
optional string defaultRemFnBaseEffort = 40;
optional DescriptionSections descriptionSections = 49;
optional EducationPrinciples educationPrinciples = 50;
optional string updatedAt = 51;
+ optional sonarqube.ws.commons.CleanCodeAttribute cleanCodeAttribute = 52;
+ optional sonarqube.ws.commons.CleanCodeAttributeCategory cleanCodeAttributeCategory = 53;
+ optional Impacts impacts = 54;
message DescriptionSections {
repeated DescriptionSection descriptionSections = 1;
}
}
+message Impacts{
+ repeated sonarqube.ws.commons.Impact impacts = 1;
+}
+
message DeprecatedKeys {
repeated string deprecatedKey = 1;
}