@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.server.component.db; | |||
import com.google.common.collect.Lists; | |||
import org.sonar.api.ServerComponent; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.core.component.AuthorizedComponentDto; | |||
@@ -31,8 +32,12 @@ import org.sonar.server.exceptions.NotFoundException; | |||
import javax.annotation.CheckForNull; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
/** | |||
* @since 4.3 | |||
*/ | |||
@@ -85,6 +90,27 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String> | |||
return mapper(session).findModulesByProject(projectKey); | |||
} | |||
public List<ComponentDto> findSubProjectsByComponentKeys(DbSession session, Collection<String> keys) { | |||
return mapper(session).findSubProjectsByComponentKeys(keys); | |||
} | |||
public List<ComponentDto> getByIds(DbSession session, Collection<Long> ids) { | |||
if (ids.isEmpty()) { | |||
return Collections.emptyList(); | |||
} | |||
List<ComponentDto> components = newArrayList(); | |||
List<List<Long>> partitionList = Lists.partition(newArrayList(ids), 1000); | |||
for (List<Long> partition : partitionList) { | |||
List<ComponentDto> dtos = mapper(session).findByIds(partition); | |||
components.addAll(dtos); | |||
} | |||
return components; | |||
} | |||
protected List<ComponentDto> doGetByKeys(DbSession session, Collection<String> keys) { | |||
return mapper(session).findByKeys(keys); | |||
} | |||
@CheckForNull | |||
public AuthorizedComponentDto getNullableAuthorizedComponentById(Long id, DbSession session) { | |||
return mapper(session).selectAuthorizedComponentById(id); |
@@ -22,6 +22,7 @@ package org.sonar.server.db; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.Lists; | |||
import org.apache.ibatis.session.ResultContext; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
@@ -32,24 +33,16 @@ import org.sonar.core.persistence.Dto; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.search.DbSynchronizationHandler; | |||
import org.sonar.server.search.IndexDefinition; | |||
import org.sonar.server.search.action.DeleteKey; | |||
import org.sonar.server.search.action.DeleteNestedItem; | |||
import org.sonar.server.search.action.InsertDto; | |||
import org.sonar.server.search.action.RefreshIndex; | |||
import org.sonar.server.search.action.UpsertDto; | |||
import org.sonar.server.search.action.UpsertNestedItem; | |||
import org.sonar.server.search.action.*; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import java.io.Serializable; | |||
import java.sql.Timestamp; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.*; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static com.google.common.collect.Maps.newHashMap; | |||
/** | |||
@@ -168,14 +161,20 @@ public abstract class BaseDao<MAPPER, DTO extends Dto<KEY>, KEY extends Serializ | |||
} | |||
public List<DTO> getByKeys(DbSession session, Collection<KEY> keys) { | |||
List<DTO> results = new ArrayList<DTO>(); | |||
for (KEY key : keys) { | |||
DTO result = this.getNullableByKey(session, key); | |||
if (result != null) { | |||
results.add(result); | |||
} | |||
if (keys.isEmpty()) { | |||
return Collections.emptyList(); | |||
} | |||
List<DTO> components = newArrayList(); | |||
List<List<KEY>> partitionList = Lists.partition(newArrayList(keys), 1000); | |||
for (List<KEY> partition : partitionList) { | |||
List<DTO> dtos = doGetByKeys(session, partition); | |||
components.addAll(dtos); | |||
} | |||
return results; | |||
return components; | |||
} | |||
protected List<DTO> doGetByKeys(DbSession session, Collection<KEY> keys) { | |||
throw notImplemented(this); | |||
} | |||
@Override | |||
@@ -348,7 +347,7 @@ public abstract class BaseDao<MAPPER, DTO extends Dto<KEY>, KEY extends Serializ | |||
return getSynchronizationParams(date, Collections.<String, String>emptyMap()); | |||
} | |||
protected Map getSynchronizationParams(Date date, Map<String, String> params) { | |||
protected Map<String, Object> getSynchronizationParams(Date date, Map<String, String> params) { | |||
Map<String, Object> finalParams = newHashMap(); | |||
finalParams.put("date", new Timestamp(date.getTime())); | |||
return finalParams; |
@@ -143,6 +143,7 @@ public class DbClient implements ServerComponent { | |||
public ComponentDao componentDao() { | |||
return componentDao; | |||
} | |||
public SnapshotDao snapshotDao() { | |||
return snapshotDao; | |||
} |
@@ -29,7 +29,9 @@ import org.sonar.core.persistence.DbSession; | |||
import org.sonar.server.db.BaseDao; | |||
import org.sonar.server.search.IndexDefinition; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
public class IssueDao extends BaseDao<IssueMapper, IssueDto, String> implements DaoComponent { | |||
@@ -50,6 +52,10 @@ public class IssueDao extends BaseDao<IssueMapper, IssueDto, String> implements | |||
return mapper(session).selectByKey(key); | |||
} | |||
protected List<IssueDto> doGetByKeys(DbSession session, Collection<String> keys) { | |||
return mapper(session).selectByKeys(keys); | |||
} | |||
@Override | |||
protected IssueDto doUpdate(DbSession session, IssueDto issue) { | |||
mapper(session).update(issue); | |||
@@ -70,7 +76,7 @@ public class IssueDao extends BaseDao<IssueMapper, IssueDto, String> implements | |||
} | |||
@Override | |||
protected Map getSynchronizationParams(Date date, Map<String, String> params) { | |||
protected Map<String, Object> getSynchronizationParams(Date date, Map<String, String> params) { | |||
Map<String, Object> finalParams = super.getSynchronizationParams(date, params); | |||
finalParams.put(PROJECT_KEY, params.get(PROJECT_KEY)); | |||
return finalParams; |
@@ -234,6 +234,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> { | |||
Set<RuleKey> ruleKeys = newHashSet(); | |||
Set<String> projectKeys = newHashSet(); | |||
Set<String> componentKeys = newHashSet(); | |||
Set<Long> componentIds = newHashSet(); | |||
Set<String> actionPlanKeys = newHashSet(); | |||
List<String> userLogins = newArrayList(); | |||
Map<String, User> usersByLogin = newHashMap(); | |||
@@ -244,6 +245,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> { | |||
ruleKeys.add(issue.ruleKey()); | |||
projectKeys.add(issue.projectKey()); | |||
componentKeys.add(issue.componentKey()); | |||
// componentIds.add(issue.com()); | |||
actionPlanKeys.add(issue.actionPlanKey()); | |||
if (issue.reporter() != null) { | |||
userLogins.add(issue.reporter()); | |||
@@ -263,8 +265,10 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> { | |||
usersByLogin = getUsersByLogin(session, userLogins); | |||
List<ComponentDto> componentDtos = dbClient.componentDao().getByKeys(session, componentKeys); | |||
List<ComponentDto> subProjectDtos = dbClient.componentDao().findSubProjectsByComponentKeys(session, componentKeys); | |||
List<ComponentDto> projectDtos = dbClient.componentDao().getByKeys(session, projectKeys); | |||
componentDtos.addAll(subProjectDtos); | |||
componentDtos.addAll(projectDtos); | |||
writeProjects(json, projectDtos); | |||
writeComponents(json, componentDtos); |
@@ -33,6 +33,7 @@ import org.sonar.server.exceptions.NotFoundException; | |||
import java.util.Date; | |||
import java.util.List; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -94,6 +95,50 @@ public class ComponentDaoTest extends AbstractDaoTestCase { | |||
assertThat(result.getAuthorizationUpdatedAt()).isEqualTo(DateUtils.parseDate("2014-06-18")); | |||
} | |||
@Test | |||
public void find_by_keys() { | |||
setupData("shared"); | |||
List<ComponentDto> results = dao.getByKeys(session, "org.struts:struts-core:src/org/struts/RequestContext.java"); | |||
assertThat(results).hasSize(1); | |||
ComponentDto result = results.get(0); | |||
assertThat(result).isNotNull(); | |||
assertThat(result.key()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); | |||
assertThat(result.path()).isEqualTo("src/org/struts/RequestContext.java"); | |||
assertThat(result.name()).isEqualTo("RequestContext.java"); | |||
assertThat(result.longName()).isEqualTo("org.struts.RequestContext"); | |||
assertThat(result.qualifier()).isEqualTo("FIL"); | |||
assertThat(result.scope()).isEqualTo("FIL"); | |||
assertThat(result.language()).isEqualTo("java"); | |||
assertThat(result.subProjectId()).isEqualTo(2); | |||
assertThat(result.projectId()).isEqualTo(1); | |||
assertThat(dao.getByKeys(session, "unknown")).isEmpty(); | |||
} | |||
@Test | |||
public void find_by_ids() { | |||
setupData("shared"); | |||
List<ComponentDto> results = dao.getByIds(session, newArrayList(4L)); | |||
assertThat(results).hasSize(1); | |||
ComponentDto result = results.get(0); | |||
assertThat(result).isNotNull(); | |||
assertThat(result.key()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); | |||
assertThat(result.path()).isEqualTo("src/org/struts/RequestContext.java"); | |||
assertThat(result.name()).isEqualTo("RequestContext.java"); | |||
assertThat(result.longName()).isEqualTo("org.struts.RequestContext"); | |||
assertThat(result.qualifier()).isEqualTo("FIL"); | |||
assertThat(result.scope()).isEqualTo("FIL"); | |||
assertThat(result.language()).isEqualTo("java"); | |||
assertThat(result.subProjectId()).isEqualTo(2); | |||
assertThat(result.projectId()).isEqualTo(1); | |||
assertThat(dao.getByIds(session, newArrayList(123L))).isEmpty(); | |||
} | |||
@Test | |||
public void get_by_id() { | |||
setupData("shared"); | |||
@@ -181,6 +226,39 @@ public class ComponentDaoTest extends AbstractDaoTestCase { | |||
assertThat(dao.getParentModuleByKey("unknown", session)).isNull(); | |||
} | |||
@Test | |||
public void find_sub_projects_by_component_keys() throws Exception { | |||
setupData("multi-modules"); | |||
// Sub project of a file | |||
List<ComponentDto> results = dao.findSubProjectsByComponentKeys(session, newArrayList("org.struts:struts-core:src/org/struts/RequestContext.java")); | |||
assertThat(results).hasSize(1); | |||
assertThat(results.get(0).getKey()).isEqualTo("org.struts:struts-data"); | |||
// Sub project of a directory | |||
results = dao.findSubProjectsByComponentKeys(session, newArrayList("org.struts:struts-core:src/org/struts")); | |||
assertThat(results).hasSize(1); | |||
assertThat(results.get(0).getKey()).isEqualTo("org.struts:struts-data"); | |||
// Sub project of a sub module | |||
results = dao.findSubProjectsByComponentKeys(session, newArrayList("org.struts:struts-data")); | |||
assertThat(results).hasSize(1); | |||
assertThat(results.get(0).getKey()).isEqualTo("org.struts:struts"); | |||
// Sub project of a module | |||
results = dao.findSubProjectsByComponentKeys(session, newArrayList("org.struts:struts-core")); | |||
assertThat(results).hasSize(1); | |||
assertThat(results.get(0).getKey()).isEqualTo("org.struts:struts"); | |||
// Sub project of a project | |||
assertThat(dao.findSubProjectsByComponentKeys(session, newArrayList("org.struts:struts"))).isEmpty(); | |||
// SUb projects of a component and a sub module | |||
assertThat(dao.findSubProjectsByComponentKeys(session, newArrayList("org.struts:struts-core:src/org/struts/RequestContext.java", "org.struts:struts-data"))).hasSize(2); | |||
assertThat(dao.findSubProjectsByComponentKeys(session, newArrayList("unknown"))).isEmpty(); | |||
} | |||
@Test | |||
public void get_nullable_authorized_component_by_id() { | |||
setupData("shared"); |
@@ -33,6 +33,7 @@ import org.sonar.core.persistence.DbSession; | |||
import org.sonar.server.rule.RuleTesting; | |||
import java.util.Date; | |||
import java.util.List; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
@@ -90,6 +91,43 @@ public class IssueDaoTest extends AbstractDaoTestCase { | |||
assertThat(issue.getRootComponentKey()).isEqualTo("struts"); | |||
} | |||
@Test | |||
public void get_by_keys() { | |||
setupData("shared", "get_by_key"); | |||
List<IssueDto> issues = dao.getByKeys(session, "ABCDE"); | |||
assertThat(issues).hasSize(1); | |||
IssueDto issue = issues.get(0); | |||
assertThat(issue.getKee()).isEqualTo("ABCDE"); | |||
assertThat(issue.getId()).isEqualTo(100L); | |||
assertThat(issue.getComponentId()).isEqualTo(401); | |||
assertThat(issue.getRootComponentId()).isEqualTo(399); | |||
assertThat(issue.getRuleId()).isEqualTo(500); | |||
assertThat(issue.getLanguage()).isEqualTo("java"); | |||
assertThat(issue.getSeverity()).isEqualTo("BLOCKER"); | |||
assertThat(issue.isManualSeverity()).isFalse(); | |||
assertThat(issue.getMessage()).isNull(); | |||
assertThat(issue.getLine()).isEqualTo(200); | |||
assertThat(issue.getEffortToFix()).isEqualTo(4.2); | |||
assertThat(issue.getStatus()).isEqualTo("OPEN"); | |||
assertThat(issue.getResolution()).isEqualTo("FIXED"); | |||
assertThat(issue.getChecksum()).isEqualTo("XXX"); | |||
assertThat(issue.getAuthorLogin()).isEqualTo("karadoc"); | |||
assertThat(issue.getReporter()).isEqualTo("arthur"); | |||
assertThat(issue.getAssignee()).isEqualTo("perceval"); | |||
assertThat(issue.getIssueAttributes()).isEqualTo("JIRA=FOO-1234"); | |||
assertThat(issue.getIssueCreationDate()).isNotNull(); | |||
assertThat(issue.getIssueUpdateDate()).isNotNull(); | |||
assertThat(issue.getIssueCloseDate()).isNotNull(); | |||
assertThat(issue.getCreatedAt()).isNotNull(); | |||
assertThat(issue.getUpdatedAt()).isNotNull(); | |||
assertThat(issue.getRuleRepo()).isEqualTo("squid"); | |||
assertThat(issue.getRule()).isEqualTo("AvoidCycle"); | |||
assertThat(issue.getComponentKey()).isEqualTo("Action.java"); | |||
assertThat(issue.getRootComponentKey()).isEqualTo("struts"); | |||
} | |||
@Test | |||
public void find_after_dates() throws Exception { | |||
setupData("shared", "some_issues"); |
@@ -91,7 +91,7 @@ public class SearchActionMediumTest { | |||
file = new ComponentDto() | |||
.setKey("MyComponent") | |||
.setProjectId(project.getId()); | |||
.setSubProjectId(project.getId()); | |||
db.componentDao().insert(session, file); | |||
db.snapshotDao().insert(session, SnapshotTesting.createForComponent(file)); | |||
@@ -233,6 +233,40 @@ public class SearchActionMediumTest { | |||
result.assertJson(this.getClass(), "issue_with_extra_fields.json", false); | |||
} | |||
@Test | |||
public void components_contains_sub_projects() throws Exception { | |||
ComponentDto project = new ComponentDto() | |||
.setKey("ProjectHavingModule") | |||
.setScope("PRJ"); | |||
db.componentDao().insert(session, project); | |||
db.snapshotDao().insert(session, SnapshotTesting.createForComponent(project)); | |||
// project can be seen by anyone | |||
tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session); | |||
db.issueAuthorizationDao().synchronizeAfter(session, new Date(0)); | |||
ComponentDto module = new ComponentDto() | |||
.setKey("ModuleHavingFile") | |||
.setScope("PRJ") | |||
.setSubProjectId(project.getId()); | |||
db.componentDao().insert(session, module); | |||
db.snapshotDao().insert(session, SnapshotTesting.createForComponent(module)); | |||
ComponentDto file = new ComponentDto() | |||
.setKey("FileLinkedToModule") | |||
.setScope("FIL") | |||
.setSubProjectId(module.getId()); | |||
db.componentDao().insert(session, file); | |||
db.snapshotDao().insert(session, SnapshotTesting.createForComponent(file)); | |||
IssueDto issue = IssueTesting.newDto(rule, file, project); | |||
db.issueDao().insert(session, issue); | |||
session.commit(); | |||
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute(); | |||
result.assertJson(this.getClass(), "components_contains_sub_projects.json", false); | |||
} | |||
@Test | |||
public void display_facets() throws Exception { | |||
IssueDto issue = IssueTesting.newDto(rule, file, project) |
@@ -0,0 +1,18 @@ | |||
{ | |||
"components": [ | |||
{ | |||
"key": "FileLinkedToModule" | |||
}, | |||
{ | |||
"key": "ModuleHavingFile" | |||
}, | |||
{ | |||
"key": "ProjectHavingModule" | |||
} | |||
], | |||
"projects": [ | |||
{ | |||
"key": "ProjectHavingModule" | |||
} | |||
] | |||
} |
@@ -32,11 +32,13 @@ public class ComponentDto extends AuthorizedComponentDto implements Component { | |||
private String name; | |||
private String longName; | |||
private String language; | |||
private Long projectId; | |||
private Long subProjectId; | |||
private boolean enabled = true; | |||
private Date authorizationUpdatedAt; | |||
// Return by join for the moment | |||
private Long projectId; | |||
public ComponentDto setId(Long id) { | |||
super.setId(id); | |||
return this; |
@@ -25,6 +25,7 @@ import org.sonar.core.component.ComponentDto; | |||
import javax.annotation.CheckForNull; | |||
import java.util.Collection; | |||
import java.util.List; | |||
/** | |||
@@ -49,6 +50,15 @@ public interface ComponentMapper { | |||
*/ | |||
List<ComponentDto> findModulesByProject(@Param("projectKey") String projectKey); | |||
/** | |||
* Return sub project of component keys | |||
*/ | |||
List<ComponentDto> findSubProjectsByComponentKeys(@Param("keys") Collection<String> keys); | |||
List<ComponentDto> findByIds(@Param("ids") Collection<Long> ids); | |||
List<ComponentDto> findByKeys(@Param("keys") Collection<String> keys); | |||
long countById(long id); | |||
@CheckForNull |
@@ -34,6 +34,8 @@ public interface IssueMapper { | |||
IssueDto selectByKey(String key); | |||
List<IssueDto> selectByKeys(Collection<String> keys); | |||
/** | |||
* Return a paginated list of authorized issue ids for a user. | |||
* If the role is null, then the authorisation check is disabled. |
@@ -86,6 +86,47 @@ | |||
</where> | |||
</select> | |||
<select id="findByIds" parameterType="long" resultType="Component"> | |||
select <include refid="componentColumns"/> | |||
from projects p | |||
inner join snapshots s on s.project_id=p.id and s.islast=${_true} | |||
<where> | |||
p.enabled=${_true} | |||
and p.id in | |||
<foreach collection="ids" open="(" close=")" item="id" separator=","> | |||
#{id} | |||
</foreach> | |||
</where> | |||
</select> | |||
<select id="findByKeys" parameterType="String" resultType="Component"> | |||
select <include refid="componentColumns"/> | |||
from projects p | |||
inner join snapshots s on s.project_id=p.id and s.islast=${_true} | |||
<where> | |||
p.enabled=${_true} | |||
and p.kee in | |||
<foreach collection="keys" open="(" close=")" item="key" separator=","> | |||
#{key} | |||
</foreach> | |||
</where> | |||
</select> | |||
<select id="findSubProjectsByComponentKeys" parameterType="String" resultType="Component"> | |||
SELECT <include refid="componentColumns"/> | |||
FROM projects p | |||
INNER JOIN snapshots s ON s.project_id=p.id AND s.islast=${_true} | |||
INNER JOIN projects child ON child.root_id=p.id AND child.enabled=${_true} | |||
<where> | |||
AND p.enabled=${_true} | |||
AND p.scope='PRJ' | |||
AND child.kee in | |||
<foreach collection="keys" open="(" close=")" item="key" separator=","> | |||
#{key} | |||
</foreach> | |||
</where> | |||
</select> | |||
<select id="selectAuthorizedComponentById" parameterType="long" resultType="AuthorizedComponent"> | |||
SELECT <include refid="authorizedComponentColumns"/> | |||
FROM projects p |
@@ -204,6 +204,19 @@ | |||
</foreach> | |||
</select> | |||
<select id="selectByKeys" parameterType="map" resultType="Issue"> | |||
select | |||
<include refid="issueColumns"/> | |||
from issues i | |||
inner join rules r on r.id=i.rule_id | |||
inner join projects p on p.id=i.component_id | |||
inner join projects root on root.id=i.root_component_id | |||
where i.kee in | |||
<foreach collection="list" open="(" close=")" item="key" separator=","> | |||
#{key} | |||
</foreach> | |||
</select> | |||
<select id="selectIssues" parameterType="map" resultType="Issue"> | |||
select | |||
<include refid="issueColumns"/> |