From 84c3361acbf85b6d5d02ddee7d353f5491fbbd7f Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 19 May 2014 17:15:49 +0200 Subject: [PATCH] SONAR-5305 Return aggregation of rules and severities. Return snapshot date in periods --- .../org/sonar/core/issue/db/IssueDao.java | 21 ++++ .../org/sonar/core/issue/db/IssueMapper.java | 5 + .../org/sonar/core/issue/db/IssueMapper.xml | 22 ++++ .../sonar/core/issue/db/IssueStatsMapper.xml | 6 +- .../org/sonar/core/issue/db/IssueDaoTest.java | 18 ++++ .../IssueDaoTest/find_rules_by_component.xml | 80 ++++++++++++++ .../find_severities_by_component.xml | 100 +++++++++++++++++ .../core/issue/db/IssueDaoTest/shared.xml | 4 +- .../component/ws/ComponentAppAction.java | 43 +++++++- .../org/sonar/server/issue/IssueService.java | 48 +++++++-- .../sonar/server/issue/RulesAggregation.java | 102 ++++++++++++++++++ .../org/sonar/server/user/UserSession.java | 1 + .../ws/components-app-example-show.json | 46 +++++--- .../component/ws/ComponentAppActionTest.java | 82 +++++++++++++- .../server/component/ws/ComponentsWsTest.java | 3 +- .../sonar/server/issue/IssueServiceTest.java | 8 +- .../ws/ComponentAppActionTest/app.json | 19 +--- .../app_with_measures.json | 24 +++++ .../app_with_periods.json | 15 +++ .../app_with_rules.json | 15 +++ .../app_with_severities.json | 15 +++ 21 files changed, 622 insertions(+), 55 deletions(-) create mode 100644 sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml create mode 100644 sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java create mode 100644 sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java index f5662628586..44bfdb9de51 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java @@ -28,6 +28,7 @@ import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.issue.IssueQuery; import org.sonar.core.persistence.MyBatis; +import org.sonar.core.rule.RuleDto; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -138,4 +139,24 @@ public class IssueDao implements BatchComponent, ServerComponent { } return dtosList; } + + // TODO replace by aggregation in IssueIndex + public List findRulesByComponent(String componentKey) { + SqlSession session = mybatis.openSession(false); + try { + return session.getMapper(IssueMapper.class).findRulesByComponent(componentKey); + } finally { + MyBatis.closeQuietly(session); + } + } + + // TODO replace by aggregation in IssueIndex + public List findSeveritiesByComponent(String componentKey) { + SqlSession session = mybatis.openSession(false); + try { + return session.getMapper(IssueMapper.class).findSeveritiesByComponent(componentKey); + } finally { + MyBatis.closeQuietly(session); + } + } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java index 600b5e4f8c2..7204be2b572 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java @@ -21,6 +21,7 @@ package org.sonar.core.issue.db; import org.apache.ibatis.annotations.Param; import org.sonar.api.issue.IssueQuery; +import org.sonar.core.rule.RuleDto; import javax.annotation.Nullable; @@ -47,6 +48,10 @@ public interface IssueMapper { @Nullable @Param("userId") Integer userId, @Nullable @Param("role") String role); + List findRulesByComponent(String componentKey); + + List findSeveritiesByComponent(String componentKey); + void insert(IssueDto issue); int update(IssueDto issue); diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml index 785410c1ae2..3180427721c 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml @@ -314,5 +314,27 @@ + + + + diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml index 5369af45148..a721832a98a 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml @@ -5,12 +5,12 @@ - \ No newline at end of file + diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java index 196b10204e0..cf8dc4a3d3b 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java @@ -27,8 +27,10 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.issue.IssueQuery; import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; import org.sonar.api.utils.DateUtils; import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.rule.RuleDto; import java.util.List; @@ -428,6 +430,22 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(issue.getRootComponentKey()).isEqualTo("struts"); } + @Test + public void find_rules_by_component() { + setupData("shared", "find_rules_by_component"); + + List results = dao.findRulesByComponent("Action.java"); + assertThat(results).hasSize(3); + } + + @Test + public void find_severities_by_component() { + setupData("shared", "find_severities_by_component"); + + List results = dao.findSeveritiesByComponent("Action.java"); + assertThat(results).containsExactly(Severity.BLOCKER, Severity.MAJOR, Severity.BLOCKER); + } + private List getIssueIds(List issues) { return newArrayList(Iterables.transform(issues, new Function() { @Override diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml new file mode 100644 index 00000000000..d396550e94d --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml new file mode 100644 index 00000000000..fbadd1cb359 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/shared.xml index 1a7ef5f4246..fe24d237d6b 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/shared.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/shared.xml @@ -12,7 +12,7 @@ - - + + diff --git a/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java b/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java index c21ff1afdce..2535694121c 100644 --- a/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java +++ b/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java @@ -20,6 +20,7 @@ package org.sonar.server.component.ws; +import com.google.common.collect.Multiset; import com.google.common.io.Resources; import org.sonar.api.component.Component; import org.sonar.api.i18n.I18n; @@ -29,6 +30,7 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Durations; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; @@ -42,11 +44,14 @@ import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; import org.sonar.core.timemachine.Periods; import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.issue.IssueService; +import org.sonar.server.issue.RulesAggregation; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import java.util.Date; import java.util.List; public class ComponentAppAction implements RequestHandler { @@ -56,14 +61,16 @@ public class ComponentAppAction implements RequestHandler { private final ResourceDao resourceDao; private final MeasureDao measureDao; private final PropertiesDao propertiesDao; + private final IssueService issueService; private final Periods periods; private final Durations durations; private final I18n i18n; - public ComponentAppAction(ResourceDao resourceDao, MeasureDao measureDao, PropertiesDao propertiesDao, Periods periods, Durations durations, I18n i18n) { + public ComponentAppAction(ResourceDao resourceDao, MeasureDao measureDao, PropertiesDao propertiesDao, IssueService issueService, Periods periods, Durations durations, I18n i18n) { this.resourceDao = resourceDao; this.measureDao = measureDao; this.propertiesDao = propertiesDao; + this.issueService = issueService; this.periods = periods; this.durations = durations; this.i18n = i18n; @@ -121,6 +128,7 @@ public class ComponentAppAction implements RequestHandler { json.prop("fav", isFavourite); appendPeriods(json, projectId); + appendRulesAggregation(json, component.key()); appendMeasures(json, fileKey); } json.endObject(); @@ -149,9 +157,14 @@ public class ComponentAppAction implements RequestHandler { for (int i = 1; i <= 5; i++) { String mode = snapshotDto.getPeriodMode(i); if (mode != null) { - String label = periods.label(mode, snapshotDto.getPeriodModeParameter(i), snapshotDto.getPeriodDate(i)); + Date periodDate = snapshotDto.getPeriodDate(i); + String label = periods.label(mode, snapshotDto.getPeriodModeParameter(i), periodDate); if (label != null) { - json.beginObject().prop(Integer.toString(i), label).endObject(); + json.beginArray() + .value(i) + .value(label) + .value(periodDate != null ? DateUtils.formatDateTime(periodDate) : null) + .endArray(); } } } @@ -159,6 +172,30 @@ public class ComponentAppAction implements RequestHandler { json.endArray(); } + private void appendRulesAggregation(JsonWriter json, String componentKey) { + json.name("severities").beginArray(); + Multiset severities = issueService.findSeveritiesByComponent(componentKey); + for (String severity : severities.elementSet()) { + json.beginArray() + .value(severity) + .value(i18n.message(UserSession.get().locale(), "severity." + severity, null)) + .value(severities.count(severity)) + .endArray(); + } + json.endArray(); + + json.name("rules").beginArray(); + RulesAggregation rulesAggregation = issueService.findRulesByComponent(componentKey); + for (RulesAggregation.Rule rule : rulesAggregation.rules()) { + json.beginArray() + .value(rule.ruleKey().toString()) + .value(rule.name()) + .value(rulesAggregation.countRule(rule)) + .endArray(); + } + json.endArray(); + } + @CheckForNull private Component componentById(@Nullable Long componentId) { if (componentId != null) { diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index c15c3e10c1e..774bca951be 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -21,6 +21,8 @@ package org.sonar.server.issue; import com.google.common.base.Objects; import com.google.common.base.Strings; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; import org.apache.commons.lang.StringUtils; import org.sonar.api.ServerComponent; import org.sonar.api.issue.ActionPlan; @@ -39,6 +41,7 @@ import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssueBuilder; import org.sonar.core.issue.IssueNotifications; import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; import org.sonar.core.issue.db.IssueStorage; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; @@ -46,6 +49,7 @@ import org.sonar.core.preview.PreviewCache; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.rule.RuleDto; import org.sonar.core.user.AuthorizationDao; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.user.UserSession; @@ -67,21 +71,24 @@ public class IssueService implements ServerComponent { private final ActionPlanService actionPlanService; private final RuleFinder ruleFinder; private final ResourceDao resourceDao; + private final IssueDao issueDao; private final AuthorizationDao authorizationDao; private final UserFinder userFinder; private final PreviewCache dryRunCache; + public IssueService(DefaultIssueFinder finder, - IssueWorkflow workflow, - IssueStorage issueStorage, - IssueUpdater issueUpdater, - IssueNotifications issueNotifications, - ActionPlanService actionPlanService, - RuleFinder ruleFinder, - ResourceDao resourceDao, - AuthorizationDao authorizationDao, - UserFinder userFinder, - PreviewCache dryRunCache) { + IssueWorkflow workflow, + IssueStorage issueStorage, + IssueUpdater issueUpdater, + IssueNotifications issueNotifications, + ActionPlanService actionPlanService, + RuleFinder ruleFinder, + ResourceDao resourceDao, + IssueDao issueDao, + AuthorizationDao authorizationDao, + UserFinder userFinder, + PreviewCache dryRunCache) { this.finder = finder; this.workflow = workflow; this.issueStorage = issueStorage; @@ -90,6 +97,7 @@ public class IssueService implements ServerComponent { this.ruleFinder = ruleFinder; this.issueNotifications = issueNotifications; this.resourceDao = resourceDao; + this.issueDao = issueDao; this.authorizationDao = authorizationDao; this.userFinder = userFinder; this.dryRunCache = dryRunCache; @@ -243,7 +251,7 @@ public class IssueService implements ServerComponent { return issue; } - private Rule findRule (RuleKey ruleKey) { + private Rule findRule(RuleKey ruleKey) { Rule rule = ruleFinder.findByKey(ruleKey); if (rule == null) { throw new IllegalArgumentException("Unknown rule: " + ruleKey); @@ -271,4 +279,22 @@ public class IssueService implements ServerComponent { } } + // TODO result should be replaced by an aggregation object in IssueIndex + public RulesAggregation findRulesByComponent(String componentKey) { + RulesAggregation rulesAggregation = new RulesAggregation(); + for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey)) { + rulesAggregation.add(ruleDto); + } + return rulesAggregation; + } + + // TODO result should be replaced by an aggregation object in IssueIndex + public Multiset findSeveritiesByComponent(String componentKey) { + Multiset aggregation = HashMultiset.create(); + for (String severity : issueDao.findSeveritiesByComponent(componentKey)) { + aggregation.add(severity); + } + return aggregation; + } + } diff --git a/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java b/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java new file mode 100644 index 00000000000..433cfba56f7 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java @@ -0,0 +1,102 @@ +/* + * 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.issue; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import org.sonar.api.rule.RuleKey; +import org.sonar.core.rule.RuleDto; + +import java.util.Collection; + +public class RulesAggregation { + + private Multiset rules; + + public RulesAggregation() { + this.rules = HashMultiset.create(); + } + + public RulesAggregation add(RuleDto ruleDto) { + rules.add(new Rule().setRuleKey(ruleDto.getKey()).setName(ruleDto.getName())); + return this; + } + + public Collection rules() { + return rules.elementSet(); + } + + public int countRule(Rule rule) { + return rules.count(rule); + } + + public static class Rule { + + private RuleKey ruleKey; + private String name; + + public RuleKey ruleKey() { + return ruleKey; + } + + public Rule setRuleKey(RuleKey ruleKey) { + this.ruleKey = ruleKey; + return this; + } + + public String name() { + return name; + } + + public Rule setName(String name) { + this.name = name; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Rule rule = (Rule) o; + + if (!name.equals(rule.name)) { + return false; + } + if (!ruleKey.equals(rule.ruleKey)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = ruleKey.hashCode(); + result = 31 * result + name.hashCode(); + return result; + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/sonar-server/src/main/java/org/sonar/server/user/UserSession.java index a7c4ee00a4e..cf3ffe6b7b4 100644 --- a/sonar-server/src/main/java/org/sonar/server/user/UserSession.java +++ b/sonar-server/src/main/java/org/sonar/server/user/UserSession.java @@ -61,6 +61,7 @@ public class UserSession { List globalPermissions = null; HashMultimap projectKeyByPermission = HashMultimap.create(); + HashMultimap componentKeyByPermission = HashMultimap.create(); List projectPermissions = newArrayList(); UserSession() { diff --git a/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json b/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json index 83bf5229658..3ff72f9b0b0 100644 --- a/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json +++ b/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json @@ -7,20 +7,40 @@ "projectName": "SonarQube", "fav": true, "periods": [ - {"1" : "since previous analysis (May 08 2014)"}, - {"2" : "over 365 days (May 17 2013)"}, - {"3" : "since previous version (4.3 - Apr 17 2014)"} + [ + 1, + "since previous analysis (2014 May 08)", + "2014-05-08T23:40:12+0200" + ], + [ + 2, + "over 365 days (2013 May 17)", + "2013-05-17T23:52:45+0200" + ], + [ + 3, + "since previous version (4.3 - 2014 Apr 17)", + "2014-04-17T23:34:08+0200" + ] + ], + "severities": [ + [ + "INFO", + "Info", + 4 + ] + ], + "rules": [ + [ + "squid:S1133", + "Deprecated code should be removed eventually", + 4 + ] ], "measures": { - "fNcloc": "200", - "fCoverage": "95.4%", - "fDuplicationDensity": "7.4%", - "fDebt": "3d 2h", - "fIssues": "14", - "fBlockerIssues": "1", - "fCriticalIssues": "2", - "fMajorIssues": "5", - "fMinorIssues": "4", - "fInfoIssues": "2" + "fNcloc": "12", + "fDebt": "4h", + "fIssues": "4", + "fInfoIssues": "4" } } diff --git a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java index 4b1720ec673..24d5adc52f9 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java @@ -20,6 +20,8 @@ package org.sonar.server.component.ws; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,6 +29,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.i18n.I18n; import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; import org.sonar.api.web.UserRole; @@ -38,7 +41,10 @@ import org.sonar.core.properties.PropertyDto; import org.sonar.core.properties.PropertyQuery; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; +import org.sonar.core.rule.RuleDto; import org.sonar.core.timemachine.Periods; +import org.sonar.server.issue.IssueService; +import org.sonar.server.issue.RulesAggregation; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -47,6 +53,7 @@ import java.util.Locale; import static com.google.common.collect.Lists.newArrayList; import static org.mockito.Matchers.*; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -64,6 +71,9 @@ public class ComponentAppActionTest { @Mock PropertiesDao propertiesDao; + @Mock + IssueService issueService; + @Mock Periods periods; @@ -77,7 +87,10 @@ public class ComponentAppActionTest { @Before public void setUp() throws Exception { - tester = new WsTester(new ComponentsWs(new ComponentAppAction(resourceDao, measureDao, propertiesDao, periods, durations, i18n))); + when(issueService.findSeveritiesByComponent(COMPONENT_KEY)).thenReturn(mock(Multiset.class)); + when(issueService.findRulesByComponent(COMPONENT_KEY)).thenReturn(mock(RulesAggregation.class)); + + tester = new WsTester(new ComponentsWs(new ComponentAppAction(resourceDao, measureDao, propertiesDao, issueService, periods, durations, i18n))); } @Test @@ -91,6 +104,16 @@ public class ComponentAppActionTest { when(resourceDao.findById(1L)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); when(propertiesDao.selectByQuery(any(PropertyQuery.class))).thenReturn(newArrayList(new PropertyDto())); + WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); + request.execute().assertJson(getClass(), "app.json"); + } + + @Test + public void app_with_measures() throws Exception { + MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + + addProjectSample(); + addMeasure(CoreMetrics.NCLOC_KEY, 200); addMeasure(CoreMetrics.COVERAGE_KEY, 95.4); addMeasure(CoreMetrics.DUPLICATED_LINES_DENSITY_KEY, 7.4); @@ -104,19 +127,68 @@ public class ComponentAppActionTest { when(measureDao.findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.TECHNICAL_DEBT_KEY)).thenReturn(new MeasureDto().setValue(182.0)); when(durations.format(any(Locale.class), any(Duration.class), eq(Durations.DurationFormat.SHORT))).thenReturn("3h 2min"); - when(resourceDao.getLastSnapshotByResourceId(eq(1L))).thenReturn(new SnapshotDto().setPeriod1Mode("previous_analysis")); + WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); + request.execute().assertJson(getClass(), "app_with_measures.json"); + } + + @Test + public void app_with_periods() throws Exception { + MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + + addProjectSample(); + + when(resourceDao.getLastSnapshotByResourceId(eq(1L))).thenReturn( + new SnapshotDto().setPeriod1Mode("previous_analysis").setPeriod1Date(DateUtils.parseDate("2014-05-08")) + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); - request.execute().assertJson(getClass(), "app.json"); + request.execute().assertJson(getClass(), "app_with_periods.json"); + } + + @Test + public void app_with_severities() throws Exception { + MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + + addProjectSample(); + + Multiset severities = HashMultiset.create(); + severities.add("MAJOR", 5); + when(issueService.findSeveritiesByComponent(COMPONENT_KEY)).thenReturn(severities); + when(i18n.message(any(Locale.class), eq("severity.MAJOR"), isNull(String.class))).thenReturn("Major"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); + request.execute().assertJson(getClass(), "app_with_severities.json"); + } + + @Test + public void app_with_rules() throws Exception { + MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + + addProjectSample(); + when(issueService.findRulesByComponent(COMPONENT_KEY)).thenReturn( + new RulesAggregation().add(new RuleDto().setRuleKey("AvoidCycle").setRepositoryKey("squid").setName("Avoid Cycle")) + ); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); + request.execute().assertJson(getClass(), "app_with_rules.json"); + } + + private void addProjectSample() { + ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(COMPONENT_KEY).setName("Plugin.java") + .setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L); + when(resourceDao.selectComponentByKey(COMPONENT_KEY)).thenReturn(file); + when(resourceDao.findById(5L)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API")); + when(resourceDao.findById(1L)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); + } - private void addMeasure(String metricKey, Integer value){ + private void addMeasure(String metricKey, Integer value) { when(measureDao.findByComponentKeyAndMetricKey(COMPONENT_KEY, metricKey)).thenReturn(new MeasureDto().setValue(value.doubleValue())); when(i18n.formatInteger(any(Locale.class), eq(value.intValue()))).thenReturn(Integer.toString(value)); } - private void addMeasure(String metricKey, Double value){ + private void addMeasure(String metricKey, Double value) { when(measureDao.findByComponentKeyAndMetricKey(COMPONENT_KEY, metricKey)).thenReturn(new MeasureDto().setValue(value)); when(i18n.formatDouble(any(Locale.class), eq(value))).thenReturn(Double.toString(value)); } diff --git a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java index 6bd1d3e10e7..76f461113f4 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java @@ -30,6 +30,7 @@ import org.sonar.core.measure.db.MeasureDao; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.resource.ResourceDao; import org.sonar.core.timemachine.Periods; +import org.sonar.server.issue.IssueService; import org.sonar.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; @@ -42,7 +43,7 @@ public class ComponentsWsTest { @Before public void setUp() throws Exception { WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(ResourceDao.class), mock(MeasureDao.class), mock(PropertiesDao.class), - mock(Periods.class), mock(Durations.class), mock(I18n.class)))); + mock(IssueService.class), mock(Periods.class), mock(Durations.class), mock(I18n.class)))); controller = tester.controller("api/components"); } diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java index f62b7272acb..745bc149649 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java @@ -41,6 +41,7 @@ import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.IssueNotifications; import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; import org.sonar.core.issue.db.IssueStorage; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; @@ -92,6 +93,9 @@ public class IssueServiceTest { @Mock ResourceDao resourceDao; + @Mock + IssueDao issueDao; + @Mock AuthorizationDao authorizationDao; @@ -130,8 +134,8 @@ public class IssueServiceTest { when(resource.getKey()).thenReturn("org.sonar.Sample"); when(project.getKey()).thenReturn("Sample"); - issueService = new IssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, authorizationDao, userFinder, - mock(PreviewCache.class)); + issueService = new IssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, issueDao, + authorizationDao, userFinder, mock(PreviewCache.class)); } @Test diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json index e50f1e591bd..3b318935cb2 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json @@ -6,19 +6,8 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": true, - "periods": [ - {"1" : "since previous analysis (May 08 2014)"} - ], - "measures": { - "fNcloc": "200", - "fCoverage": "95.4%", - "fDuplicationDensity": "7.4%", - "fDebt": "3h 2min", - "fIssues": "14", - "fBlockerIssues": "1", - "fCriticalIssues": "2", - "fMajorIssues": "5", - "fMinorIssues": "4", - "fInfoIssues": "2" - } + "periods": [], + "severities": [], + "rules": [], + "measures": {} } diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json new file mode 100644 index 00000000000..ced3e77122f --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json @@ -0,0 +1,24 @@ +{ + "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java", + "path": "src/main/java/org/sonar/api/Plugin.java", + "name": "Plugin.java", + "q": "FIL", + "subProjectName": "SonarQube :: Plugin API", + "projectName": "SonarQube", + "fav": false, + "periods": [], + "severities": [], + "rules": [], + "measures": { + "fNcloc": "200", + "fCoverage": "95.4%", + "fDuplicationDensity": "7.4%", + "fDebt": "3h 2min", + "fIssues": "14", + "fBlockerIssues": "1", + "fCriticalIssues": "2", + "fMajorIssues": "5", + "fMinorIssues": "4", + "fInfoIssues": "2" + } +} diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json new file mode 100644 index 00000000000..1cdfe1d791a --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json @@ -0,0 +1,15 @@ +{ + "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java", + "path": "src/main/java/org/sonar/api/Plugin.java", + "name": "Plugin.java", + "q": "FIL", + "subProjectName": "SonarQube :: Plugin API", + "projectName": "SonarQube", + "fav": false, + "periods": [ + [1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"] + ], + "severities": [], + "rules": [], + "measures": {} +} diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json new file mode 100644 index 00000000000..9eb08aff8d6 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json @@ -0,0 +1,15 @@ +{ + "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java", + "path": "src/main/java/org/sonar/api/Plugin.java", + "name": "Plugin.java", + "q": "FIL", + "subProjectName": "SonarQube :: Plugin API", + "projectName": "SonarQube", + "fav": false, + "periods": [], + "severities": [], + "rules": [ + ["squid:AvoidCycle", "Avoid Cycle", 1] + ], + "measures": {} +} diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json new file mode 100644 index 00000000000..ce7fde78d5a --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json @@ -0,0 +1,15 @@ +{ + "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java", + "path": "src/main/java/org/sonar/api/Plugin.java", + "name": "Plugin.java", + "q": "FIL", + "subProjectName": "SonarQube :: Plugin API", + "projectName": "SonarQube", + "fav": false, + "periods": [], + "severities": [ + ["MAJOR", "Major", 5] + ], + "rules": [], + "measures": {} +} -- 2.39.5