diff options
author | Dejan Milisavljevic <dejan.milisavljevic@sonarsource.com> | 2024-10-18 15:49:43 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-18 20:03:11 +0000 |
commit | 3a6c69b9a1c0280bfe735ceb133add2f318fba9c (patch) | |
tree | 1bfb7afadf1ca86cfccd9347f8ad51cf3b9738c9 /server/sonar-webserver-webapi | |
parent | d914719cc873a630ea09450614c8f190a7de4abc (diff) | |
download | sonarqube-3a6c69b9a1c0280bfe735ceb133add2f318fba9c.tar.gz sonarqube-3a6c69b9a1c0280bfe735ceb133add2f318fba9c.zip |
SONAR-23191 Add filtering to changelog API (#12101)
Diffstat (limited to 'server/sonar-webserver-webapi')
3 files changed, 291 insertions, 22 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/ChangelogActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/ChangelogActionIT.java index ac4918b6b0f..184da7022de 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/ChangelogActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/ChangelogActionIT.java @@ -26,8 +26,8 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import javax.annotation.Nullable; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; @@ -59,26 +59,27 @@ import static org.sonar.api.rules.CleanCodeAttribute.COMPLETE; import static org.sonar.api.rules.CleanCodeAttribute.FOCUSED; import static org.sonar.api.rules.CleanCodeAttribute.TESTED; import static org.sonar.test.JsonAssert.assertJson; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FILTER_MODE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SINCE; -public class ChangelogActionIT { +class ChangelogActionIT { private static final String DATE = "2011-04-25T01:15:42+0100"; private final TestSystem2 system2 = new TestSystem2().setNow(DateUtils.parseDateTime(DATE).getTime()); - @Rule - public DbTester db = DbTester.create(system2); - @Rule - public UserSessionRule userSession = UserSessionRule.standalone(); + @RegisterExtension + private final DbTester db = DbTester.create(system2); + @RegisterExtension + private final UserSessionRule userSession = UserSessionRule.standalone(); private final QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession); private final WsActionTester ws = new WsActionTester(new ChangelogAction(wsSupport, new Languages(), db.getDbClient())); @Test - public void return_change_with_all_fields() { + void return_change_with_all_fields() { QProfileDto profile = db.qualityProfiles().insert(); UserDto user = db.users().insertUser(); RuleDto rule = db.rules().insert(RuleKey.of("java", "S001")); @@ -146,7 +147,7 @@ public class ChangelogActionIT { } @Test - public void call_shouldReturnRemovedOrAddedImpacts() { + void call_shouldReturnRemovedOrAddedImpacts() { QProfileDto profile = db.qualityProfiles().insert(); UserDto user = db.users().insertUser(); RuleDto rule = db.rules().insert(RuleKey.of("java", "S001")); @@ -216,7 +217,7 @@ public class ChangelogActionIT { } @Test - public void call_whenNoChangeData_shouldIncludeRuleUuid() { + void call_whenNoChangeData_shouldIncludeRuleUuid() { String ruleUuid = "ruleUuid"; QProfileDto profile = db.qualityProfiles().insert(); UserDto user = db.users().insertUser(); @@ -234,7 +235,7 @@ public class ChangelogActionIT { } @Test - public void find_changelog_by_profile_key() { + void find_changelog_by_profile_key() { QProfileDto profile = db.qualityProfiles().insert(); RuleDto rule = db.rules().insert(); UserDto user = db.users().insertUser(); @@ -266,7 +267,7 @@ public class ChangelogActionIT { } @Test - public void find_changelog_by_language_and_name() { + void find_changelog_by_language_and_name() { QProfileDto qualityProfile = db.qualityProfiles().insert(); RuleDto rule = db.rules().insert(); UserDto user = db.users().insertUser(); @@ -298,7 +299,7 @@ public class ChangelogActionIT { } @Test - public void changelog_empty() { + void changelog_empty() { QProfileDto qualityProfile = db.qualityProfiles().insert(); String response = ws.newRequest() .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage()) @@ -310,7 +311,7 @@ public class ChangelogActionIT { } @Test - public void changelog_filter_by_since() { + void changelog_filter_by_since() { QProfileDto qualityProfile = db.qualityProfiles().insert(); system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime()); RuleDto rule = db.rules().insert(); @@ -348,7 +349,7 @@ public class ChangelogActionIT { } @Test - public void sort_changelog_events_in_reverse_chronological_order() { + void sort_changelog_events_in_reverse_chronological_order() { QProfileDto profile = db.qualityProfiles().insert(); system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime()); RuleDto rule1 = db.rules().insert(); @@ -392,7 +393,7 @@ public class ChangelogActionIT { } @Test - public void changelog_on_no_more_existing_rule() { + void changelog_on_no_more_existing_rule() { QProfileDto qualityProfile = db.qualityProfiles().insert(); UserDto user = db.users().insertUser(); insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user, @@ -417,7 +418,7 @@ public class ChangelogActionIT { } @Test - public void changelog_on_no_more_existing_user() { + void changelog_on_no_more_existing_user() { QProfileDto qualityProfile = db.qualityProfiles().insert(); RuleDto rule = db.rules().insert(); insertChange(c -> c.setRulesProfileUuid(qualityProfile.getRulesProfileUuid()) @@ -446,7 +447,235 @@ public class ChangelogActionIT { } @Test - public void changelog_example() { + void changelog_filter_by_STANDARD_mode() { + QProfileDto qualityProfile = db.qualityProfiles().insert(); + system2.setNow(DateUtils.parseDateTime(DATE).getTime()); + RuleDto rule = db.rules().insert(); + UserDto user = db.users().insertUser(); + //ACTIVATED and DEACTIVATED rules must always appear + insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user, Map.of("ruleUuid", rule.getUuid())); + insertChange(qualityProfile, ActiveRuleChange.Type.DEACTIVATED, user, Map.of("ruleUuid", rule.getUuid())); + // Changes with data must appear in STANDARD mode + insertChange(qualityProfile, ActiveRuleChange.Type.UPDATED, user, Map.of("severity", "BLOCKER", "ruleUuid", rule.getUuid())); + // Changes without data must not appear in STANDARD mode + RuleChangeDto ruleChange = insertRuleChange(TESTED, CLEAR, rule.getUuid(), + Set.of(new RuleImpactChangeDto(MAINTAINABILITY, SECURITY, HIGH, MEDIUM), new RuleImpactChangeDto(null, RELIABILITY, null, LOW))); + insertChange(qualityProfile, ActiveRuleChange.Type.UPDATED, user, null, ruleChange); + + String response = ws.newRequest() + .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage()) + .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName()) + .setParam(PARAM_FILTER_MODE, QProfileChangelogFilterMode.STANDARD.name()) + .execute() + .getInput(); + + assertJson(response).isSimilarTo(""" + { + "total": 3, + "p": 1, + "ps": 50, + "paging": { + "pageIndex": 1, + "pageSize": 50, + "total": 3 + }, + "events": [ + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "ACTIVATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": {} + }, + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "DEACTIVATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": {} + }, + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "UPDATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": { + "severity": "BLOCKER" + } + } + ] + } + """.formatted( + DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName(), + DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName(), + DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName())); + } + + @Test + void changelog_filter_by_MQR_mode() { + QProfileDto qualityProfile = db.qualityProfiles().insert(); + system2.setNow(DateUtils.parseDateTime(DATE).getTime()); + RuleDto rule = db.rules().insert(); + UserDto user = db.users().insertUser(); + //ACTIVATED and DEACTIVATED rules must always appear + insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user, Map.of("ruleUuid", rule.getUuid())); + insertChange(qualityProfile, ActiveRuleChange.Type.DEACTIVATED, user, Map.of("ruleUuid", rule.getUuid())); + // Changes without rule_change must not appear in MQR mode + insertChange(qualityProfile, ActiveRuleChange.Type.UPDATED, user, Map.of("severity", "BLOCKER", "ruleUuid", rule.getUuid())); + // Changes with param changes must appear in MQR mode + insertChange(qualityProfile, ActiveRuleChange.Type.UPDATED, user, Map.of("param_format", "^[A-Z][a-zA-Z0-9]*", "ruleUuid", + rule.getUuid())); + // Changes with rule_change must appear in MQR mode + RuleChangeDto ruleChange = insertRuleChange(TESTED, CLEAR, rule.getUuid(), + Set.of(new RuleImpactChangeDto(MAINTAINABILITY, SECURITY, HIGH, MEDIUM), new RuleImpactChangeDto(null, RELIABILITY, null, LOW))); + insertChange(qualityProfile, ActiveRuleChange.Type.UPDATED, user, null, ruleChange); + + String response = ws.newRequest() + .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage()) + .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName()) + .setParam(PARAM_FILTER_MODE, QProfileChangelogFilterMode.MQR.name()) + .execute() + .getInput(); + + assertJson(response).isSimilarTo(""" + { + "total": 4, + "p": 1, + "ps": 50, + "paging": { + "pageIndex": 1, + "pageSize": 50, + "total": 4 + }, + "events": [ + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "ACTIVATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": {} + }, + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "DEACTIVATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": {} + }, + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "UPDATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": { + "format": "^[A-Z][a-zA-Z0-9]*" + } + }, + { + "date": "%s", + "sonarQubeVersion": "7.6", + "action": "UPDATED", + "authorLogin": "%s", + "authorName": "%s", + "ruleKey": "%s", + "ruleName": "%s", + "cleanCodeAttributeCategory": "INTENTIONAL", + "impacts": [ + { + "softwareQuality": "MAINTAINABILITY", + "severity": "HIGH" + } + ], + "params": { + "oldCleanCodeAttribute": "TESTED", + "newCleanCodeAttribute": "CLEAR", + "oldCleanCodeAttributeCategory": "ADAPTABLE", + "newCleanCodeAttributeCategory": "INTENTIONAL", + "impactChanges": [ + { + "oldSoftwareQuality": "SECURITY", + "newSoftwareQuality": "MAINTAINABILITY", + "oldSeverity": "MEDIUM", + "newSeverity": "HIGH" + }, + { + "oldSoftwareQuality": "RELIABILITY", + "oldSeverity": "LOW" + } + ] + } + } + ] + } + """.formatted(DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName(), + DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName(), + DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName(), + DATE, user.getLogin(), user.getName(), rule.getKey(), rule.getName() + )); + } + + @Test + void changelog_example() { QProfileDto profile = db.qualityProfiles().insert(); String profileUuid = profile.getRulesProfileUuid(); @@ -487,13 +716,13 @@ public class ChangelogActionIT { } @Test - public void definition() { + void definition() { WebService.Action definition = ws.getDef(); assertThat(definition.isPost()).isFalse(); assertThat(definition.responseExampleAsString()).isNotEmpty(); assertThat(definition.params()).extracting(WebService.Param::key) - .containsExactlyInAnyOrder("qualityProfile", "language", "since", "to", "p", "ps"); + .containsExactlyInAnyOrder("qualityProfile", "language", "filterMode", "since", "to", "p", "ps"); WebService.Param profileName = definition.param("qualityProfile"); assertThat(profileName.deprecatedSince()).isNullOrEmpty(); WebService.Param language = definition.param("language"); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/ChangelogAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/ChangelogAction.java index 39127d20eb6..919bd4a00ad 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/ChangelogAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/ChangelogAction.java @@ -48,9 +48,13 @@ import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleImpactChangeDto; import org.sonar.db.user.UserDto; +import static java.lang.String.format; import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime; import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime; import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE; +import static org.sonar.server.qualityprofile.ws.QProfileChangelogFilterMode.MQR; +import static org.sonar.server.qualityprofile.ws.QProfileChangelogFilterMode.STANDARD; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FILTER_MODE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SINCE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TO; @@ -70,7 +74,8 @@ public class ChangelogAction implements QProfileWsAction { public void define(NewController context) { NewAction wsAction = context.createAction("changelog") .setSince("5.2") - .setDescription("Get the history of changes on a quality profile: rule activation/deactivation, change in parameters/severity. " + + .setDescription("Get the history of changes on a quality profile: rule activation/deactivation, change in " + + "parameters/severity/impacts. " + "Events are ordered by date in descending order (most recent first).") .setChangelog( new Change("9.8", "response fields 'total', 's', 'ps' have been deprecated, please use 'paging' object instead"), @@ -78,7 +83,8 @@ public class ChangelogAction implements QProfileWsAction { new Change("10.3", "Added fields 'cleanCodeAttributeCategory', 'impacts' to response"), new Change("10.3", "Added fields 'oldCleanCodeAttribute', 'newCleanCodeAttribute', 'oldCleanCodeAttributeCategory', " + "'newCleanCodeAttributeCategory' and 'impactChanges' to 'params' section of response"), - new Change("10.3", "Added field 'sonarQubeVersion' to 'params' section of response")) + new Change("10.3", "Added field 'sonarQubeVersion' to 'params' section of response"), + new Change("10.8", format("Added parameter '%s'", PARAM_FILTER_MODE))) .setHandler(this) .setResponseExample(getClass().getResource("changelog-example.json")); @@ -86,6 +92,14 @@ public class ChangelogAction implements QProfileWsAction { wsAction.addPagingParams(50, MAX_PAGE_SIZE); + wsAction.createParam(PARAM_FILTER_MODE) + .setDescription(format("If specified, will return changelog events related to %s or %s mode. " + + "If not specified, all the events are returned", MQR, STANDARD)) + .setRequired(false) + .setPossibleValues(QProfileChangelogFilterMode.values()) + .setSince("10.8") + .setExampleValue(MQR); + wsAction.createParam(PARAM_SINCE) .setDescription("Start date for the changelog (inclusive). <br>" + "Either a date (server timezone) or datetime can be provided.") @@ -116,6 +130,7 @@ public class ChangelogAction implements QProfileWsAction { int pageSize = request.mandatoryParamAsInt(Param.PAGE_SIZE); query.setPage(page, pageSize); + query.setFilterMode(request.param(PARAM_FILTER_MODE)); int total = dbClient.qProfileChangeDao().countByQuery(dbSession, query); List<QProfileChangeDto> changelogs = load(dbSession, query); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangelogFilterMode.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangelogFilterMode.java new file mode 100644 index 00000000000..d085bc4d15b --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangelogFilterMode.java @@ -0,0 +1,25 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.server.qualityprofile.ws; + +public enum QProfileChangelogFilterMode { + STANDARD, + MQR; +} |