From 6fe8b0cd64c4b4e2386d994016b465b0386ed09c Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Thu, 17 Dec 2015 16:19:44 +0100 Subject: SONAR-7129 WS api/components/tree --- .../java/org/sonar/db/component/ComponentDao.java | 19 ++ .../org/sonar/db/component/ComponentMapper.java | 26 ++- .../org/sonar/db/component/ComponentTreeQuery.java | 188 ++++++++++++++++++++ .../org/sonar/db/component/ComponentMapper.xml | 69 +++++++- .../org/sonar/db/component/ComponentDaoTest.java | 191 ++++++++++++++++++++- .../org/sonar/db/component/ComponentDbTester.java | 25 ++- .../org/sonar/db/component/ComponentTesting.java | 3 + .../sonar/db/component/ComponentTreeQueryTest.java | 63 +++++++ .../org/sonar/db/component/ResourceTypesRule.java | 21 ++- .../org/sonar/db/component/SnapshotTesting.java | 10 +- 10 files changed, 590 insertions(+), 25 deletions(-) create mode 100644 sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java create mode 100644 sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java (limited to 'sonar-db') 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 selectDirectChildren(DbSession dbSession, ComponentTreeQuery componentQuery) { + RowBounds rowBounds = new RowBounds(offset(componentQuery.getPage(), componentQuery.getPageSize()), componentQuery.getPageSize()); + return mapper(dbSession).selectDirectChildren(componentQuery, rowBounds); + } + + public List 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> { 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 selectComponentsByQualifiers(@Param("qualifiers") Collection qualifiers); - List selectByQuery(ComponentQuery query, RowBounds rowBounds); + List selectByQuery(@Param("query") ComponentQuery query, RowBounds rowBounds); - int countByQuery(ComponentQuery query); + int countByQuery(@Param("query") ComponentQuery query); + + /** + * Return direct children components + */ + List selectDirectChildren(@Param("query") ComponentTreeQuery componentTreeQuery, RowBounds rowBounds); + + int countDirectChildren(@Param("query") ComponentTreeQuery componentTreeQuery); + + /** + * Return all children components. + */ + List 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 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 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 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 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 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 sortFields, String direction) { + List sqlSortFields = from(sortFields) + .transform(new SortFieldToSqlSortFieldFunction(direction)).toList(); + + return Joiner.on(", ").join(sqlSortFields); + } + + public static class Builder { + @CheckForNull + private String nameOrKeyQuery; + @CheckForNull + private Collection qualifiers; + @CheckForNull + private Integer page; + @CheckForNull + private Integer pageSize; + private SnapshotDto baseSnapshot; + private List 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 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 sorts) { + this.sortFields = requireNonNull(sorts); + return this; + } + + public Builder setAsc(boolean asc) { + this.asc = asc; + return this; + } + } + + private static class SortFieldToSqlSortFieldFunction implements Function { + 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 - + #{qualifier} - + AND (exists ( select 1 from resource_index ri where ri.resource_id=p.id AND ri.qualifier in - + #{qualifier} - 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 '/') + + + + + + + + + + + + and s.root_snapshot_id = #{query.baseSnapshot.rootId} + + + and s.root_snapshot_id = #{query.baseSnapshot.id} + + and s.path like #{query.baseSnapshotPath} ESCAPE '/' + + + + from projects p + inner join snapshots s on p.id = s.project_id + where + p.enabled=${_true} + + AND p.qualifier in + + #{qualifier} + + + + 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 '/') + + +