]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8089 Load all measures at once
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 4 Nov 2016 15:15:12 +0000 (16:15 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 8 Nov 2016 10:12:52 +0000 (11:12 +0100)
server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeSort.java
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java

index 93db78f2ab7e8e4032247896ea70b046515cbafb..cf14648c5e946d92e22b06cc46f79b7364d471aa 100644 (file)
  */
 package org.sonar.server.component.ws;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Ordering;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.List;
@@ -40,6 +43,7 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTreeQuery;
+import org.sonar.db.component.ComponentTreeQuery.Strategy;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.WsComponents.TreeWsResponse;
@@ -48,9 +52,13 @@ import org.sonarqube.ws.client.component.TreeWsRequest;
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.collect.FluentIterable.from;
 import static com.google.common.collect.Sets.newHashSet;
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
 import static java.lang.String.format;
 import static java.util.Collections.emptyMap;
+import static org.sonar.api.utils.Paging.offset;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
+import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN;
+import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES;
 import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
 import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
 import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
@@ -66,12 +74,17 @@ import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUA
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
 
 public class TreeAction implements ComponentsWsAction {
+
   private static final int MAX_SIZE = 500;
   private static final int QUERY_MINIMUM_LENGTH = 3;
   private static final String ALL_STRATEGY = "all";
   private static final String CHILDREN_STRATEGY = "children";
   private static final String LEAVES_STRATEGY = "leaves";
-  private static final Set<String> STRATEGIES = ImmutableSortedSet.of(ALL_STRATEGY, CHILDREN_STRATEGY, LEAVES_STRATEGY);
+  private static final Map<String, Strategy> STRATEGIES = ImmutableMap.of(
+    ALL_STRATEGY, LEAVES,
+    CHILDREN_STRATEGY, CHILDREN,
+    LEAVES_STRATEGY, LEAVES);
+
   private static final String NAME_SORT = "name";
   private static final String PATH_SORT = "path";
   private static final String QUALIFIER_SORT = "qualifier";
@@ -137,7 +150,7 @@ public class TreeAction implements ComponentsWsAction {
         "<li>all: return all the descendants components of the base component. Grandchildren are returned.</li>" +
         "<li>leaves: return all the descendant components (files, in general) which don't have other children. They are the leaves of the component tree.</li>" +
         "</ul>")
-      .setPossibleValues(STRATEGIES)
+      .setPossibleValues(STRATEGIES.keySet())
       .setDefaultValue(ALL_STRATEGY);
   }
 
@@ -148,33 +161,20 @@ public class TreeAction implements ComponentsWsAction {
   }
 
   private TreeWsResponse doHandle(TreeWsRequest treeWsRequest) {
-    DbSession dbSession = dbClient.openSession(false);
-    try {
+    try (DbSession dbSession = dbClient.openSession(false)) {
       ComponentDto baseComponent = componentFinder.getByUuidOrKey(dbSession, treeWsRequest.getBaseComponentId(), treeWsRequest.getBaseComponentKey(), BASE_COMPONENT_ID_AND_KEY);
       checkPermissions(baseComponent);
 
       ComponentTreeQuery query = toComponentTreeQuery(treeWsRequest, baseComponent);
-      List<ComponentDto> components;
-      int total;
-      switch (treeWsRequest.getStrategy()) {
-        case CHILDREN_STRATEGY:
-          components = dbClient.componentDao().selectDescendants(dbSession, query);
-          total = components.size();
-          break;
-        case LEAVES_STRATEGY:
-        case ALL_STRATEGY:
-          components = dbClient.componentDao().selectDescendants(dbSession, query);
-          total = components.size();
-          break;
-        default:
-          throw new IllegalStateException("Unknown component tree strategy");
-      }
+      List<ComponentDto> components = dbClient.componentDao().selectDescendants(dbSession, query);
+      int total = components.size();
+      components = sortComponents(components, treeWsRequest);
+      components = paginateComponents(components, treeWsRequest);
+
       Map<String, ComponentDto> referenceComponentsByUuid = searchReferenceComponentsByUuid(dbSession, components);
 
       return buildResponse(baseComponent, components, referenceComponentsByUuid,
-        Paging.forPageIndex(query.getPage()).withPageSize(query.getPageSize()).andTotal(total));
-    } finally {
-      dbClient.closeSession(dbSession);
+        Paging.forPageIndex(treeWsRequest.getPage()).withPageSize(treeWsRequest.getPageSize()).andTotal(total));
     }
   }
 
@@ -200,8 +200,7 @@ public class TreeAction implements ComponentsWsAction {
     }
   }
 
-  private static TreeWsResponse buildResponse(ComponentDto baseComponent, List<ComponentDto> components,
-    Map<String, ComponentDto> referenceComponentsByUuid, Paging paging) {
+  private static TreeWsResponse buildResponse(ComponentDto baseComponent, List<ComponentDto> components, Map<String, ComponentDto> referenceComponentsByUuid, Paging paging) {
     TreeWsResponse.Builder response = TreeWsResponse.newBuilder();
     response.getPagingBuilder()
       .setPageIndex(paging.pageIndex())
@@ -222,10 +221,7 @@ public class TreeAction implements ComponentsWsAction {
 
     ComponentTreeQuery.Builder query = ComponentTreeQuery.builder()
       .setBaseUuid(baseComponent.uuid())
-      .setPage(request.getPage())
-      .setPageSize(request.getPageSize())
-      .setSortFields(request.getSort())
-      .setAsc(request.getAsc());
+      .setStrategy(STRATEGIES.get(request.getStrategy()));
     if (request.getQuery() != null) {
       query.setNameOrKeyQuery(request.getQuery());
     }
@@ -261,7 +257,7 @@ public class TreeAction implements ComponentsWsAction {
     TreeWsRequest treeWsRequest = new TreeWsRequest()
       .setBaseComponentId(request.param(PARAM_BASE_COMPONENT_ID))
       .setBaseComponentKey(request.param(PARAM_BASE_COMPONENT_KEY))
-      .setStrategy(request.param(PARAM_STRATEGY))
+      .setStrategy(request.mandatoryParam(PARAM_STRATEGY))
       .setQuery(request.param(Param.TEXT_QUERY))
       .setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS))
       .setSort(request.mandatoryParamAsStrings(Param.SORT))
@@ -276,4 +272,43 @@ public class TreeAction implements ComponentsWsAction {
     return treeWsRequest;
   }
 
+  private static List<ComponentDto> paginateComponents(List<ComponentDto> components, TreeWsRequest wsRequest) {
+    return from(components)
+      .skip(offset(wsRequest.getPage(), wsRequest.getPageSize()))
+      .limit(wsRequest.getPageSize())
+      .toList();
+  }
+
+  public static List<ComponentDto> sortComponents(List<ComponentDto> components, TreeWsRequest wsRequest) {
+    List<String> sortParameters = wsRequest.getSort();
+    if (sortParameters == null || sortParameters.isEmpty()) {
+      return components;
+    }
+    boolean isAscending = wsRequest.getAsc();
+    Map<String, Ordering<ComponentDto>> orderingsBySortField = ImmutableMap.<String, Ordering<ComponentDto>>builder()
+      .put(NAME_SORT, stringOrdering(isAscending, ComponentDto::name))
+      .put(QUALIFIER_SORT, stringOrdering(isAscending, ComponentDto::qualifier))
+      .put(PATH_SORT, stringOrdering(isAscending, ComponentDto::path))
+      .build();
+
+    String firstSortParameter = sortParameters.get(0);
+    Ordering<ComponentDto> primaryOrdering = orderingsBySortField.get(firstSortParameter);
+    if (sortParameters.size() > 1) {
+      for (int i = 1; i < sortParameters.size(); i++) {
+        String secondarySortParameter = sortParameters.get(i);
+        Ordering<ComponentDto> secondaryOrdering = orderingsBySortField.get(secondarySortParameter);
+        primaryOrdering = primaryOrdering.compound(secondaryOrdering);
+      }
+    }
+    return primaryOrdering.immutableSortedCopy(components);
+  }
+
+  private static Ordering<ComponentDto> stringOrdering(boolean isAscending, Function<ComponentDto, String> function) {
+    Ordering<String> ordering = Ordering.from(CASE_INSENSITIVE_ORDER);
+    if (!isAscending) {
+      ordering = ordering.reverse();
+    }
+    return ordering.nullsLast().onResultOf(function);
+  }
+
 }
index d0fafede87cb50744824cfc23832e6d3b8da71cf..dbe62325ffaadccc42ef679c386369e848b518b6 100644 (file)
  */
 package org.sonar.server.measure.ws;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedSet;
+import java.util.Map;
+import java.util.Set;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.Paging;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTreeQuery.Strategy;
+import org.sonar.db.metric.MetricDto;
+import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.ComponentTreeWsResponse;
+import org.sonarqube.ws.client.measure.ComponentTreeWsRequest;
+
 import static java.lang.String.format;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
+import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN;
+import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES;
 import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
 import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
 import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
+import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
 import static org.sonar.server.ws.WsUtils.checkRequest;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT_TREE;
@@ -46,21 +66,6 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEGY;
 
-import com.google.common.collect.ImmutableSortedSet;
-import java.util.Set;
-import org.sonar.api.i18n.I18n;
-import org.sonar.api.resources.ResourceTypes;
-import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.Response;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.api.server.ws.WebService.Param;
-import org.sonar.api.utils.Paging;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.metric.MetricDto;
-import org.sonarqube.ws.WsMeasures;
-import org.sonarqube.ws.WsMeasures.ComponentTreeWsResponse;
-import org.sonarqube.ws.client.measure.ComponentTreeWsRequest;
-
 /**
  * <p>Navigate through components based on different strategy with specified measures.
  * To limit the number of rows in database, a best value algorithm exists in database.</p>
@@ -84,7 +89,10 @@ public class ComponentTreeAction implements MeasuresWsAction {
   static final String ALL_STRATEGY = "all";
   static final String CHILDREN_STRATEGY = "children";
   static final String LEAVES_STRATEGY = "leaves";
-  static final Set<String> STRATEGIES = ImmutableSortedSet.of(ALL_STRATEGY, CHILDREN_STRATEGY, LEAVES_STRATEGY);
+  static final Map<String, Strategy> STRATEGIES = ImmutableMap.of(
+    ALL_STRATEGY, LEAVES,
+    CHILDREN_STRATEGY, CHILDREN,
+    LEAVES_STRATEGY, LEAVES);
   // sort
   static final String NAME_SORT = "name";
   static final String PATH_SORT = "path";
@@ -176,7 +184,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
         "<li>all: return all the descendants components of the base component. Grandchildren are returned.</li>" +
         "<li>leaves: return all the descendant components (files, in general) which don't have other children. They are the leaves of the component tree.</li>" +
         "</ul>")
-      .setPossibleValues(STRATEGIES)
+      .setPossibleValues(STRATEGIES.keySet())
       .setDefaultValue(ALL_STRATEGY);
   }
 
index 04f5c44c397ced3b611fb044bfef89bb74000a13..201b1f2a6a97acad60517515cdb279343fa2233b 100644 (file)
@@ -26,12 +26,12 @@ import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Table;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -51,7 +51,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTreeQuery;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.measure.MeasureDto;
-import org.sonar.db.measure.MeasureQuery;
+import org.sonar.db.measure.MeasureTreeQuery;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.db.metric.MetricDtoFunctions;
 import org.sonar.server.component.ComponentFinder;
@@ -63,21 +63,15 @@ import org.sonarqube.ws.client.measure.ComponentTreeWsRequest;
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.FluentIterable.from;
-import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Sets.newHashSet;
 import static java.lang.String.format;
 import static java.util.Collections.emptyMap;
-import static java.util.Collections.singletonList;
 import static java.util.Objects.requireNonNull;
 import static org.sonar.api.utils.Paging.offset;
 import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
 import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY;
-import static org.sonar.server.measure.ws.ComponentTreeAction.ALL_STRATEGY;
-import static org.sonar.server.measure.ws.ComponentTreeAction.CHILDREN_STRATEGY;
 import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
-import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT;
-import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT;
-import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT;
+import static org.sonar.server.measure.ws.ComponentTreeAction.STRATEGIES;
 import static org.sonar.server.measure.ws.ComponentTreeAction.WITH_MEASURES_ONLY_METRIC_SORT_FILTER;
 import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
 import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
@@ -110,19 +104,18 @@ public class ComponentTreeDataLoader {
       }
       Long developerId = searchDeveloperId(dbSession, wsRequest);
 
-      ComponentTreeQuery dbQuery = toComponentTreeQuery(wsRequest, baseComponent);
-      ComponentDtosAndTotal componentDtosAndTotal = searchComponents(dbSession, dbQuery, wsRequest);
-      List<ComponentDto> components = componentDtosAndTotal.componentDtos;
+      ComponentTreeQuery componentTreeQuery = toComponentTreeQuery(wsRequest, baseComponent);
+      List<ComponentDto> components = searchComponents(dbSession, componentTreeQuery);
       List<MetricDto> metrics = searchMetrics(dbSession, wsRequest);
       List<WsMeasures.Period> periods = snapshotToWsPeriods(baseSnapshot.get());
-      Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, components, metrics,
-        periods, developerId);
+      Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery, components,
+        metrics, periods, developerId);
 
       components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest);
       components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
-      int componentCount = computeComponentCount(componentDtosAndTotal.total, components, componentWithMeasuresOnly(wsRequest));
+
+      int componentCount = components.size();
       components = paginateComponents(components, wsRequest);
-      Map<String, ComponentDto> referenceComponentsById = searchReferenceComponentsById(dbSession, components);
 
       return ComponentTreeData.builder()
         .setBaseComponent(baseComponent)
@@ -131,17 +124,13 @@ public class ComponentTreeDataLoader {
         .setMeasuresByComponentUuidAndMetric(measuresByComponentUuidAndMetric)
         .setMetrics(metrics)
         .setPeriods(periods)
-        .setReferenceComponentsByUuid(referenceComponentsById)
+        .setReferenceComponentsByUuid(searchReferenceComponentsById(dbSession, components))
         .build();
     } finally {
       dbClient.closeSession(dbSession);
     }
   }
 
-  private static int computeComponentCount(int dbComponentCount, List<ComponentDto> components, boolean returnOnlyComponentsWithMeasures) {
-    return returnOnlyComponentsWithMeasures ? components.size() : dbComponentCount;
-  }
-
   @CheckForNull
   private Long searchDeveloperId(DbSession dbSession, ComponentTreeWsRequest wsRequest) {
     if (wsRequest.getDeveloperId() == null && wsRequest.getDeveloperKey() == null) {
@@ -164,24 +153,12 @@ public class ComponentTreeDataLoader {
       .uniqueIndex(ComponentDto::uuid);
   }
 
-  private ComponentDtosAndTotal searchComponents(DbSession dbSession, ComponentTreeQuery dbQuery, ComponentTreeWsRequest wsRequest) {
-    if (dbQuery.getQualifiers() != null && dbQuery.getQualifiers().isEmpty()) {
-      return new ComponentDtosAndTotal(Collections.emptyList(), 0);
-    }
-    String strategy = requireNonNull(wsRequest.getStrategy());
-    switch (strategy) {
-      case CHILDREN_STRATEGY:
-        return new ComponentDtosAndTotal(
-          dbClient.componentDao().selectDescendants(dbSession, dbQuery),
-          0);
-      case LEAVES_STRATEGY:
-      case ALL_STRATEGY:
-        return new ComponentDtosAndTotal(
-          dbClient.componentDao().selectDescendants(dbSession, dbQuery),
-          0);
-      default:
-        throw new IllegalStateException("Unknown component tree strategy");
+  private List<ComponentDto> searchComponents(DbSession dbSession, ComponentTreeQuery componentTreeQuery) {
+    Collection<String> qualifiers = componentTreeQuery.getQualifiers();
+    if (qualifiers != null && qualifiers.isEmpty()) {
+      return Collections.emptyList();
     }
+    return dbClient.componentDao().selectDescendants(dbSession, componentTreeQuery);
   }
 
   private List<MetricDto> searchMetrics(DbSession dbSession, ComponentTreeWsRequest request) {
@@ -199,20 +176,18 @@ public class ComponentTreeDataLoader {
     return metrics;
   }
 
-  private Table<String, MetricDto, MeasureDto> searchMeasuresByComponentUuidAndMetric(DbSession dbSession, ComponentDto baseComponent,
-    List<ComponentDto> components, List<MetricDto> metrics,
-    List<WsMeasures.Period> periods, @Nullable Long developerId) {
-    List<String> componentUuids = new ArrayList<>();
-    componentUuids.add(baseComponent.uuid());
-    components.stream().forEach(c -> componentUuids.add(c.uuid()));
+  private Table<String, MetricDto, MeasureDto> searchMeasuresByComponentUuidAndMetric(DbSession dbSession, ComponentDto baseComponent, ComponentTreeQuery componentTreeQuery,
+    List<ComponentDto> components, List<MetricDto> metrics, List<WsMeasures.Period> periods, @Nullable Long developerId) {
 
     Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDtoFunctions.toId());
-    MeasureQuery measureQuery = MeasureQuery.builder()
+    MeasureTreeQuery measureQuery = MeasureTreeQuery.builder()
+      .setStrategy(MeasureTreeQuery.Strategy.valueOf(componentTreeQuery.getStrategy().name()))
+      .setNameOrKeyQuery(componentTreeQuery.getNameOrKeyQuery())
+      .setQualifiers(componentTreeQuery.getQualifiers())
       .setPersonId(developerId)
-      .setComponentUuids(baseComponent.projectUuid(), componentUuids)
       .setMetricIds(new ArrayList<>(metricsById.keySet()))
       .build();
-    List<MeasureDto> measureDtos = dbClient.measureDao().selectByQuery(dbSession, measureQuery);
+    List<MeasureDto> measureDtos = dbClient.measureDao().selectTreeByQuery(dbSession, baseComponent, measureQuery);
 
     Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric = HashBasedTable.create(components.size(), metrics.size());
     for (MeasureDto measureDto : measureDtos) {
@@ -276,29 +251,16 @@ public class ComponentTreeDataLoader {
 
   private static List<ComponentDto> sortComponents(List<ComponentDto> components, ComponentTreeWsRequest wsRequest, List<MetricDto> metrics,
     Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric) {
-    if (!isSortByMetric(wsRequest)) {
-      return components;
-    }
-
     return ComponentTreeSort.sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
   }
 
   private static List<ComponentDto> paginateComponents(List<ComponentDto> components, ComponentTreeWsRequest wsRequest) {
-    if (!isSortByMetric(wsRequest)) {
-      return components;
-    }
-
     return from(components)
       .skip(offset(wsRequest.getPage(), wsRequest.getPageSize()))
       .limit(wsRequest.getPageSize())
       .toList();
   }
 
-  private static boolean isSortByMetric(ComponentTreeWsRequest wsRequest) {
-    requireNonNull(wsRequest.getSort());
-    return wsRequest.getSort().contains(METRIC_SORT) || wsRequest.getSort().contains(METRIC_PERIOD_SORT);
-  }
-
   @CheckForNull
   private List<String> childrenQualifiers(ComponentTreeWsRequest request, String baseQualifier) {
     List<String> requestQualifiers = request.getQualifiers();
@@ -323,29 +285,17 @@ public class ComponentTreeDataLoader {
   private ComponentTreeQuery toComponentTreeQuery(ComponentTreeWsRequest wsRequest, ComponentDto baseComponent) {
     List<String> childrenQualifiers = childrenQualifiers(wsRequest, baseComponent.qualifier());
 
-    List<String> sortsWithoutMetricSort = newArrayList(Iterables.filter(wsRequest.getSort(), IsNotMetricSort.INSTANCE));
-    sortsWithoutMetricSort = sortsWithoutMetricSort.isEmpty() ? singletonList(NAME_SORT) : sortsWithoutMetricSort;
-
-    ComponentTreeQuery.Builder dbQuery = ComponentTreeQuery.builder()
+    ComponentTreeQuery.Builder componentTreeQueryBuilder = ComponentTreeQuery.builder()
       .setBaseUuid(baseComponent.uuid())
-      .setPage(wsRequest.getPage())
-      .setPageSize(wsRequest.getPageSize())
-      .setSortFields(sortsWithoutMetricSort)
-      .setAsc(wsRequest.getAsc());
+      .setStrategy(STRATEGIES.get(wsRequest.getStrategy()));
 
     if (wsRequest.getQuery() != null) {
-      dbQuery.setNameOrKeyQuery(wsRequest.getQuery());
+      componentTreeQueryBuilder.setNameOrKeyQuery(wsRequest.getQuery());
     }
     if (childrenQualifiers != null) {
-      dbQuery.setQualifiers(childrenQualifiers);
+      componentTreeQueryBuilder.setQualifiers(childrenQualifiers);
     }
-    // load all components if we must sort by metric value
-    if (isSortByMetric(wsRequest)) {
-      dbQuery.setPage(1);
-      dbQuery.setPageSize(Integer.MAX_VALUE);
-    }
-
-    return dbQuery.build();
+    return componentTreeQueryBuilder.build();
   }
 
   private void checkPermissions(ComponentDto baseComponent) {
@@ -356,16 +306,6 @@ public class ComponentTreeDataLoader {
     }
   }
 
-  private static class ComponentDtosAndTotal {
-    private final List<ComponentDto> componentDtos;
-    private final int total;
-
-    private ComponentDtosAndTotal(List<ComponentDto> componentDtos, int total) {
-      this.componentDtos = componentDtos;
-      this.total = total;
-    }
-  }
-
   private enum IsFileComponent implements Predicate<ComponentDto> {
     INSTANCE;
 
@@ -397,15 +337,6 @@ public class ComponentTreeDataLoader {
     }
   }
 
-  private enum IsNotMetricSort implements Predicate<String> {
-    INSTANCE;
-
-    @Override
-    public boolean apply(@Nonnull String input) {
-      return !input.equals(METRIC_SORT) && !input.equals(METRIC_PERIOD_SORT);
-    }
-  }
-
   private static class MatchMetricKey implements Predicate<MetricDto> {
     private final String metricKeyToSort;
 
index a833af62fed0d8c08d539e7abb25732581720fd3..be1d15aaf9788eb1b0299ef30b9194c0be84fdba 100644 (file)
@@ -57,7 +57,7 @@ import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT;
 import static org.sonar.server.measure.ws.ComponentTreeAction.PATH_SORT;
 import static org.sonar.server.measure.ws.ComponentTreeAction.QUALIFIER_SORT;
 
-class ComponentTreeSort {
+public class ComponentTreeSort {
 
   private static final Set<ValueType> NUMERIC_VALUE_TYPES = EnumSet.of(BOOL, FLOAT, INT, MILLISEC, WORK_DUR, PERCENT, RATING);
   private static final Set<ValueType> TEXTUAL_VALUE_TYPES = EnumSet.of(DATA, DISTRIB, STRING);
@@ -66,7 +66,7 @@ class ComponentTreeSort {
     // static method only
   }
 
-  static List<ComponentDto> sortComponents(List<ComponentDto> components, ComponentTreeWsRequest wsRequest, List<MetricDto> metrics,
+  public static List<ComponentDto> sortComponents(List<ComponentDto> components, ComponentTreeWsRequest wsRequest, List<MetricDto> metrics,
     Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric) {
     List<String> sortParameters = wsRequest.getSort();
     if (sortParameters == null || sortParameters.isEmpty()) {
@@ -95,15 +95,15 @@ class ComponentTreeSort {
   }
 
   private static Ordering<ComponentDto> componentNameOrdering(boolean isAscending) {
-    return stringOrdering(isAscending, ComponentDtoToName.INSTANCE);
+    return stringOrdering(isAscending, ComponentDto::name);
   }
 
   private static Ordering<ComponentDto> componentQualifierOrdering(boolean isAscending) {
-    return stringOrdering(isAscending, ComponentDtoToQualifier.INSTANCE);
+    return stringOrdering(isAscending, ComponentDto::qualifier);
   }
 
   private static Ordering<ComponentDto> componentPathOrdering(boolean isAscending) {
-    return stringOrdering(isAscending, ComponentDtoToPath.INSTANCE);
+    return stringOrdering(isAscending, ComponentDto::path);
   }
 
   private static Ordering<ComponentDto> stringOrdering(boolean isAscending, Function<ComponentDto, String> function) {
@@ -272,30 +272,4 @@ class ComponentTreeSort {
     }
   }
 
-  private enum ComponentDtoToName implements Function<ComponentDto, String> {
-    INSTANCE;
-
-    @Override
-    public String apply(@Nonnull ComponentDto input) {
-      return input.name();
-    }
-  }
-
-  private enum ComponentDtoToQualifier implements Function<ComponentDto, String> {
-    INSTANCE;
-
-    @Override
-    public String apply(@Nonnull ComponentDto input) {
-      return input.qualifier();
-    }
-  }
-
-  private enum ComponentDtoToPath implements Function<ComponentDto, String> {
-    INSTANCE;
-
-    @Override
-    public String apply(@Nonnull ComponentDto input) {
-      return input.path();
-    }
-  }
 }
index 8b46974206184063e41e07b73923e642c87c525a..098f917316bf9e0fe33e5d22a7d91b9eebea5e25 100644 (file)
@@ -287,6 +287,9 @@ public class ComponentTreeActionTest {
       .setParam(Param.PAGE_SIZE, "3"));
 
     assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-4", "file-uuid-5", "file-uuid-6");
+    assertThat(response.getPaging().getPageIndex()).isEqualTo(2);
+    assertThat(response.getPaging().getPageSize()).isEqualTo(3);
+    assertThat(response.getPaging().getTotal()).isEqualTo(9);
   }
 
   @Test