aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2015-12-17 16:19:44 +0100
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2015-12-21 12:27:25 +0100
commit6fe8b0cd64c4b4e2386d994016b465b0386ed09c (patch)
tree73006c620bed41a2849e4d563aa8c1981de7c38d /sonar-db
parent8239ac084eb9aaaba9f661a4e2fe5891bda8d452 (diff)
downloadsonarqube-6fe8b0cd64c4b4e2386d994016b465b0386ed09c.tar.gz
sonarqube-6fe8b0cd64c4b4e2386d994016b465b0386ed09c.zip
SONAR-7129 WS api/components/tree
Diffstat (limited to 'sonar-db')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java19
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java26
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java188
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml69
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java191
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java25
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java3
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java63
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ResourceTypesRule.java21
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/SnapshotTesting.java10
10 files changed, 590 insertions, 25 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java
index 4b8f62b0e06..14371abbcf1 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java
@@ -41,6 +41,7 @@ import org.sonar.db.RowNotFoundException;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
+import static org.sonar.api.utils.Paging.offset;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
public class ComponentDao implements Dao {
@@ -150,6 +151,24 @@ public class ComponentDao implements Dao {
return mapper(session).selectComponentsHavingSameKeyOrderedById(key);
}
+ public List<ComponentDto> selectDirectChildren(DbSession dbSession, ComponentTreeQuery componentQuery) {
+ RowBounds rowBounds = new RowBounds(offset(componentQuery.getPage(), componentQuery.getPageSize()), componentQuery.getPageSize());
+ return mapper(dbSession).selectDirectChildren(componentQuery, rowBounds);
+ }
+
+ public List<ComponentDto> selectAllChildren(DbSession dbSession, ComponentTreeQuery componentQuery) {
+ RowBounds rowBounds = new RowBounds(offset(componentQuery.getPage(), componentQuery.getPageSize()), componentQuery.getPageSize());
+ return mapper(dbSession).selectAllChildren(componentQuery, rowBounds);
+ }
+
+ public int countDirectChildren(DbSession dbSession, ComponentTreeQuery query) {
+ return mapper(dbSession).countDirectChildren(query);
+ }
+
+ public int countAllChildren(DbSession dbSession, ComponentTreeQuery query) {
+ return mapper(dbSession).countAllChildren(query);
+ }
+
private static class KeyToDto implements Function<List<String>, List<ComponentDto>> {
private final ComponentMapper mapper;
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java
index 42a59729461..47e3191cca9 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java
@@ -59,9 +59,24 @@ public interface ComponentMapper {
List<ComponentDto> selectComponentsByQualifiers(@Param("qualifiers") Collection<String> qualifiers);
- List<ComponentDto> selectByQuery(ComponentQuery query, RowBounds rowBounds);
+ List<ComponentDto> selectByQuery(@Param("query") ComponentQuery query, RowBounds rowBounds);
- int countByQuery(ComponentQuery query);
+ int countByQuery(@Param("query") ComponentQuery query);
+
+ /**
+ * Return direct children components
+ */
+ List<ComponentDto> selectDirectChildren(@Param("query") ComponentTreeQuery componentTreeQuery, RowBounds rowBounds);
+
+ int countDirectChildren(@Param("query") ComponentTreeQuery componentTreeQuery);
+
+ /**
+ * Return all children components.
+ */
+ List<ComponentDto> selectAllChildren(@Param("query") ComponentTreeQuery componentTreeQuery,
+ RowBounds rowBounds);
+
+ int countAllChildren(@Param("query") ComponentTreeQuery componentTreeQuery);
/**
* Return all project (PRJ/TRK) uuids
@@ -83,7 +98,7 @@ public interface ComponentMapper {
* Return all descendant modules (including itself) from a given component uuid and scope
*/
List<ComponentDto> selectDescendantModules(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope,
- @Param(value = "excludeDisabled") boolean excludeDisabled);
+ @Param(value = "excludeDisabled") boolean excludeDisabled);
/**
* Return all files from a given project uuid and scope
@@ -94,7 +109,7 @@ public interface ComponentMapper {
* Return all descendant files from a given module uuid and scope
*/
List<FilePathWithHashDto> selectDescendantFiles(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope,
- @Param(value = "excludeDisabled") boolean excludeDisabled);
+ @Param(value = "excludeDisabled") boolean excludeDisabled);
/**
* Return uuids and project uuids from list of qualifiers
@@ -109,7 +124,7 @@ public interface ComponentMapper {
* @param scope scope of components to return. If null, all components are returned
*/
List<ComponentDto> selectComponentsFromProjectKeyAndScope(@Param("projectKey") String projectKey, @Nullable @Param("scope") String scope,
- @Param(value = "excludeDisabled") boolean excludeDisabled);
+ @Param(value = "excludeDisabled") boolean excludeDisabled);
/**
* Return technical projects from a view or a sub-view
@@ -133,5 +148,4 @@ public interface ComponentMapper {
void update(ComponentDto componentDto);
void delete(long componentId);
-
}
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java
new file mode 100644
index 00000000000..749c21e3aff
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java
@@ -0,0 +1,188 @@
+/*
+ * 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.db.component;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.sonar.db.WildcardPosition;
+
+import static com.google.common.collect.FluentIterable.from;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.db.DatabaseUtils.buildLikeValue;
+import static org.sonar.db.WildcardPosition.AFTER;
+
+public class ComponentTreeQuery {
+ @CheckForNull
+ private final String nameOrKeyQuery;
+ @CheckForNull
+ private final Collection<String> qualifiers;
+ @CheckForNull
+ private final Integer page;
+ @CheckForNull
+ private final Integer pageSize;
+ private final SnapshotDto baseSnapshot;
+ private final String baseSnapshotPath;
+ private final String sqlSort;
+ private final String direction;
+
+ private ComponentTreeQuery(Builder builder) {
+ this.nameOrKeyQuery = builder.nameOrKeyQuery;
+ this.qualifiers = builder.qualifiers;
+ this.page = builder.page;
+ this.pageSize = builder.pageSize;
+ this.baseSnapshot = builder.baseSnapshot;
+ this.baseSnapshotPath = buildLikeValue(baseSnapshot.getPath() + baseSnapshot.getId() + ".", WildcardPosition.AFTER);
+ this.direction = builder.asc ? "ASC" : "DESC";
+ this.sqlSort = sortFieldsToSqlSort(builder.sortFields, direction);
+ }
+
+ public Collection<String> getQualifiers() {
+ return qualifiers;
+ }
+
+ public String getNameOrKeyQuery() {
+ return nameOrKeyQuery;
+ }
+
+ @CheckForNull
+ public String getNameOrKeyQueryToSqlForResourceIndex() {
+ return nameOrKeyQuery == null ? null : buildLikeValue(nameOrKeyQuery, AFTER).toLowerCase();
+ }
+
+ @CheckForNull
+ public String getNameOrKeyQueryToSqlForProjectKey() {
+ return nameOrKeyQuery == null ? null : buildLikeValue(nameOrKeyQuery, AFTER);
+ }
+
+ public Integer getPage() {
+ return page;
+ }
+
+ public Integer getPageSize() {
+ return pageSize;
+ }
+
+ public SnapshotDto getBaseSnapshot() {
+ return baseSnapshot;
+ }
+
+ public String getBaseSnapshotPath() {
+ return baseSnapshotPath;
+ }
+
+ public String getSqlSort() {
+ return sqlSort;
+ }
+
+ public String getDirection() {
+ return direction;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private String sortFieldsToSqlSort(List<String> sortFields, String direction) {
+ List<String> sqlSortFields = from(sortFields)
+ .transform(new SortFieldToSqlSortFieldFunction(direction)).toList();
+
+ return Joiner.on(", ").join(sqlSortFields);
+ }
+
+ public static class Builder {
+ @CheckForNull
+ private String nameOrKeyQuery;
+ @CheckForNull
+ private Collection<String> qualifiers;
+ @CheckForNull
+ private Integer page;
+ @CheckForNull
+ private Integer pageSize;
+ private SnapshotDto baseSnapshot;
+ private List<String> sortFields;
+ private boolean asc = true;
+
+ private Builder() {
+ // private constructor
+ }
+
+ public ComponentTreeQuery build() {
+ requireNonNull(baseSnapshot);
+ return new ComponentTreeQuery(this);
+ }
+
+ public Builder setNameOrKeyQuery(@Nullable String nameOrKeyQuery) {
+ this.nameOrKeyQuery = nameOrKeyQuery;
+ return this;
+ }
+
+ public Builder setQualifiers(Collection<String> qualifiers) {
+ this.qualifiers = qualifiers;
+ return this;
+ }
+
+ public Builder setPage(int page) {
+ this.page = page;
+ return this;
+ }
+
+ public Builder setPageSize(int pageSize) {
+ this.pageSize = pageSize;
+ return this;
+ }
+
+ public Builder setBaseSnapshot(SnapshotDto baseSnapshot) {
+ this.baseSnapshot = baseSnapshot;
+ return this;
+ }
+
+ public Builder setSortFields(List<String> sorts) {
+ this.sortFields = requireNonNull(sorts);
+ return this;
+ }
+
+ public Builder setAsc(boolean asc) {
+ this.asc = asc;
+ return this;
+ }
+ }
+
+ private static class SortFieldToSqlSortFieldFunction implements Function<String, String> {
+ private static final String PATTERN = "LOWER(p.%1$s) %2$s, p.%1$s %2$s";
+
+ private final String direction;
+
+ private SortFieldToSqlSortFieldFunction(String direction) {
+ this.direction = direction;
+ }
+
+ @Nonnull
+ @Override
+ public String apply(@Nonnull String input) {
+ return String.format(PATTERN, input, direction);
+ }
+ }
+}
diff --git a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml
index c9c9205ec8e..f88928f83ca 100644
--- a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml
+++ b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml
@@ -280,25 +280,84 @@
AND p.enabled=${_true}
AND p.copy_resource_id is null
AND p.qualifier in
- <foreach collection="qualifiers" item="qualifier" open="(" close=")" separator=",">
+ <foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=",">
#{qualifier}
</foreach>
- <if test="nameOrKeyQuery!=null">
+ <if test="query.nameOrKeyQuery!=null">
AND (exists (
select 1
from resource_index ri
where
ri.resource_id=p.id
AND ri.qualifier in
- <foreach collection="qualifiers" item="qualifier" open="(" close=")" separator=",">
+ <foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=",">
#{qualifier}
</foreach>
- AND ri.kee like #{nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/')
- OR p.kee like #{nameOrKeyQueryToSqlForProjectKey} ESCAPE '/')
+ AND ri.kee like #{query.nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/')
+ OR p.kee like #{query.nameOrKeyQueryToSqlForProjectKey} ESCAPE '/')
</if>
</where>
</sql>
+ <select id="selectDirectChildren" resultType="Component">
+ select
+ <include refid="componentColumns"/>
+ <include refid="sqlSelectByTreeQuery"/>
+ and s.parent_snapshot_id = #{query.baseSnapshot.id}
+ order by ${query.sqlSort}
+ </select>
+
+ <select id="countDirectChildren" resultType="int">
+ select count(p.id)
+ <include refid="sqlSelectByTreeQuery"/>
+ and s.parent_snapshot_id = #{query.baseSnapshot.id}
+ </select>
+
+ <select id="selectAllChildren" resultType="Component">
+ select
+ <include refid="componentColumns"/>
+ <include refid="sqlSelectAllChildren" />
+ order by ${query.sqlSort}
+ </select>
+
+ <select id="countAllChildren" resultType="int">
+ select count(p.id)
+ <include refid="sqlSelectAllChildren"/>
+ </select>
+
+ <sql id="sqlSelectAllChildren">
+ <include refid="sqlSelectByTreeQuery"/>
+ <if test="query.baseSnapshot.rootId!=null">
+ and s.root_snapshot_id = #{query.baseSnapshot.rootId}
+ </if>
+ <if test="query.baseSnapshot.rootId==null">
+ and s.root_snapshot_id = #{query.baseSnapshot.id}
+ </if>
+ and s.path like #{query.baseSnapshotPath} ESCAPE '/'
+ </sql>
+
+ <sql id="sqlSelectByTreeQuery">
+ from projects p
+ inner join snapshots s on p.id = s.project_id
+ where
+ p.enabled=${_true}
+ <if test="query.qualifiers!=null">
+ AND p.qualifier in
+ <foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=",">
+ #{qualifier}
+ </foreach>
+ </if>
+ <if test="query.nameOrKeyQuery!=null">
+ AND (exists (
+ select 1
+ from resource_index ri
+ where
+ ri.resource_id=p.id
+ AND ri.kee like #{query.nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/')
+ OR p.kee like #{query.nameOrKeyQueryToSqlForProjectKey} ESCAPE '/')
+ </if>
+ </sql>
+
<select id="countRootComponents" resultType="int">
select count(p.id)
from projects p
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java
index 129a2c4f117..5cf3d42c5f7 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java
@@ -42,6 +42,8 @@ import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.guava.api.Assertions.assertThat;
import static org.sonar.db.component.ComponentTesting.newDeveloper;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newProjectDto;
import static org.sonar.db.component.ComponentTesting.newView;
@@ -672,11 +674,11 @@ public class ComponentDaoTest {
@Test
public void select_by_query_with_paging_query_and_qualifiers() {
- componentDb.insertProjectAndSnapshot(dbSession, newProjectDto().setName("aaaa-name"));
- componentDb.insertProjectAndSnapshot(dbSession, newView());
- componentDb.insertProjectAndSnapshot(dbSession, newDeveloper("project-name"));
+ componentDb.insertProjectAndSnapshot(newProjectDto().setName("aaaa-name"));
+ componentDb.insertProjectAndSnapshot(newView());
+ componentDb.insertProjectAndSnapshot(newDeveloper("project-name"));
for (int i = 9; i >= 1; i--) {
- componentDb.insertProjectAndSnapshot(dbSession, newProjectDto().setName("project-" + i));
+ componentDb.insertProjectAndSnapshot(newProjectDto().setName("project-" + i));
}
db.commit();
componentDb.indexProjects();
@@ -691,7 +693,7 @@ public class ComponentDaoTest {
@Test
public void select_by_query_name_with_special_characters() {
- componentDb.insertProjectAndSnapshot(dbSession, newProjectDto().setName("project-\\_%/-name"));
+ componentDb.insertProjectAndSnapshot(newProjectDto().setName("project-\\_%/-name"));
db.commit();
componentDb.indexProjects();
@@ -704,7 +706,7 @@ public class ComponentDaoTest {
@Test
public void select_by_query_key_with_special_characters() {
- componentDb.insertProjectAndSnapshot(dbSession, newProjectDto()
+ componentDb.insertProjectAndSnapshot(newProjectDto()
.setKey("project-_%-key"));
db.commit();
componentDb.indexProjects();
@@ -715,4 +717,181 @@ public class ComponentDaoTest {
assertThat(result).hasSize(1);
assertThat(result.get(0).key()).isEqualTo("project-_%-key");
}
+
+ @Test
+ public void select_direct_children_of_a_project() {
+ ComponentDto project = newProjectDto().setKey("project-key").setUuid("project-uuid");
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto moduleSnapshot = componentDb.insertComponentAndSnapshot(newModuleDto("module-1-uuid", project), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-1-uuid"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-2-uuid"), moduleSnapshot);
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot).build();
+
+ List<ComponentDto> result = underTest.selectDirectChildren(dbSession, query);
+ int count = underTest.countDirectChildren(dbSession, query);
+
+ assertThat(count).isEqualTo(2);
+ assertThat(result).extracting("uuid").containsExactly("file-1-uuid", "module-1-uuid");
+ }
+
+ @Test
+ public void select_direct_children_with_name_query() {
+ ComponentDto project = newProjectDto().setKey("project-key").setUuid("project-uuid");
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto moduleSnapshot = componentDb.insertComponentAndSnapshot(newModuleDto("module-1-uuid", project), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-1-uuid").setName("file-name-1"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-2-uuid").setName("file-name-2"), moduleSnapshot);
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot)
+ .setNameOrKeyQuery("file-name").build();
+
+ List<ComponentDto> result = underTest.selectDirectChildren(dbSession, query);
+ int count = underTest.countDirectChildren(dbSession, query);
+
+ assertThat(count).isEqualTo(1);
+ assertThat(result).extracting("uuid").containsExactly("file-1-uuid");
+ }
+
+ @Test
+ public void select_direct_children_with_key_query() {
+ ComponentDto project = newProjectDto().setKey("project-key").setUuid("project-uuid");
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto moduleSnapshot = componentDb.insertComponentAndSnapshot(newModuleDto("module-1-uuid", project), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-1-uuid").setKey("file-key-1"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-2-uuid").setKey("file-key-2"), moduleSnapshot);
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot)
+ .setNameOrKeyQuery("file-key").build();
+
+ List<ComponentDto> result = underTest.selectDirectChildren(dbSession, query);
+ int count = underTest.countDirectChildren(dbSession, query);
+
+ assertThat(count).isEqualTo(1);
+ assertThat(result).extracting("uuid").containsExactly("file-1-uuid");
+ }
+
+ @Test
+ public void select_direct_children_with_pagination() {
+ ComponentDto project = newProjectDto().setKey("project-key").setUuid("project-uuid");
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ for (int i = 1; i <= 9; i++) {
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid-" + i), projectSnapshot);
+ }
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot)
+ .setPage(2)
+ .setPageSize(3)
+ .setAsc(false)
+ .build();
+
+ List<ComponentDto> result = underTest.selectDirectChildren(dbSession, query);
+ int count = underTest.countDirectChildren(dbSession, query);
+
+ assertThat(count).isEqualTo(9);
+ assertThat(result).extracting("uuid").containsExactly("file-uuid-6", "file-uuid-5", "file-uuid-4");
+ }
+
+ @Test
+ public void select_direct_children_with_order_by_path() {
+ ComponentDto project = newProjectDto();
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid-1").setName("file-name-1").setPath("3"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid-2").setName("file-name-2").setPath("2"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid-3").setName("file-name-3").setPath("1"), projectSnapshot);
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot)
+ .setSortFields(singletonList("path"))
+ .setAsc(true)
+ .build();
+
+ List<ComponentDto> result = underTest.selectDirectChildren(dbSession, query);
+
+ assertThat(result).extracting("uuid").containsExactly("file-uuid-3", "file-uuid-2", "file-uuid-1");
+ }
+
+ @Test
+ public void select_direct_children_of_a_module() {
+ ComponentDto project = newProjectDto();
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto moduleSnapshot = componentDb.insertComponentAndSnapshot(newModuleDto("module-1-uuid", project), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-1-uuid"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-2-uuid"), moduleSnapshot);
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(moduleSnapshot).build();
+
+ List<ComponentDto> result = underTest.selectDirectChildren(dbSession, query);
+
+ assertThat(result).extracting("uuid").containsOnly("file-2-uuid");
+ }
+
+ @Test
+ public void select_all_children_of_a_project() {
+ ComponentDto project = newProjectDto().setKey("project-key").setUuid("project-uuid");
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto moduleSnapshot = componentDb.insertComponentAndSnapshot(newModuleDto("module-1-uuid", project), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-1-uuid"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-2-uuid"), moduleSnapshot);
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot).build();
+
+ List<ComponentDto> result = underTest.selectAllChildren(dbSession, query);
+ int count = underTest.countAllChildren(dbSession, query);
+
+ assertThat(count).isEqualTo(3);
+ assertThat(result).extracting("uuid").containsExactly("file-1-uuid", "file-2-uuid", "module-1-uuid");
+ }
+
+ @Test
+ public void select_all_files_of_a_project_paginated_and_ordered() {
+ ComponentDto project = newProjectDto().setKey("project-key").setUuid("project-uuid");
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto moduleSnapshot = componentDb.insertComponentAndSnapshot(newModuleDto("module-1-uuid", project), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid-1").setName("file-name-1"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "another-uuid"), projectSnapshot);
+ for (int i = 2; i <= 9; i++) {
+ componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid-" + i).setName("file-name-" + i), moduleSnapshot);
+ }
+ db.commit();
+ componentDb.indexProjects();
+
+ ComponentTreeQuery query = newTreeQuery(projectSnapshot)
+ .setQualifiers(newArrayList(Qualifiers.FILE))
+ .setPage(2)
+ .setPageSize(3)
+ .setNameOrKeyQuery("file-name")
+ .setSortFields(singletonList("name"))
+ .setAsc(false)
+ .build();
+
+ List<ComponentDto> result = underTest.selectAllChildren(dbSession, query);
+ int count = underTest.countAllChildren(dbSession, query);
+
+ assertThat(count).isEqualTo(9);
+ assertThat(result).extracting("uuid").containsExactly("file-uuid-6", "file-uuid-5", "file-uuid-4");
+ }
+
+ private static ComponentTreeQuery.Builder newTreeQuery(SnapshotDto baseSnapshot) {
+ return ComponentTreeQuery.builder()
+ .setPage(1)
+ .setPageSize(500)
+ .setBaseSnapshot(baseSnapshot)
+ .setSortFields(singletonList("name"))
+ .setAsc(true)
+ .setQualifiers(newArrayList(Qualifiers.FILE, Qualifiers.MODULE, Qualifiers.DIRECTORY, Qualifiers.PROJECT));
+ }
}
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java
index 731a05a866a..74932cfe65e 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java
@@ -24,7 +24,9 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import static org.sonar.db.component.SnapshotTesting.createForComponent;
import static org.sonar.db.component.SnapshotTesting.newSnapshotForProject;
+import static org.sonar.db.component.SnapshotTesting.newSnapshotForView;
public class ComponentDbTester {
private final DbTester db;
@@ -37,9 +39,28 @@ public class ComponentDbTester {
this.dbSession = db.getSession();
}
- public void insertProjectAndSnapshot(DbSession dbSession, ComponentDto component) {
+ public SnapshotDto insertProjectAndSnapshot(ComponentDto component) {
dbClient.componentDao().insert(dbSession, component);
- dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(component));
+ SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(component));
+ db.commit();
+
+ return snapshot;
+ }
+
+ public SnapshotDto insertViewAndSnapshot(ComponentDto component) {
+ dbClient.componentDao().insert(dbSession, component);
+ SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForView(component));
+ db.commit();
+
+ return snapshot;
+ }
+
+ public SnapshotDto insertComponentAndSnapshot(ComponentDto component, SnapshotDto parentSnapshot) {
+ dbClient.componentDao().insert(dbSession, component);
+ SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, createForComponent(component, parentSnapshot));
+ db.commit();
+
+ return snapshot;
}
public ComponentDto insertComponent(ComponentDto component) {
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java
index 11c3cebbc8c..8ac8cb816b2 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java
@@ -20,6 +20,7 @@
package org.sonar.db.component;
+import java.util.Date;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.core.util.Uuids;
@@ -42,6 +43,7 @@ public class ComponentTesting {
.setScope(Scopes.FILE)
.setQualifier(Qualifiers.FILE)
.setPath(path)
+ .setCreatedAt(new Date())
.setLanguage("xoo");
}
@@ -168,6 +170,7 @@ public class ComponentTesting {
.setModuleUuid(module.uuid())
.setModuleUuidPath(module.moduleUuidPath())
.setParentProjectId(module.getId())
+ .setCreatedAt(new Date())
.setEnabled(true);
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java
new file mode 100644
index 00000000000..c3a1695dac4
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.db.component;
+
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ComponentTreeQueryTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void convert_sorts_in_sql_representation() {
+ ComponentTreeQuery result = ComponentTreeQuery.builder()
+ .setBaseSnapshot(new SnapshotDto())
+ .setSortFields(newArrayList("name", "path", "qualifier"))
+ .build();
+
+ assertThat(result.getSqlSort()).isEqualTo("LOWER(p.name) ASC, p.name ASC, LOWER(p.path) ASC, p.path ASC, LOWER(p.qualifier) ASC, p.qualifier ASC");
+ }
+
+ @Test
+ public void fail_if_no_base_snapshot() {
+ expectedException.expect(NullPointerException.class);
+
+ ComponentTreeQuery.builder()
+ .setSortFields(Collections.<String>emptyList())
+ .build();
+ }
+
+ @Test
+ public void fail_if_no_sort() {
+ expectedException.expect(NullPointerException.class);
+
+ ComponentTreeQuery.builder()
+ .setBaseSnapshot(new SnapshotDto())
+ .build();
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ResourceTypesRule.java b/sonar-db/src/test/java/org/sonar/db/component/ResourceTypesRule.java
index e305ca60a63..23d00b9f211 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ResourceTypesRule.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ResourceTypesRule.java
@@ -20,6 +20,8 @@
package org.sonar.db.component;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -31,6 +33,8 @@ import org.sonar.api.resources.ResourceTypes;
public class ResourceTypesRule extends ResourceTypes {
private final Set<ResourceType> allResourceTypes = new HashSet<>();
private final Set<ResourceType> rootResourceTypes = new HashSet<>();
+ private final List<String> childrenQualifiers = new ArrayList<>();
+ private final List<String> leavesQualifiers = new ArrayList<>();
@Override
public Collection<ResourceType> getAll() {
@@ -51,6 +55,19 @@ public class ResourceTypesRule extends ResourceTypes {
return this;
}
+ public ResourceTypesRule setLeavesQualifiers(String... qualifiers) {
+ leavesQualifiers.clear();
+ leavesQualifiers.addAll(Arrays.asList(qualifiers));
+ return this;
+ }
+
+ public ResourceTypesRule setChildrenQualifiers(String... qualifiers) {
+ childrenQualifiers.clear();
+ childrenQualifiers.addAll(Arrays.asList(qualifiers));
+
+ return this;
+ }
+
public ResourceTypesRule setAllQualifiers(String... qualifiers) {
allResourceTypes.clear();
for (String qualifier : qualifiers) {
@@ -82,7 +99,7 @@ public class ResourceTypesRule extends ResourceTypes {
@Override
public List<String> getChildrenQualifiers(String qualifier) {
- throw new UnsupportedOperationException();
+ return this.childrenQualifiers;
}
@Override
@@ -92,7 +109,7 @@ public class ResourceTypesRule extends ResourceTypes {
@Override
public List<String> getLeavesQualifiers(String qualifier) {
- throw new UnsupportedOperationException();
+ return this.leavesQualifiers;
}
@Override
diff --git a/sonar-db/src/test/java/org/sonar/db/component/SnapshotTesting.java b/sonar-db/src/test/java/org/sonar/db/component/SnapshotTesting.java
index 65d2aca5c72..727adc6f0f4 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/SnapshotTesting.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/SnapshotTesting.java
@@ -20,20 +20,22 @@
package org.sonar.db.component;
-import com.google.common.base.Preconditions;
import org.assertj.core.util.Strings;
+import static com.google.common.base.Preconditions.checkNotNull;
+
public class SnapshotTesting {
/**
* Can be used for modules and files
*/
public static SnapshotDto createForComponent(ComponentDto component, SnapshotDto parentSnapshot) {
- Preconditions.checkNotNull(parentSnapshot.getId(), "The parent snapshot need to be persisted before creating this snapshot");
+ checkNotNull(parentSnapshot.getId(), "The parent snapshot need to be persisted before creating this snapshot");
Long parentRootId = parentSnapshot.getRootId();
return createBasicSnapshot(component, parentSnapshot.getRootProjectId())
.setRootId(parentRootId != null ? parentRootId : parentSnapshot.getId())
.setParentId(parentSnapshot.getId())
+ .setDepth(parentSnapshot.getDepth()+1)
.setPath(
Strings.isNullOrEmpty(parentSnapshot.getPath()) ? Long.toString(parentSnapshot.getId()) + "." : parentSnapshot.getPath() + Long.toString(parentSnapshot.getId()) + ".");
}
@@ -57,8 +59,8 @@ public class SnapshotTesting {
}
private static SnapshotDto createBasicSnapshot(ComponentDto component, Long rootProjectId) {
- Preconditions.checkNotNull(component.getId(), "The project need to be persisted before creating this snapshot");
- Preconditions.checkNotNull(rootProjectId, "Root project id is null");
+ checkNotNull(component.getId(), "The project need to be persisted before creating this snapshot");
+ checkNotNull(rootProjectId, "Root project id is null");
return new SnapshotDto()
.setComponentId(component.getId())
.setRootProjectId(rootProjectId)