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;
}
return dtosList;
}
+
+ // TODO replace by aggregation in IssueIndex
+ public List<RuleDto> 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<String> findSeveritiesByComponent(String componentKey) {
+ SqlSession session = mybatis.openSession(false);
+ try {
+ return session.getMapper(IssueMapper.class).findSeveritiesByComponent(componentKey);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
}
import org.apache.ibatis.annotations.Param;
import org.sonar.api.issue.IssueQuery;
+import org.sonar.core.rule.RuleDto;
import javax.annotation.Nullable;
@Nullable @Param("userId") Integer userId, @Nullable @Param("role") String role);
+ List<RuleDto> findRulesByComponent(String componentKey);
+
+ List<String> findSeveritiesByComponent(String componentKey);
+
void insert(IssueDto issue);
int update(IssueDto issue);
</where>
</sql>
+ <select id="findRulesByComponent" parameterType="String" resultType="Rule">
+ SELECT <include refid="org.sonar.core.rule.RuleMapper.selectColumns" />
+ FROM issues i
+ INNER JOIN projects p on p.id=i.component_id
+ INNER JOIN rules r on r.id=i.rule_id
+ <where>
+ AND p.kee=#{componentKey}
+ AND i.resolution IS NULL
+ </where>
+ </select>
+
+ <select id="findSeveritiesByComponent" parameterType="String" resultType="String">
+ SELECT i.severity
+ FROM issues i
+ INNER JOIN projects on projects.id=i.component_id
+ INNER JOIN rules on rules.id=i.rule_id
+ <where>
+ AND projects.kee=#{componentKey}
+ AND i.resolution IS NULL
+ </where>
+ </select>
+
</mapper>
<mapper namespace="org.sonar.core.issue.db.IssueStatsMapper">
<select id="selectIssuesColumn" parameterType="map" resultType="Object">
- select
+ SELECT
<if test="'ASSIGNEE'.equals(column)">
i.assignee
</if>
- from issues i
+ FROM issues i
<include refid="org.sonar.core.issue.db.IssueMapper.selectQueryConditions"/>
</select>
-</mapper>
\ No newline at end of file
+</mapper>
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;
assertThat(issue.getRootComponentKey()).isEqualTo("struts");
}
+ @Test
+ public void find_rules_by_component() {
+ setupData("shared", "find_rules_by_component");
+
+ List<RuleDto> results = dao.findRulesByComponent("Action.java");
+ assertThat(results).hasSize(3);
+ }
+
+ @Test
+ public void find_severities_by_component() {
+ setupData("shared", "find_severities_by_component");
+
+ List<String> results = dao.findSeveritiesByComponent("Action.java");
+ assertThat(results).containsExactly(Severity.BLOCKER, Severity.MAJOR, Severity.BLOCKER);
+ }
+
private List<Long> getIssueIds(List<IssueDto> issues) {
return newArrayList(Iterables.transform(issues, new Function<IssueDto, Long>() {
@Override
--- /dev/null
+<dataset>
+
+ <!-- rule 500 -->
+ <issues
+ id="100"
+ kee="ABCDE-1"
+ component_id="401"
+ root_component_id="399"
+ rule_id="500"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ message="[null]"
+ line="200"
+ effort_to_fix="4.2"
+ status="OPEN"
+ resolution="[null]"
+ checksum="XXX"
+ reporter="arthur"
+ assignee="perceval"
+ author_login="[null]"
+ issue_attributes="JIRA=FOO-1234"
+ issue_creation_date="2013-04-16"
+ issue_update_date="2013-04-16"
+ issue_close_date="2013-04-16"
+ created_at="2013-04-16"
+ updated_at="2013-04-16"
+ />
+
+ <issues
+ id="101"
+ kee="ABCDE-2"
+ component_id="401"
+ root_component_id="399"
+ rule_id="500"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ message="[null]"
+ line="200"
+ effort_to_fix="4.2"
+ status="OPEN"
+ resolution="[null]"
+ checksum="XXX"
+ reporter="arthur"
+ assignee="perceval"
+ author_login="[null]"
+ issue_attributes="JIRA=FOO-1234"
+ issue_creation_date="2013-04-16"
+ issue_update_date="2013-04-16"
+ issue_close_date="2013-04-16"
+ created_at="2013-04-16"
+ updated_at="2013-04-16"
+ />
+
+
+ <!-- rule 501 -->
+ <issues
+ id="102"
+ kee="ABCDE-3"
+ component_id="401"
+ root_component_id="399"
+ rule_id="501"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ message="[null]"
+ line="200"
+ effort_to_fix="4.2"
+ status="OPEN"
+ resolution="[null]"
+ checksum="XXX"
+ reporter="arthur"
+ assignee="perceval"
+ author_login="[null]"
+ issue_attributes="JIRA=FOO-1234"
+ issue_creation_date="2013-04-16"
+ issue_update_date="2013-04-16"
+ issue_close_date="2013-04-16"
+ created_at="2013-04-16"
+ updated_at="2013-04-16"
+ />
+</dataset>
--- /dev/null
+<!--
+ ~ 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.
+ -->
+
+<dataset>
+
+ <!-- rule 500 -->
+ <issues
+ id="100"
+ kee="ABCDE-1"
+ component_id="401"
+ root_component_id="399"
+ rule_id="500"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ message="[null]"
+ line="200"
+ effort_to_fix="4.2"
+ status="OPEN"
+ resolution="[null]"
+ checksum="XXX"
+ reporter="arthur"
+ assignee="perceval"
+ author_login="[null]"
+ issue_attributes="JIRA=FOO-1234"
+ issue_creation_date="2013-04-16"
+ issue_update_date="2013-04-16"
+ issue_close_date="2013-04-16"
+ created_at="2013-04-16"
+ updated_at="2013-04-16"
+ />
+
+ <issues
+ id="101"
+ kee="ABCDE-2"
+ component_id="401"
+ root_component_id="399"
+ rule_id="500"
+ severity="MAJOR"
+ manual_severity="[false]"
+ message="[null]"
+ line="200"
+ effort_to_fix="4.2"
+ status="OPEN"
+ resolution="[null]"
+ checksum="XXX"
+ reporter="arthur"
+ assignee="perceval"
+ author_login="[null]"
+ issue_attributes="JIRA=FOO-1234"
+ issue_creation_date="2013-04-16"
+ issue_update_date="2013-04-16"
+ issue_close_date="2013-04-16"
+ created_at="2013-04-16"
+ updated_at="2013-04-16"
+ />
+
+
+ <!-- rule 501 -->
+ <issues
+ id="102"
+ kee="ABCDE-3"
+ component_id="401"
+ root_component_id="399"
+ rule_id="501"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ message="[null]"
+ line="200"
+ effort_to_fix="4.2"
+ status="OPEN"
+ resolution="[null]"
+ checksum="XXX"
+ reporter="arthur"
+ assignee="perceval"
+ author_login="[null]"
+ issue_attributes="JIRA=FOO-1234"
+ issue_creation_date="2013-04-16"
+ issue_update_date="2013-04-16"
+ issue_close_date="2013-04-16"
+ created_at="2013-04-16"
+ updated_at="2013-04-16"
+ />
+</dataset>
<snapshots id="102" project_id="401" root_snapshot_id="100" parent_snapshot_id="101" root_project_id="399" path="100.101." islast="[true]" />
<snapshots id="103" project_id="402" root_snapshot_id="100" parent_snapshot_id="101" root_project_id="399" path="100.101." islast="[true]" />
- <rules tags="[null]" system_tags="[null]" id="500" plugin_rule_key="AvoidCycle" plugin_name="squid" language="java" />
- <rules tags="[null]" system_tags="[null]" id="501" plugin_rule_key="NullRef" plugin_name="squid" language="xoo" />
+ <rules id="500" tags="[null]" system_tags="[null]" plugin_rule_key="AvoidCycle" plugin_name="squid" language="java" />
+ <rules id="501" tags="[null]" system_tags="[null]" plugin_rule_key="NullRef" plugin_name="squid" language="xoo" />
</dataset>
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;
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;
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 {
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;
json.prop("fav", isFavourite);
appendPeriods(json, projectId);
+ appendRulesAggregation(json, component.key());
appendMeasures(json, fileKey);
}
json.endObject();
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();
}
}
}
json.endArray();
}
+ private void appendRulesAggregation(JsonWriter json, String componentKey) {
+ json.name("severities").beginArray();
+ Multiset<String> 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) {
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;
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;
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;
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;
this.ruleFinder = ruleFinder;
this.issueNotifications = issueNotifications;
this.resourceDao = resourceDao;
+ this.issueDao = issueDao;
this.authorizationDao = authorizationDao;
this.userFinder = userFinder;
this.dryRunCache = dryRunCache;
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);
}
}
+ // 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<String> findSeveritiesByComponent(String componentKey) {
+ Multiset<String> aggregation = HashMultiset.create();
+ for (String severity : issueDao.findSeveritiesByComponent(componentKey)) {
+ aggregation.add(severity);
+ }
+ return aggregation;
+ }
+
}
--- /dev/null
+/*
+ * 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<Rule> 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<Rule> 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;
+ }
+ }
+}
List<String> globalPermissions = null;
HashMultimap<String, String> projectKeyByPermission = HashMultimap.create();
+ HashMultimap<String, String> componentKeyByPermission = HashMultimap.create();
List<String> projectPermissions = newArrayList();
UserSession() {
"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"
}
}
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;
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;
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;
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)
@Mock
PropertiesDao propertiesDao;
+ @Mock
+ IssueService issueService;
+
@Mock
Periods periods;
@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
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);
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<String> 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));
}
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;
@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");
}
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;
@Mock
ResourceDao resourceDao;
+ @Mock
+ IssueDao issueDao;
+
@Mock
AuthorizationDao authorizationDao;
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
"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": {}
}
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+{
+ "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": {}
+}
--- /dev/null
+{
+ "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": {}
+}
--- /dev/null
+{
+ "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": {}
+}