From 3696825ce64bdd9c9ddb36faf82d4d1f5be6cb36 Mon Sep 17 00:00:00 2001 From: Eric Giffon Date: Thu, 28 Sep 2023 14:59:34 +0200 Subject: [PATCH] SONAR-20547 Add CCT attributes in api/qualityprofiles/compare --- .../qualityprofile/ws/CompareActionIT.java | 48 +++++++++++++++++-- .../ws/CompareActionIT/compare_hotspot.json | 34 +++++++++++++ .../ws/CompareActionIT/compare_nominal.json | 38 +++++++++++++-- .../qualityprofile/ws/CompareAction.java | 33 ++++++++++--- .../qualityprofile/ws/compare-example.json | 31 ++++++++++-- 5 files changed, 168 insertions(+), 16 deletions(-) create mode 100644 server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_hotspot.json diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/CompareActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/CompareActionIT.java index 0e5274a4673..c038389293b 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/CompareActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/CompareActionIT.java @@ -22,16 +22,20 @@ package org.sonar.server.qualityprofile.ws; import org.apache.commons.lang.StringUtils; import org.junit.Rule; import org.junit.Test; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; +import org.sonar.api.rules.CleanCodeAttribute; +import org.sonar.api.rules.RuleType; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.server.ws.WebService; import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.issue.ImpactDto; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.QProfileDto; @@ -47,6 +51,7 @@ import org.sonar.server.ws.WsActionTester; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.sonar.api.issue.impact.Severity.HIGH; public class CompareActionIT { @@ -57,10 +62,10 @@ public class CompareActionIT { @Rule public UserSessionRule userSession = UserSessionRule.standalone(); - private DbClient dbClient = db.getDbClient(); - private DbSession session = db.getSession(); + private final DbClient dbClient = db.getDbClient(); + private final DbSession session = db.getSession(); - private WsActionTester ws = new WsActionTester( + private final WsActionTester ws = new WsActionTester( new CompareAction(db.getDbClient(), new QProfileComparison(db.getDbClient()), new Languages(LanguageTesting.newLanguage("xoo", "Xoo")))); @Test @@ -106,6 +111,26 @@ public class CompareActionIT { .execute().assertJson(this.getClass(), "compare_nominal.json"); } + @Test + public void compare_whenSecurityHotspot_shouldReturnEmptyCleanCodeInformation() { + createRepository("blah", "xoo", "Blah"); + + RuleDto rule1 = createSecurityHotspot("xoo", "rule1"); + RuleDto rule2 = createSecurityHotspot("xoo", "rule2"); + + QProfileDto profile1 = createProfile("xoo", "Profile 1", "xoo-profile-1-01234"); + createActiveRule(rule1, profile1); + + QProfileDto profile2 = createProfile("xoo", "Profile 2", "xoo-profile-2-12345"); + createActiveRule(rule2, profile2); + session.commit(); + + ws.newRequest() + .setParam("leftKey", profile1.getKee()) + .setParam("rightKey", profile2.getKee()) + .execute().assertJson(this.getClass(), "compare_hotspot.json"); + } + @Test public void compare_param_on_left() { RuleDto rule1 = createRuleWithParam("xoo", "rule1"); @@ -206,12 +231,27 @@ public class CompareActionIT { .setLanguage(lang) .setSeverity(Severity.BLOCKER) .setScope(Scope.MAIN) - .setStatus(RuleStatus.READY); + .setStatus(RuleStatus.READY) + .setCleanCodeAttribute(CleanCodeAttribute.EFFICIENT) + .addDefaultImpact(new ImpactDto(Uuids.createFast(), SoftwareQuality.RELIABILITY, HIGH)); RuleDto ruleDto = rule; dbClient.ruleDao().insert(session, ruleDto); return ruleDto; } + private RuleDto createSecurityHotspot(String lang, String id) { + RuleDto rule = createFor(RuleKey.of("blah", id)) + .setUuid(Uuids.createFast()) + .setName(StringUtils.capitalize(id)) + .setLanguage(lang) + .setSeverity(Severity.BLOCKER) + .setScope(Scope.MAIN) + .setStatus(RuleStatus.READY) + .setType(RuleType.SECURITY_HOTSPOT); + dbClient.ruleDao().insert(session, rule); + return rule; + } + private static RuleDto createFor(RuleKey key) { return new RuleDto() .setRepositoryKey(key.repository()) diff --git a/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_hotspot.json b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_hotspot.json new file mode 100644 index 00000000000..2135cb08f02 --- /dev/null +++ b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_hotspot.json @@ -0,0 +1,34 @@ +{ + "left": { + "key": "xoo-profile-1-01234", + "name": "Profile 1" + }, + "right": { + "key": "xoo-profile-2-12345", + "name": "Profile 2" + }, + "inLeft": [ + { + "key": "blah:rule1", + "name": "Rule1", + "pluginKey": "blah", + "pluginName": "Blah", + "languageKey": "xoo", + "languageName": "Xoo", + "impacts": [] + } + ], + "inRight": [ + { + "key": "blah:rule2", + "name": "Rule2", + "pluginKey": "blah", + "pluginName": "Blah", + "languageKey": "xoo", + "languageName": "Xoo", + "impacts": [] + } + ], + "modified": [], + "same": [] +} diff --git a/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_nominal.json b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_nominal.json index 1d0ef292292..d2885aac763 100644 --- a/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_nominal.json +++ b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/CompareActionIT/compare_nominal.json @@ -15,7 +15,13 @@ "languageKey": "xoo", "languageName": "Xoo", "name" : "Rule1", - "severity" : "BLOCKER" + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "RELIABILITY", + "severity": "HIGH" + } + ] } ], "inLeft" : [ @@ -26,7 +32,13 @@ "languageKey": "xoo", "languageName": "Xoo", "name" : "Rule2", - "severity" : "BLOCKER" + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "RELIABILITY", + "severity": "HIGH" + } + ] } ], "inRight" : [ @@ -37,7 +49,13 @@ "languageKey": "xoo", "languageName": "Xoo", "name" : "Rule3", - "severity" : "BLOCKER" + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "RELIABILITY", + "severity": "HIGH" + } + ] } ], "modified" : [ @@ -48,6 +66,13 @@ "languageKey": "xoo", "languageName": "Xoo", "name" : "Rule4", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "RELIABILITY", + "severity": "HIGH" + } + ], "left" : { "severity" : "BLOCKER", "params" : { @@ -68,6 +93,13 @@ "languageKey": "xoo", "languageName": "Xoo", "name" : "Rule5", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "RELIABILITY", + "severity": "HIGH" + } + ], "left" : { "severity" : "MINOR" }, diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/CompareAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/CompareAction.java index 1cc445fa9b3..f74db463ebb 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/CompareAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/CompareAction.java @@ -29,6 +29,8 @@ import javax.annotation.Nullable; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.CleanCodeAttribute; +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.NewAction; @@ -36,6 +38,7 @@ import org.sonar.api.server.ws.WebService.NewController; import org.sonar.api.utils.text.JsonWriter; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.issue.ImpactDto; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.db.rule.RuleDto; @@ -64,6 +67,10 @@ public class CompareAction implements QProfileWsAction { private static final String ATTRIBUTE_LANGUAGE_KEY = "languageKey"; private static final String ATTRIBUTE_LANGUAGE_NAME = "languageName"; private static final String ATTRIBUTE_PARAMS = "params"; + private static final String ATTRIBUTE_CLEAN_CODE_ATTRIBUTE_CATEGORY = "cleanCodeAttributeCategory"; + private static final String ATTRIBUTE_IMPACTS = "impacts"; + private static final String ATTRIBUTE_IMPACT_SOFTWARE_QUALITY = "softwareQuality"; + private static final String ATTRIBUTE_IMPACT_SEVERITY = "severity"; private static final String PARAM_LEFT_KEY = "leftKey"; private static final String PARAM_RIGHT_KEY = "rightKey"; @@ -85,7 +92,11 @@ public class CompareAction implements QProfileWsAction { .setHandler(this) .setInternal(true) .setResponseExample(getClass().getResource("compare-example.json")) - .setSince("5.2"); + .setSince("5.2") + .setChangelog( + new Change("10.3", String.format("Added '%s' and '%s' fields", ATTRIBUTE_CLEAN_CODE_ATTRIBUTE_CATEGORY, ATTRIBUTE_IMPACTS)), + new Change("10.3", String.format("Dropped '%s' field from '%s', '%s' and '%s' objects", + ATTRIBUTE_SEVERITY, ATTRIBUTE_SAME, ATTRIBUTE_IN_LEFT, ATTRIBUTE_IN_RIGHT))); compare.createParam(PARAM_LEFT_KEY) .setDescription("Profile key.") @@ -152,14 +163,10 @@ public class CompareAction implements QProfileWsAction { private void writeRules(JsonWriter json, Map activeRules, Map rulesByKey, Map repositoriesByKey) { json.beginArray(); - for (Entry activeRule : activeRules.entrySet()) { - RuleKey key = activeRule.getKey(); - ActiveRuleDto value = activeRule.getValue(); - + for (RuleKey key : activeRules.keySet()) { json.beginObject(); RuleDto rule = rulesByKey.get(key); writeRule(json, rule, repositoriesByKey.get(rule.getRepositoryKey())); - json.prop(ATTRIBUTE_SEVERITY, value.getSeverityString()); json.endObject(); } json.endArray(); @@ -179,6 +186,20 @@ public class CompareAction implements QProfileWsAction { json.prop(ATTRIBUTE_LANGUAGE_KEY, languageKey); json.prop(ATTRIBUTE_LANGUAGE_NAME, language == null ? null : language.getName()); } + + CleanCodeAttribute cleanCodeAttribute = rule.getCleanCodeAttribute(); + if (cleanCodeAttribute != null) { + json.prop(ATTRIBUTE_CLEAN_CODE_ATTRIBUTE_CATEGORY, cleanCodeAttribute.getAttributeCategory().toString()); + } + json.name(ATTRIBUTE_IMPACTS); + json.beginArray(); + for (ImpactDto impact : rule.getDefaultImpacts()) { + json.beginObject(); + json.prop(ATTRIBUTE_IMPACT_SOFTWARE_QUALITY, impact.getSoftwareQuality().toString()); + json.prop(ATTRIBUTE_IMPACT_SEVERITY, impact.getSeverity().toString()); + json.endObject(); + } + json.endArray(); } private void writeDifferences(JsonWriter json, Map modified, Map rulesByKey, diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualityprofile/ws/compare-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualityprofile/ws/compare-example.json index 82f84ee0446..18dcb2d7e64 100644 --- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualityprofile/ws/compare-example.json +++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualityprofile/ws/compare-example.json @@ -15,7 +15,13 @@ "languageKey": "js", "languageName": "JavaScript", "name" : "\"===\" and \"!==\" should be used instead of \"==\" and \"!=\"", - "severity" : "MAJOR" + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "RELIABILITY", + "severity": "MEDIUM" + } + ] } ], "inLeft" : [ @@ -26,7 +32,13 @@ "languageKey": "js", "languageName": "JavaScript", "name" : "Avoid trailing whitespaces", - "severity" : "MAJOR" + "cleanCodeAttributeCategory": "CONSISTENT", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "LOW" + } + ] } ], "inRight" : [ @@ -37,7 +49,13 @@ "languageKey": "js", "languageName": "JavaScript", "name" : "Avoid use of tabulation character", - "severity" : "MINOR" + "cleanCodeAttributeCategory": "CONSISTENT", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "LOW" + } + ] } ], "modified" : [ @@ -48,6 +66,13 @@ "languageKey": "js", "languageName": "JavaScript", "name" : "Avoid function with too many parameters", + "cleanCodeAttributeCategory": "ADAPTABLE", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], "right" : { "severity" : "MAJOR", "params" : { -- 2.39.5