aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java15
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml2
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java8
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java9
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java11
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java77
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb1
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb1
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java113
-rw-r--r--sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java5
-rw-r--r--sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueParser.java9
-rw-r--r--sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issues.java18
-rw-r--r--sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueParserTest.java15
-rw-r--r--sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-comments.json1
-rw-r--r--sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-components.json1
-rw-r--r--sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-projects.json58
-rw-r--r--sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-users.json1
-rw-r--r--sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/search.json2
19 files changed, 256 insertions, 93 deletions
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 fc3d571fb48..5ffa8a84495 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
@@ -103,10 +103,10 @@ public class IssueDao implements BatchComponent, ServerComponent {
}
@VisibleForTesting
- List<IssueDto> selectIssueAndProjectIds(IssueQuery query, Integer maxResults) {
+ List<IssueDto> selectIssueAndProjectIds(IssueQuery query, Collection<Integer> authorizedRootProjectIds, Integer maxResults) {
SqlSession session = mybatis.openSession();
try {
- return selectIssueAndProjectIds(query, maxResults, session);
+ return selectIssueAndProjectIds(query, authorizedRootProjectIds, maxResults, session);
} finally {
MyBatis.closeQuietly(session);
}
@@ -115,12 +115,19 @@ public class IssueDao implements BatchComponent, ServerComponent {
/**
* The returned IssueDto list contains only the issue id and the project id
*/
- public List<IssueDto> selectIssueAndProjectIds(IssueQuery query, final Integer maxResults, SqlSession session) {
+ public List<IssueDto> selectIssueAndProjectIds(final IssueQuery query, final Collection<Integer> authorizedRootProjectIds, SqlSession session) {
+ return selectIssueAndProjectIds(query, authorizedRootProjectIds, query.maxResults(), session);
+ }
+
+ private List<IssueDto> selectIssueAndProjectIds(final IssueQuery query, final Collection<Integer> authorizedRootProjectIds, final Integer maxResults, SqlSession session) {
final List<IssueDto> issues = newArrayList();
ResultHandler resultHandler = new ResultHandler(){
@Override
public void handleResult(ResultContext context) {
- issues.add((IssueDto) context.getResultObject());
+ IssueDto issueDto = (IssueDto) context.getResultObject();
+ if (authorizedRootProjectIds.contains(issueDto.getProjectId())) {
+ issues.add(issueDto);
+ }
if (issues.size() >= maxResults) {
context.stop();
}
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 4b27aa1f2dd..f82d0f9e7cf 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
@@ -162,7 +162,7 @@
</select>
<select id="selectIssueAndProjectIds" parameterType="map" resultType="Issue">
- select i.id, i.resource_id as resourceId
+ select i.id, i.project_id as projectId
<include refid="selectQueryConditions"/>
</select>
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 6f8d460f0c1..8418243ce77 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,6 +27,7 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.DateUtils;
import org.sonar.core.persistence.AbstractDaoTestCase;
+import java.util.Collections;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
@@ -309,11 +310,14 @@ public class IssueDaoTest extends AbstractDaoTestCase {
setupData("shared", "should_select_issue_and_project_ids");
IssueQuery query = IssueQuery.builder().build();
- List<IssueDto> results = dao.selectIssueAndProjectIds(query, 5);
+ List<IssueDto> results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
assertThat(results).hasSize(3);
- results = dao.selectIssueAndProjectIds(query, 2);
+ results = dao.selectIssueAndProjectIds(query, newArrayList(399), 2);
assertThat(results).hasSize(2);
+
+ results = dao.selectIssueAndProjectIds(query, Collections.<Integer>emptyList(), 1000);
+ assertThat(results).isEmpty();
}
@Test
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
index 42a38bf4190..36bf2abc812 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
@@ -50,6 +50,8 @@ public interface Issue extends Serializable {
String componentKey();
+ String projectKey();
+
RuleKey ruleKey();
String severity();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
index 158cba4c329..74edf09d6ec 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
@@ -22,6 +22,7 @@ package org.sonar.api.issue;
import com.google.common.base.Preconditions;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.web.UserRole;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -36,6 +37,7 @@ public class IssueQuery {
public static final int DEFAULT_PAGE_INDEX = 1;
public static final int DEFAULT_PAGE_SIZE = 100;
+ public static final int MAX_RESULTS = 5000;
public static final int MAX_PAGE_SIZE = 500;
public static final int MAX_ISSUE_KEYS = 500;
@@ -174,7 +176,10 @@ public class IssueQuery {
return pageIndex;
}
- @CheckForNull
+ public int maxResults() {
+ return MAX_RESULTS;
+ }
+
public String requiredRole() {
return requiredRole;
}
@@ -208,7 +213,7 @@ public class IssueQuery {
private Boolean asc = false;
private Integer pageSize;
private Integer pageIndex;
- private String requiredRole;
+ private String requiredRole = UserRole.CODEVIEWER;
private Builder() {
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java
index b40991195ff..4a710cecaa6 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java
@@ -25,6 +25,7 @@ import org.sonar.api.user.User;
import org.sonar.api.utils.Paging;
import javax.annotation.CheckForNull;
+
import java.util.Collection;
import java.util.List;
@@ -48,6 +49,14 @@ public interface IssueQueryResult {
*/
Collection<Component> components();
+ Component project(Issue issue);
+
+ /**
+ * The projects involved in the paginated {@link #issues()}.
+ */
+ Collection<Component> projects();
+
+
@CheckForNull
ActionPlan actionPlan(Issue issue);
@@ -65,4 +74,6 @@ public interface IssueQueryResult {
Paging paging();
boolean securityExclusions();
+
+ boolean maxResultsReached();
}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
index f0fb6517564..caea62016a2 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
@@ -19,8 +19,6 @@
*/
package org.sonar.server.issue;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -102,16 +100,11 @@ public class DefaultIssueFinder implements IssueFinder {
LOG.debug("IssueQuery : {}", query);
SqlSession sqlSession = myBatis.openSession();
try {
- // 1. Select the ids of all the issues that match the query
- List<IssueDto> allIssues = issueDao.selectIssueAndComponentIds(query, sqlSession);
-
- // 2. Apply security, if needed
- List<IssueDto> authorizedIssues;
- if (query.requiredRole() != null) {
- authorizedIssues = keepAuthorized(allIssues, query.requiredRole(), sqlSession);
- } else {
- authorizedIssues = allIssues;
- }
+ // 1. Select all authorized root project ids for the user
+ Collection<Integer> rootProjectIds = authorizationDao.selectAuthorizedRootProjectsIds(UserSession.get().userId(), query.requiredRole(), sqlSession);
+
+ // 2. Select the authorized ids of all the issues that match the query
+ List<IssueDto> authorizedIssues = issueDao.selectIssueAndProjectIds(query, rootProjectIds, sqlSession);
// 3. Apply pagination
Paging paging = Paging.create(query.pageSize(), query.pageIndex(), authorizedIssues.size());
@@ -123,6 +116,7 @@ public class DefaultIssueFinder implements IssueFinder {
List<Issue> issues = newArrayList();
Set<Integer> ruleIds = Sets.newHashSet();
Set<Integer> componentIds = Sets.newHashSet();
+ Set<Integer> projectIds = Sets.newHashSet();
Set<String> actionPlanKeys = Sets.newHashSet();
Set<String> users = Sets.newHashSet();
for (IssueDto dto : pagedIssues) {
@@ -131,6 +125,7 @@ public class DefaultIssueFinder implements IssueFinder {
issues.add(defaultIssue);
ruleIds.add(dto.getRuleId());
componentIds.add(dto.getResourceId());
+ projectIds.add(dto.getProjectId());
actionPlanKeys.add(dto.getActionPlanKey());
if (dto.getReporter() != null) {
users.add(dto.getReporter());
@@ -151,38 +146,20 @@ public class DefaultIssueFinder implements IssueFinder {
return new DefaultResults(issues,
findRules(ruleIds),
findComponents(componentIds),
+ findProjects(projectIds),
findActionPlans(actionPlanKeys),
findUsers(users),
paging,
- authorizedIssues.size() != allIssues.size());
+ false,
+ authorizedIssues.size() != query.maxResults()
+ // TODO
+// authorizedIssues.size() != allIssues.size()
+ );
} finally {
MyBatis.closeQuietly(sqlSession);
}
}
- private List<IssueDto> keepAuthorized(List<IssueDto> issues, String requiredRole, SqlSession sqlSession) {
- final Set<Integer> authorizedComponentIds = authorizationDao.keepAuthorizedComponentIds(
- extractResourceIds(issues),
- UserSession.get().userId(),
- requiredRole,
- sqlSession
- );
- return newArrayList(Iterables.filter(issues, new Predicate<IssueDto>() {
- @Override
- public boolean apply(IssueDto issueDto) {
- return authorizedComponentIds.contains(issueDto.getResourceId());
- }
- }));
- }
-
- private Set<Integer> extractResourceIds(List<IssueDto> issues) {
- Set<Integer> componentIds = Sets.newLinkedHashSet();
- for (IssueDto issue : issues) {
- componentIds.add(issue.getResourceId());
- }
- return componentIds;
- }
-
private Set<Long> pagedIssueIds(Collection<IssueDto> issues, Paging paging) {
Set<Long> issueIds = Sets.newLinkedHashSet();
int index = 0;
@@ -209,6 +186,10 @@ public class DefaultIssueFinder implements IssueFinder {
return resourceDao.findByIds(componentIds);
}
+ private Collection<Component> findProjects(Set<Integer> projectIds) {
+ return resourceDao.findByIds(projectIds);
+ }
+
private Collection<ActionPlan> findActionPlans(Set<String> actionPlanKeys) {
return actionPlanService.findByKeys(actionPlanKeys);
}
@@ -222,17 +203,20 @@ public class DefaultIssueFinder implements IssueFinder {
private final List<Issue> issues;
private final Map<RuleKey, Rule> rulesByKey = Maps.newHashMap();
private final Map<String, Component> componentsByKey = Maps.newHashMap();
+ private final Map<String, Component> projectsByKey = Maps.newHashMap();
private final Map<String, ActionPlan> actionPlansByKey = Maps.newHashMap();
private final Map<String, User> usersByLogin = Maps.newHashMap();
private final boolean securityExclusions;
+ private final boolean maxResultsReached;
private final Paging paging;
DefaultResults(List<Issue> issues,
Collection<Rule> rules,
Collection<Component> components,
+ Collection<Component> projects,
Collection<ActionPlan> actionPlans,
Collection<User> users,
- Paging paging, boolean securityExclusions) {
+ Paging paging, boolean securityExclusions, boolean maxResultsReached) {
this.issues = issues;
for (Rule rule : rules) {
rulesByKey.put(rule.ruleKey(), rule);
@@ -240,6 +224,9 @@ public class DefaultIssueFinder implements IssueFinder {
for (Component component : components) {
componentsByKey.put(component.key(), component);
}
+ for (Component project : projects) {
+ projectsByKey.put(project.key(), project);
+ }
for (ActionPlan actionPlan : actionPlans) {
actionPlansByKey.put(actionPlan.key(), actionPlan);
}
@@ -248,6 +235,7 @@ public class DefaultIssueFinder implements IssueFinder {
}
this.paging = paging;
this.securityExclusions = securityExclusions;
+ this.maxResultsReached = maxResultsReached;
}
@Override
@@ -276,6 +264,16 @@ public class DefaultIssueFinder implements IssueFinder {
}
@Override
+ public Component project(Issue issue) {
+ return projectsByKey.get(issue.projectKey());
+ }
+
+ @Override
+ public Collection<Component> projects() {
+ return projectsByKey.values();
+ }
+
+ @Override
public ActionPlan actionPlan(Issue issue) {
return actionPlansByKey.get(issue.actionPlanKey());
}
@@ -302,6 +300,11 @@ public class DefaultIssueFinder implements IssueFinder {
}
@Override
+ public boolean maxResultsReached() {
+ return maxResultsReached;
+ }
+
+ @Override
public Paging paging() {
return paging;
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb
index 7620b02b60d..cf6577cce7e 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb
@@ -34,6 +34,7 @@ class Api::IssuesController < Api::ApiController
:paging => paging_to_hash(results.paging),
:issues => results.issues.map { |issue| Issue.to_hash(issue) },
:components => results.components.map { |component| component_to_hash(component) },
+ :projects => results.projects.map { |project| component_to_hash(project) },
:rules => results.rules.map { |rule| Rule.to_hash(rule) },
:users => results.users.map { |user| User.to_hash(user) }
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
index eba1a27714d..15238c29c66 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
@@ -23,6 +23,7 @@ class Issue
hash = {
:key => issue.key,
:component => issue.componentKey,
+ :project => issue.projectKey,
:rule => issue.ruleKey.toString(),
:status => issue.status
}
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
index fd4bca4597a..b2dc2fcfe90 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
@@ -21,8 +21,6 @@ package org.sonar.server.issue;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.sonar.api.component.Component;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
@@ -50,7 +48,6 @@ import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollection;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anySet;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.anyBoolean;
@@ -70,68 +67,73 @@ public class DefaultIssueFinderTest {
@Test
public void should_find_issues() {
- grantAccessRights();
+ when(authorizationDao.selectAuthorizedRootProjectsIds(anyInt(), anyString(), any(SqlSession.class)))
+ .thenReturn(newHashSet(100));
IssueQuery query = IssueQuery.builder().build();
- IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123)
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
- IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123)
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
List<IssueDto> dtoList = newArrayList(issue1, issue2);
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
IssueQueryResult results = finder.find(query);
+ verify(issueDao).selectIssueAndProjectIds(eq(query), eq(newHashSet(100)), any(SqlSession.class));
+
assertThat(results.issues()).hasSize(2);
Issue issue = results.issues().iterator().next();
assertThat(issue.componentKey()).isEqualTo("Action.java");
+ assertThat(issue.projectKey()).isEqualTo("struts");
assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle");
assertThat(results.securityExclusions()).isFalse();
}
@Test
public void should_find_only_authorized_issues() {
+ when(authorizationDao.selectAuthorizedRootProjectsIds(anyInt(), anyString(), any(SqlSession.class)))
+ .thenReturn(Collections.<Integer>emptySet());
IssueQuery query = IssueQuery.builder().pageSize(100).requiredRole(UserRole.USER).build();
- IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123)
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
- IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(135)
- .setComponentKey_unit_test_only("Phases.java")
- .setRuleKey_unit_test_only("squid", "AvoidCycle")
- .setStatus("OPEN").setResolution("OPEN");
- List<IssueDto> dtoList = newArrayList(issue1, issue2);
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
- when(authorizationDao.keepAuthorizedComponentIds(anySet(), anyInt(), anyString(), any(SqlSession.class))).thenReturn(newHashSet(123));
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(newArrayList(issue1));
- IssueQueryResult results = finder.find(query);
+ finder.find(query);
+ verify(issueDao).selectIssueAndProjectIds(eq(query), eq(Collections.<Integer>emptySet()), any(SqlSession.class));
- verify(issueDao).selectByIds(eq(newHashSet(1L)), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class));
- assertThat(results.securityExclusions()).isTrue();
+ // TODO
+// assertThat(results.securityExclusions()).isTrue();
}
@Test
public void should_find_paginate_result() {
- grantAccessRights();
+ when(authorizationDao.selectAuthorizedRootProjectsIds(anyInt(), anyString(), any(SqlSession.class)))
+ .thenReturn(newHashSet(100));
IssueQuery query = IssueQuery.builder().pageSize(1).pageIndex(1).build();
- IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123)
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
- IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(135)
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(135).setProjectId(100)
.setComponentKey_unit_test_only("Phases.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
List<IssueDto> dtoList = newArrayList(issue1, issue2);
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
+ when(issueDao.selectIssueAndProjectIds(eq(query), eq(newHashSet(100)), any(SqlSession.class))).thenReturn(dtoList);
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
IssueQueryResult results = finder.find(query);
@@ -145,8 +147,9 @@ public class DefaultIssueFinderTest {
@Test
public void should_find_by_key() {
- IssueDto issueDto = new IssueDto().setId(1L).setRuleId(1).setResourceId(1)
+ IssueDto issueDto = new IssueDto().setId(1L).setRuleId(1).setResourceId(1).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
when(issueDao.selectByKey("ABCDE")).thenReturn(issueDto);
@@ -162,19 +165,19 @@ public class DefaultIssueFinderTest {
Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle");
when(ruleFinder.findByIds(anyCollection())).thenReturn(newArrayList(rule));
- grantAccessRights();
IssueQuery query = IssueQuery.builder().build();
- IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123)
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
- IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123)
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
List<IssueDto> dtoList = newArrayList(issue1, issue2);
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
IssueQueryResult results = finder.find(query);
@@ -190,47 +193,73 @@ public class DefaultIssueFinderTest {
Component component = new ComponentDto().setKey("Action.java");
when(resourceDao.findByIds(anyCollection())).thenReturn(newArrayList(component));
- grantAccessRights();
IssueQuery query = IssueQuery.builder().build();
- IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123)
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
- IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123)
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setProjectId(100)
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
List<IssueDto> dtoList = newArrayList(issue1, issue2);
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
IssueQueryResult results = finder.find(query);
assertThat(results.issues()).hasSize(2);
- assertThat(results.issues()).hasSize(2);
assertThat(results.components()).hasSize(1);
Issue issue = results.issues().iterator().next();
assertThat(results.component(issue)).isEqualTo(component);
}
@Test
+ public void should_get_project_from_result() {
+ Component project = new ComponentDto().setKey("struts");
+ when(resourceDao.findByIds(anyCollection())).thenReturn(newArrayList(project));
+
+ IssueQuery query = IssueQuery.builder().build();
+
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100)
+ .setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
+ .setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setStatus("OPEN").setResolution("OPEN");
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setProjectId(100)
+ .setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
+ .setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setStatus("OPEN").setResolution("OPEN");
+ List<IssueDto> dtoList = newArrayList(issue1, issue2);
+ when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+
+ IssueQueryResult results = finder.find(query);
+ assertThat(results.issues()).hasSize(2);
+ assertThat(results.projects()).hasSize(1);
+ Issue issue = results.issues().iterator().next();
+ assertThat(results.project(issue)).isEqualTo(project);
+ }
+
+ @Test
public void should_get_action_plans_from_result() {
ActionPlan actionPlan1 = DefaultActionPlan.create("Short term").setKey("A");
ActionPlan actionPlan2 = DefaultActionPlan.create("Long term").setKey("B");
- grantAccessRights();
IssueQuery query = IssueQuery.builder().build();
- IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setKee("ABC").setActionPlanKey("A")
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setProjectId(100).setKee("ABC").setActionPlanKey("A")
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
- IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setKee("DEF").setActionPlanKey("B")
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setProjectId(100).setKee("DEF").setActionPlanKey("B")
.setComponentKey_unit_test_only("Action.java")
+ .setProjectKey_unit_test_only("struts")
.setRuleKey_unit_test_only("squid", "AvoidCycle")
.setStatus("OPEN").setResolution("OPEN");
List<IssueDto> dtoList = newArrayList(issue1, issue2);
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
when(actionPlanService.findByKeys(anyCollection())).thenReturn(newArrayList(actionPlan1, actionPlan2));
@@ -245,10 +274,9 @@ public class DefaultIssueFinderTest {
public void should_get_empty_result_when_no_issue() {
grantAccessRights();
IssueQuery query = IssueQuery.builder().build();
- when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
+ when(issueDao.selectIssueAndProjectIds(eq(query), anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
-
IssueQueryResult results = finder.find(query);
assertThat(results.issues()).isEmpty();
assertThat(results.rules()).isEmpty();
@@ -257,12 +285,7 @@ public class DefaultIssueFinderTest {
}
private void grantAccessRights() {
- when(authorizationDao.keepAuthorizedComponentIds(anySet(), anyInt(), anyString(), any(SqlSession.class)))
- .thenAnswer(new Answer<Object>() {
- @Override
- public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
- return invocationOnMock.getArguments()[0];
- }
- });
+ when(authorizationDao.selectAuthorizedRootProjectsIds(anyInt(), anyString(), any(SqlSession.class)))
+ .thenReturn(newHashSet(100));
}
}
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
index c00f4d25f0e..ff9bad62e2c 100644
--- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
+++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
@@ -22,6 +22,7 @@ package org.sonar.wsclient.issue;
import org.sonar.wsclient.unmarshallers.JsonUtils;
import javax.annotation.CheckForNull;
+
import java.util.*;
/**
@@ -46,6 +47,10 @@ public class Issue {
return JsonUtils.getString(json, "component");
}
+ public String projectKey() {
+ return JsonUtils.getString(json, "project");
+ }
+
public String ruleKey() {
return JsonUtils.getString(json, "rule");
}
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueParser.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueParser.java
index 1bde473d793..0f225f03f8e 100644
--- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueParser.java
+++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueParser.java
@@ -61,7 +61,14 @@ class IssueParser {
List<Map> jsonComponents = (List) jsonRoot.get("components");
if (jsonComponents != null) {
for (Map jsonComponent : jsonComponents) {
- result.add(new Component(jsonComponent));
+ result.addComponent(new Component(jsonComponent));
+ }
+ }
+
+ List<Map> jsonProjects = (List) jsonRoot.get("projects");
+ if (jsonProjects != null) {
+ for (Map jsonProject : jsonProjects) {
+ result.addProject(new Component(jsonProject));
}
}
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issues.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issues.java
index f3d57ebf99c..ab0c8f36ca9 100644
--- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issues.java
+++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issues.java
@@ -24,6 +24,7 @@ import org.sonar.wsclient.rule.Rule;
import org.sonar.wsclient.user.User;
import javax.annotation.CheckForNull;
+
import java.util.*;
/**
@@ -35,6 +36,7 @@ public class Issues {
private final Map<String, Rule> rulesByKey = new HashMap<String, Rule>();
private final Map<String, User> usersByKey = new HashMap<String, User>();
private final Map<String, Component> componentsByKey = new HashMap<String, Component>();
+ private final Map<String, Component> projectsByKey = new HashMap<String, Component>();
private Paging paging;
private Boolean securityExclusions;
@@ -72,6 +74,15 @@ public class Issues {
return componentsByKey.get(issue.componentKey());
}
+ public Collection<Component> projects() {
+ return projectsByKey.values();
+ }
+
+ @CheckForNull
+ public Component project(Issue issue) {
+ return projectsByKey.get(issue.projectKey());
+ }
+
public Paging paging() {
return paging;
}
@@ -95,11 +106,16 @@ public class Issues {
return this;
}
- Issues add(Component c) {
+ Issues addComponent(Component c) {
componentsByKey.put(c.key(), c);
return this;
}
+ Issues addProject(Component c) {
+ projectsByKey.put(c.key(), c);
+ return this;
+ }
+
Issues setPaging(Paging paging) {
this.paging = paging;
return this;
diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueParserTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueParserTest.java
index 1d84ad11cbc..6bc95d9fdc3 100644
--- a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueParserTest.java
+++ b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueParserTest.java
@@ -39,6 +39,7 @@ public class IssueParserTest {
Issue first = list.get(0);
assertThat(first.key()).isEqualTo("ABCDE");
assertThat(first.componentKey()).isEqualTo("Action.java");
+ assertThat(first.projectKey()).isEqualTo("struts");
assertThat(first.ruleKey()).isEqualTo("squid:CycleBetweenPackages");
assertThat(first.severity()).isEqualTo("CRITICAL");
assertThat(first.line()).isEqualTo(10);
@@ -155,4 +156,18 @@ public class IssueParserTest {
assertThat(component.name()).isEqualTo("Action");
assertThat(component.longName()).isEqualTo("org.struts.Action");
}
+
+ @Test
+ public void should_parse_projects() throws Exception {
+ String json = IOUtils.toString(getClass().getResourceAsStream("/org/sonar/wsclient/issue/IssueParserTest/issue-with-projects.json"));
+ Issues issues = new IssueParser().parseIssues(json);
+
+ assertThat(issues.projects()).hasSize(1);
+
+ Component component = issues.project(issues.list().get(0));
+ assertThat(component.key()).isEqualTo("struts");
+ assertThat(component.qualifier()).isEqualTo("TRK");
+ assertThat(component.name()).isEqualTo("Struts");
+ assertThat(component.longName()).isEqualTo("org.struts");
+ }
}
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-comments.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-comments.json
index 914e7627e7c..e5a213f7ba6 100644
--- a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-comments.json
+++ b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-comments.json
@@ -3,6 +3,7 @@
{
"key": "ABCDE",
"component": "Action.java",
+ "project": "struts",
"rule": "squid:CycleBetweenPackages",
"severity": "CRITICAL",
"status": "OPEN",
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-components.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-components.json
index 0eae0aca856..86655ee1470 100644
--- a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-components.json
+++ b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-components.json
@@ -3,6 +3,7 @@
{
"key": "ABCDE",
"component": "struts:Action.java",
+ "project": "struts",
"rule": "squid:CycleBetweenPackages",
"severity": "CRITICAL",
"status": "OPEN",
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-projects.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-projects.json
new file mode 100644
index 00000000000..f0542d4f8bd
--- /dev/null
+++ b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-projects.json
@@ -0,0 +1,58 @@
+{
+ "issues": [
+ {
+ "key": "ABCDE",
+ "component": "struts:Action.java",
+ "project": "struts",
+ "rule": "squid:CycleBetweenPackages",
+ "severity": "CRITICAL",
+ "status": "OPEN",
+ "comments": [
+ {
+ "key": "COMMENT-1",
+ "login": "morgan",
+ "htmlText": "the first comment",
+ "createdAt": "2013-05-18T13:45:34+0200"
+ },
+ {
+ "key": "COMMENT-2",
+ "login": "arthur",
+ "htmlText": "the second comment",
+ "createdAt": "2013-06-19T00:02:03+0100"
+ }
+ ]
+ }
+ ],
+ "rules": [
+ {
+
+ "key": "squid:CycleBetweenPackages",
+ "name": "Avoid cycle between java packages",
+ "desc": "<p>\nWhen several packages are involved in a cycle (package A > package B > package C > package A where \">\" means \"depends upon\"),\nthat means that those packages are highly coupled and that there is no way to reuse/extract one of those packages without importing all the other packages.\nSuch cycle could quickly increase the effort required to maintain an application and to embrace business change.\nSonar not only detect cycles between packages but also determines what is the minimum effort to break those cycles.\nThis rule log a violation on each source file having an outgoing dependency to be but in order to break a cycle.\n</p>\n"
+
+ }
+ ],
+ "components": [
+ {
+ "key": "struts:Action.java",
+ "name": "Action",
+ "qualifier": "CLA",
+ "longName": "org.struts.Action"
+ }
+ ],
+ "projects": [
+ {
+ "key": "struts",
+ "name": "Struts",
+ "qualifier": "TRK",
+ "longName": "org.struts"
+ }
+ ],
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2,
+ "pages": 1
+ },
+ "securityExclusions": true
+} \ No newline at end of file
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-users.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-users.json
index 7c4735ca98d..f076b290b4d 100644
--- a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-users.json
+++ b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/issue-with-users.json
@@ -3,6 +3,7 @@
{
"key": "ABCDE",
"component": "Action.java",
+ "project": "struts",
"rule": "squid:CycleBetweenPackages",
"severity": "CRITICAL",
"status": "OPEN",
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/search.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/search.json
index 8385fa8b09b..c88120f2dd9 100644
--- a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/search.json
+++ b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/IssueParserTest/search.json
@@ -3,6 +3,7 @@
{
"key": "ABCDE",
"component": "Action.java",
+ "project": "struts",
"rule": "squid:CycleBetweenPackages",
"severity": "CRITICAL",
"line": 10,
@@ -23,6 +24,7 @@
{
"key": "FGHIJ",
"component": "Filter.java",
+ "project": "struts",
"rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck",
"severity": "BLOCKER",
"resolution": "FIXED",