From 9fa4c60191e799ee49609785f03278e2a5b8ab7e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Wed, 8 Apr 2015 16:19:44 +0200 Subject: [PATCH] SONAR-6306 Show rule stats on quality profile inheritance items --- .../ws/QProfileInheritanceAction.java | 79 ++++++++-- .../ws/example-inheritance.json | 35 +++++ .../QProfileInheritanceActionMediumTest.java | 136 ++++++++++++++++++ .../ws/QProfileInheritanceActionTest.java | 96 ------------- .../inheritance-buWide.json | 14 ++ .../inheritance-buWide.json | 10 -- 6 files changed, 251 insertions(+), 119 deletions(-) create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-inheritance.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest/inheritance-buWide.json delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest/inheritance-buWide.json diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceAction.java index 131215733b5..2212384869c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceAction.java @@ -19,6 +19,7 @@ */ package org.sonar.server.qualityprofile.ws; +import com.google.common.collect.Multimap; import org.sonar.api.resources.Languages; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; @@ -31,9 +32,12 @@ import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.qualityprofile.QProfile; import org.sonar.server.qualityprofile.QProfileFactory; +import org.sonar.server.qualityprofile.QProfileLoader; import org.sonar.server.qualityprofile.QProfileLookup; +import org.sonar.server.search.FacetValue; import java.util.List; +import java.util.Map; public class QProfileInheritanceAction implements BaseQProfileWsAction { @@ -41,13 +45,16 @@ public class QProfileInheritanceAction implements BaseQProfileWsAction { private final QProfileLookup profileLookup; + private final QProfileLoader profileLoader; + private final QProfileFactory profileFactory; private final Languages languages; - public QProfileInheritanceAction(DbClient dbClient, QProfileLookup profileLookup, QProfileFactory profileFactory, Languages languages) { + public QProfileInheritanceAction(DbClient dbClient, QProfileLookup profileLookup, QProfileLoader profileLoader, QProfileFactory profileFactory, Languages languages) { this.dbClient = dbClient; this.profileLookup = profileLookup; + this.profileLoader = profileLoader; this.profileFactory = profileFactory; this.languages = languages; } @@ -75,40 +82,86 @@ public class QProfileInheritanceAction implements BaseQProfileWsAction { List ancestors = profileLookup.ancestors(profile, session); List children = dbClient.qualityProfileDao().findChildren(session, profileKey); + Map> profileStats = profileLoader.getAllProfileStats(); - writeResponse(response.newJsonWriter(), ancestors, children); + writeResponse(response.newJsonWriter(), profile, ancestors, children, profileStats); } finally { session.close(); } } - private void writeResponse(JsonWriter json, List ancestors, List children) { + private void writeResponse(JsonWriter json, QualityProfileDto profile, List ancestors, List children, + Map> profileStats) { json.beginObject(); - writeAncestors(json, ancestors); - writeChildren(json, children); + writeProfile(json, profile, profileStats); + writeAncestors(json, ancestors, profileStats); + writeChildren(json, children, profileStats); json.endObject().close(); } - private void writeAncestors(JsonWriter json, List ancestors) { + private void writeProfile(JsonWriter json, QualityProfileDto profile, Map> profileStats) { + String profileKey = profile.getKey(); + json.name("profile").beginObject() + .prop("key", profileKey) + .prop("name", profile.getName()) + .prop("parent", profile.getParentKee()); + writeStats(json, profileKey, profileStats); + json.endObject(); + } + + private void writeAncestors(JsonWriter json, List ancestors, Map> profileStats) { json.name("ancestors").beginArray(); for (QProfile ancestor : ancestors) { + String ancestorKey = ancestor.key(); json.beginObject() - .prop("key", ancestor.key()) + .prop("key", ancestorKey) .prop("name", ancestor.name()) - .prop("parent", ancestor.parent()) - .endObject(); + .prop("parent", ancestor.parent()); + writeStats(json, ancestorKey, profileStats); + json.endObject(); } json.endArray(); } - private void writeChildren(JsonWriter json, List children) { + private void writeChildren(JsonWriter json, List children, Map> profileStats) { json.name("children").beginArray(); for (QualityProfileDto child : children) { + String childKey = child.getKey(); json.beginObject() - .prop("key", child.getKey()) - .prop("name", child.getName()) - .endObject(); + .prop("key", childKey) + .prop("name", child.getName()); + writeStats(json, childKey, profileStats); + json.endObject(); } json.endArray(); } + + private void writeStats(JsonWriter json, String profileKey, Map> profileStats) { + if (profileStats.containsKey(profileKey)) { + Multimap ancestorStats = profileStats.get(profileKey); + json.prop("activeRuleCount", getActiveRuleCount(ancestorStats)); + json.prop("overridingRuleCount", getOverridingRuleCount(ancestorStats)); + } + } + + private Long getActiveRuleCount(Multimap profileStats) { + Long result = null; + if (profileStats.containsKey("countActiveRules")) { + result = profileStats.get("countActiveRules").iterator().next().getValue(); + } + return result; + } + + private Long getOverridingRuleCount(Multimap profileStats) { + Long result = null; + if (profileStats.containsKey("inheritance")) { + for (FacetValue value : profileStats.get("inheritance")) { + if ("OVERRIDES".equals(value.getKey())) { + result = value.getValue(); + } + } + } + return result; + } + } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-inheritance.json b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-inheritance.json new file mode 100644 index 00000000000..743a4b7a18b --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-inheritance.json @@ -0,0 +1,35 @@ +{ + "profile": { + "key": "xoo-my-bu-profile-23456", + "name": "My BU Profile", + "parent": "xoo-my-company-profile-12345", + "activeRuleCount": 3, + "overridingRuleCount": 1 + }, + "ancestors": [ + { + "key": "xoo-my-company-profile-12345", + "name": "My Company Profile", + "parent": "xoo-my-group-profile-01234", + "activeRuleCount": 3 + }, + { + "key": "xoo-my-group-profile-01234", + "name": "My Group Profile", + "activeRuleCount": 2 + } + ], + "children": [ + { + "key": "xoo-for-project-one-34567", + "name": "For Project One", + "activeRuleCount": 5 + }, + { + "key": "xoo-for-project-two-45678", + "name": "For Project Two", + "activeRuleCount": 4, + "overridingRuleCount": 1 + } + ] +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest.java new file mode 100644 index 00000000000..0a6d0c823a7 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest.java @@ -0,0 +1,136 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rule.Severity; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; +import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.qualityprofile.QProfileName; +import org.sonar.server.qualityprofile.QProfileTesting; +import org.sonar.server.qualityprofile.RuleActivation; +import org.sonar.server.qualityprofile.RuleActivator; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.ws.WsTester; + +public class QProfileInheritanceActionMediumTest { + + @ClassRule + public static final ServerTester tester = new ServerTester(); + + private WsTester wsTester; + + private DbClient db; + + private DbSession session; + + @Before + public void setUp() throws Exception { + tester.clearDbAndIndexes(); + db = tester.get(DbClient.class); + session = db.openSession(false); + + wsTester = new WsTester(tester.get(QProfilesWs.class)); + } + + @After + public void tearDown() { + session.close(); + } + + @Test + public void inheritance_nominal() throws Exception { + RuleDto rule1 = createRule("xoo", "rule1"); + RuleDto rule2 = createRule("xoo", "rule2"); + RuleDto rule3 = createRule("xoo", "rule3"); + + /* + * groupWide (2) <- companyWide (2) <- buWide (2, 1 overriding) <- (forProject1 (2), forProject2 (2)) + */ + QualityProfileDto groupWide = createProfile("xoo", "My Group Profile", "xoo-my-group-profile-01234"); + createActiveRule(rule1, groupWide); + createActiveRule(rule2, groupWide); + session.commit(); + + QualityProfileDto companyWide = createProfile("xoo", "My Company Profile", "xoo-my-company-profile-12345"); + setParent(groupWide, companyWide); + + QualityProfileDto buWide = createProfile("xoo", "My BU Profile", "xoo-my-bu-profile-23456"); + setParent(companyWide, buWide); + overrideActiveRuleSeverity(rule1, buWide, Severity.CRITICAL); + + QualityProfileDto forProject1 = createProfile("xoo", "For Project One", "xoo-for-project-one-34567"); + setParent(buWide, forProject1); + createActiveRule(rule3, forProject1); + session.commit(); + + QualityProfileDto forProject2 = createProfile("xoo", "For Project Two", "xoo-for-project-two-45678"); + setParent(buWide, forProject2); + overrideActiveRuleSeverity(rule2, forProject2, Severity.CRITICAL); + + wsTester.newGetRequest("api/qualityprofiles", "inheritance").setParam("profileKey", buWide.getKee()).execute().assertJson(getClass(), "inheritance-buWide.json"); + } + + @Test(expected = NotFoundException.class) + public void fail_if_not_found() throws Exception { + wsTester.newGetRequest("api/qualityprofiles", "inheritance").setParam("profileKey", "polop").execute(); + } + + private QualityProfileDto createProfile(String lang, String name, String key) { + QualityProfileDto profile = QProfileTesting.newDto(new QProfileName(lang, name), key); + db.qualityProfileDao().insert(session, profile); + session.commit(); + return profile; + } + + private void setParent(QualityProfileDto profile, QualityProfileDto parent) { + tester.get(RuleActivator.class).setParent(parent.getKey(), profile.getKey()); + } + + private RuleDto createRule(String lang, String id) { + RuleDto rule = RuleDto.createFor(RuleKey.of("blah", id)) + .setLanguage(lang) + .setSeverity(Severity.BLOCKER) + .setStatus(RuleStatus.READY); + db.ruleDao().insert(session, rule); + return rule; + } + + private ActiveRuleDto createActiveRule(RuleDto rule, QualityProfileDto profile) { + ActiveRuleDto activeRule = ActiveRuleDto.createFor(profile, rule) + .setSeverity(rule.getSeverityString()); + db.activeRuleDao().insert(session, activeRule); + return activeRule; + } + + private void overrideActiveRuleSeverity(RuleDto rule, QualityProfileDto profile, String severity) { + tester.get(RuleActivator.class).activate(session, new RuleActivation(rule.getKey()).setSeverity(severity), profile.getKey()); + session.commit(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest.java deleted file mode 100644 index 4231d26966b..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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; - -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.sonar.api.utils.System2; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.DbTester; -import org.sonar.core.qualityprofile.db.QualityProfileDao; -import org.sonar.core.qualityprofile.db.QualityProfileDto; -import org.sonar.server.db.DbClient; -import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.language.LanguageTesting; -import org.sonar.server.qualityprofile.QProfileFactory; -import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonar.server.qualityprofile.QProfileName; -import org.sonar.server.qualityprofile.QProfileTesting; -import org.sonar.server.ws.WsTester; -import org.sonar.test.DbTests; - -import static org.mockito.Mockito.mock; - -@Category(DbTests.class) -public class QProfileInheritanceActionTest { - - @ClassRule - public static final DbTester dbTester = new DbTester(); - - private WsTester wsTester; - - private DbClient dbClient; - - private DbSession session; - - @Before - public void setUp() throws Exception { - dbTester.truncateTables(); - dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), - new QualityProfileDao(dbTester.myBatis(), mock(System2.class))); - session = dbClient.openSession(false); - - wsTester = new WsTester(new QProfilesWs( - mock(RuleActivationActions.class), - mock(BulkRuleActivationActions.class), - mock(ProjectAssociationActions.class), - new QProfileInheritanceAction(dbClient, new QProfileLookup(dbClient), new QProfileFactory(dbClient), LanguageTesting.newLanguages("xoo")))); - } - - @After - public void tearDown() { - session.close(); - } - - @Test - public void inheritance_nominal() throws Exception { - /* - * groupWide <- companyWide <- buWide <- (forProject1, forProject2) - */ - QualityProfileDto groupWide = QProfileTesting.newDto(QProfileName.createFor("xoo", "My Group Profile"), "xoo-my-group-profile-01234"); - QualityProfileDto companyWide = QProfileTesting.newDto(QProfileName.createFor("xoo", "My Company Profile"), "xoo-my-company-profile-12345").setParentKee(groupWide.getKee()); - QualityProfileDto buWide = QProfileTesting.newDto(QProfileName.createFor("xoo", "My BU Profile"), "xoo-my-by-profile-23456").setParentKee(companyWide.getKee()); - QualityProfileDto forProject1 = QProfileTesting.newDto(QProfileName.createFor("xoo", "For Project One"), "xoo-for-project-one-34567").setParentKee(buWide.getKee()); - QualityProfileDto forProject2 = QProfileTesting.newDto(QProfileName.createFor("xoo", "For Project Two"), "xoo-for-project-two-45678").setParentKee(buWide.getKee()); - - dbClient.qualityProfileDao().insert(session, groupWide, companyWide, buWide, forProject1, forProject2); - session.commit(); - - wsTester.newGetRequest("api/qualityprofiles", "inheritance").setParam("profileKey", buWide.getKee()).execute().assertJson(getClass(), "inheritance-buWide.json"); - } - - @Test(expected = NotFoundException.class) - public void fail_if_not_found() throws Exception { - wsTester.newGetRequest("api/qualityprofiles", "inheritance").setParam("profileKey", "polop").execute(); - } -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest/inheritance-buWide.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest/inheritance-buWide.json new file mode 100644 index 00000000000..9e3707e5d3f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionMediumTest/inheritance-buWide.json @@ -0,0 +1,14 @@ +{ + "profile": { + "key": "xoo-my-bu-profile-23456", "name": "My BU Profile", "parent": "xoo-my-company-profile-12345", + "activeRuleCount": 2, "overridingRuleCount": 1 + }, + "ancestors": [ + {"key": "xoo-my-company-profile-12345", "name": "My Company Profile", "parent": "xoo-my-group-profile-01234", "activeRuleCount": 2}, + {"key": "xoo-my-group-profile-01234", "name": "My Group Profile", "activeRuleCount": 2} + ], + "children": [ + {"key": "xoo-for-project-one-34567", "name": "For Project One", "activeRuleCount": 3}, + {"key": "xoo-for-project-two-45678", "name": "For Project Two", "activeRuleCount": 2, "overridingRuleCount": 1} + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest/inheritance-buWide.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest/inheritance-buWide.json deleted file mode 100644 index 154c25e3f13..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileInheritanceActionTest/inheritance-buWide.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ancestors": [ - {"key": "xoo-my-company-profile-12345", "name": "My Company Profile", "parent": "xoo-my-group-profile-01234"}, - {"key": "xoo-my-group-profile-01234", "name": "My Group Profile"} - ], - "children": [ - {"key": "xoo-for-project-one-34567", "name": "For Project One"}, - {"key": "xoo-for-project-two-45678", "name": "For Project Two"} - ] -} \ No newline at end of file -- 2.39.5