]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8803 Sort by quality gate status 1691/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 20 Feb 2017 16:05:11 +0000 (17:05 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 22 Feb 2017 10:14:51 +0000 (11:14 +0100)
server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java
server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java
server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresQueryTest.java

index cc78e7d29cec4fef7fd18c1adac89da374b9e0b0..e6547312bb9bf6de10b583c256db5b5b3ef6d7fa 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.db.measure;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -323,7 +324,7 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     }
 
     Measures setLanguageDistribution(String languageDistributionValue) {
-      this.languageDistribution = parseStringInt(languageDistributionValue);
+      this.languageDistribution = ImmutableMap.copyOf(parseStringInt(languageDistributionValue));
       return this;
     }
 
index 3a2d804fa86b0cfd5a36c1717060ff9ce8325758..6a349b316145244e83a9fc960476d3618e720cbb 100644 (file)
@@ -31,7 +31,6 @@ import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collector;
 import java.util.stream.Stream;
-import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.server.ws.Change;
@@ -58,10 +57,10 @@ import org.sonarqube.ws.Common;
 import org.sonarqube.ws.WsComponents.Component;
 import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse;
 import org.sonarqube.ws.client.component.SearchProjectsRequest;
-import org.sonarqube.ws.client.project.ProjectsWsParameters;
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static java.lang.String.format;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 import static org.sonar.core.util.stream.Collectors.toSet;
 import static org.sonar.server.component.ws.ProjectMeasuresQueryFactory.IS_FAVORITE_CRITERION;
@@ -74,6 +73,7 @@ import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FIL
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.component.SearchProjectsRequest.DEFAULT_PAGE_SIZE;
 import static org.sonarqube.ws.client.component.SearchProjectsRequest.MAX_PAGE_SIZE;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_LANGUAGE;
 
 public class SearchProjectsAction implements ComponentsWsAction {
 
@@ -98,7 +98,7 @@ public class SearchProjectsAction implements ComponentsWsAction {
       .setInternal(true)
       .setResponseExample(getClass().getResource("search_projects-example.json"))
       .setChangelog(
-        new Change("6.4", format("The '%s' parameter accepts '%s' to filter by language", ProjectsWsParameters.FILTER_LANGUAGE, PARAM_FILTER)))
+        new Change("6.4", format("The '%s' parameter accepts '%s' to filter by language", FILTER_LANGUAGE, PARAM_FILTER)))
       .setHandler(this);
 
     action.createParam(PARAM_ORGANIZATION)
@@ -146,8 +146,8 @@ public class SearchProjectsAction implements ComponentsWsAction {
         "Use the WS api/languages/list to find the key of a language.");
 
     action.createParam(Param.SORT)
-      .setDescription("Sort projects by numeric metric key or by name.<br/>" +
-        "See '%s' parameter description for the possible metric values", PARAM_FILTER)
+      .setDescription("Sort projects by numeric metric key, quality gate status (using '%s'), or by project name.<br/>" +
+        "See '%s' parameter description for the possible metric values", ALERT_STATUS_KEY, PARAM_FILTER)
       .setDefaultValue(SORT_BY_NAME)
       .setExampleValue(NCLOC_KEY)
       .setSince("6.4");
@@ -194,11 +194,8 @@ public class SearchProjectsAction implements ComponentsWsAction {
 
   private SearchResults searchData(DbSession dbSession, SearchProjectsRequest request, @Nullable OrganizationDto organization) {
     Set<String> favoriteProjectUuids = loadFavoriteProjectUuids(dbSession);
-
     List<Criterion> criteria = FilterParser.parse(firstNonNull(request.getFilter(), ""));
-    Set<String> projectUuids = buildFilterOnFavoriteProjectUuids(criteria, favoriteProjectUuids);
-
-    ProjectMeasuresQuery query = newProjectMeasuresQuery(criteria, projectUuids)
+    ProjectMeasuresQuery query = newProjectMeasuresQuery(criteria, hasFavoriteFilter(criteria) ? favoriteProjectUuids : null)
       .setSort(request.getSort())
       .setAsc(request.getAsc());
     Optional.ofNullable(organization)
@@ -217,14 +214,10 @@ public class SearchProjectsAction implements ComponentsWsAction {
     return new SearchResults(projects, favoriteProjectUuids, esResults);
   }
 
-  @CheckForNull
-  private Set<String> buildFilterOnFavoriteProjectUuids(List<Criterion> criteria, Set<String> favoriteProjectUuids) {
-    if (criteria.stream()
+  private static boolean hasFavoriteFilter(List<Criterion> criteria) {
+    return criteria.stream()
       .map(Criterion::getKey)
-      .anyMatch(IS_FAVORITE_CRITERION::equalsIgnoreCase)) {
-      return favoriteProjectUuids;
-    }
-    return null;
+      .anyMatch(IS_FAVORITE_CRITERION::equalsIgnoreCase);
   }
 
   private Set<String> loadFavoriteProjectUuids(DbSession dbSession) {
index 734d717a83b8228a8a8057972d3453ae5902d2cd..8571ab16f17c609351eb881953f82caab218a126 100644 (file)
@@ -128,17 +128,20 @@ public class ProjectMeasuresIndex extends BaseIndex {
 
   private static void addSort(ProjectMeasuresQuery query, SearchRequestBuilder requestBuilder) {
     String sort = query.getSort();
-    if (sort == null || SORT_BY_NAME.equals(sort)) {
+    if (SORT_BY_NAME.equals(sort)) {
       requestBuilder.addSort(DefaultIndexSettingsElement.SORTABLE_ANALYZER.subField(FIELD_NAME), query.isAsc() ? ASC : DESC);
+    } else if (ALERT_STATUS_KEY.equals(sort)) {
+      requestBuilder.addSort(FIELD_QUALITY_GATE_STATUS, query.isAsc() ? ASC : DESC);
+      requestBuilder.addSort(DefaultIndexSettingsElement.SORTABLE_ANALYZER.subField(FIELD_NAME), ASC);
     } else {
-      addNameSort(query, requestBuilder, sort);
+      addMetricSort(query, requestBuilder, sort);
       requestBuilder.addSort(DefaultIndexSettingsElement.SORTABLE_ANALYZER.subField(FIELD_NAME), ASC);
     }
     // last sort is by key in order to be deterministic when same value
     requestBuilder.addSort(FIELD_KEY, ASC);
   }
 
-  private static void addNameSort(ProjectMeasuresQuery query, SearchRequestBuilder requestBuilder, String sort) {
+  private static void addMetricSort(ProjectMeasuresQuery query, SearchRequestBuilder requestBuilder, String sort) {
     requestBuilder.addSort(
       new FieldSortBuilder(FIELD_MEASURES_VALUE)
         .setNestedPath(FIELD_MEASURES)
index bbc9f75ef1921611793675a1502a2c4099c09891..2bc12465beb39045a3cb9abfc5b934c7d4b5d1c3 100644 (file)
@@ -23,7 +23,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.measures.Metric;
 
@@ -39,7 +38,7 @@ public class ProjectMeasuresQuery {
   private String organizationUuid;
   private Set<String> projectUuids;
   private Set<String> languages;
-  private String sort;
+  private String sort = SORT_BY_NAME;
   private boolean asc = true;
 
   public ProjectMeasuresQuery addMetricCriterion(MetricCriterion metricCriterion) {
@@ -87,13 +86,12 @@ public class ProjectMeasuresQuery {
     return Optional.ofNullable(languages);
   }
 
-  @CheckForNull
   public String getSort() {
     return sort;
   }
 
-  public ProjectMeasuresQuery setSort(@Nullable String sort) {
-    this.sort = sort;
+  public ProjectMeasuresQuery setSort(String sort) {
+    this.sort = requireNonNull(sort, "Sort cannot be null");
     return this;
   }
 
index 86d782657f5c8a9b0437a77ff131b90fcb7cdde7..ee945548d975f13f5e79fff9cc2da50b9a5221b1 100644 (file)
@@ -69,6 +69,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
 import static org.sonar.api.measures.Metric.ValueType.INT;
+import static org.sonar.api.measures.Metric.ValueType.LEVEL;
 import static org.sonar.api.server.ws.WebService.Param.ASCENDING;
 import static org.sonar.api.server.ws.WebService.Param.FACETS;
 import static org.sonar.api.server.ws.WebService.Param.PAGE;
@@ -93,6 +94,7 @@ public class SearchProjectsActionTest {
 
   private static final String NCLOC = "ncloc";
   private static final String COVERAGE = "coverage";
+  private static final String QUALITY_GATE_STATUS = "alert_status";
   private static final String IS_FAVOURITE_CRITERION = "isFavorite";
 
   @Rule
@@ -482,6 +484,22 @@ public class SearchProjectsActionTest {
       .containsExactly("Sonar Groovy", "Sonar Java", "Sonar Markdown", "Sonar Qube");
   }
 
+  @Test
+  public void sort_by_quality_gate() throws Exception {
+    OrganizationDto organization = db.getDefaultOrganization();
+    insertProjectInDbAndEs(newProjectDto(organization).setName("Sonar Java"), "ERROR");
+    insertProjectInDbAndEs(newProjectDto(organization).setName("Sonar Groovy"), "WARN");
+    insertProjectInDbAndEs(newProjectDto(organization).setName("Sonar Markdown"), "OK");
+    insertProjectInDbAndEs(newProjectDto(organization).setName("Sonar Qube"), "OK");
+    dbClient.metricDao().insert(dbSession, newMetricDto().setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()).setEnabled(true).setHidden(false));
+    db.commit();
+
+    assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(true)).getComponentsList()).extracting(Component::getName)
+      .containsExactly("Sonar Markdown", "Sonar Qube", "Sonar Groovy", "Sonar Java");
+    assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(false)).getComponentsList()).extracting(Component::getName)
+      .containsExactly("Sonar Java", "Sonar Groovy", "Sonar Markdown", "Sonar Qube");
+  }
+
   @Test
   public void fail_when_metrics_are_unknown() {
     expectedException.expect(IllegalArgumentException.class);
index a53f9d2191551b77c21952de045fb243e9c8c40f..56708d2b8eb93f039149b93fc300248dd30cd9a5 100644 (file)
@@ -144,6 +144,35 @@ public class ProjectMeasuresIndexTest {
     assertResults(new ProjectMeasuresQuery().setSort("ncloc").setAsc(false), windows, apache1, apache2, apachee);
   }
 
+  @Test
+  public void sort_by_quality_gate_status() {
+    ComponentDto project4 = newProjectDto(ORG).setUuid("Project-4").setName("Project 4").setKey("key-4");
+    index(
+      newDoc(PROJECT1).setQualityGateStatus(OK.name()),
+      newDoc(PROJECT2).setQualityGateStatus(ERROR.name()),
+      newDoc(PROJECT3).setQualityGateStatus(WARN.name()),
+      newDoc(project4).setQualityGateStatus(OK.name()));
+
+    assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(true), PROJECT1, project4, PROJECT3, PROJECT2);
+    assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(false), PROJECT2, PROJECT3, PROJECT1, project4);
+  }
+
+  @Test
+  public void sort_by_quality_gate_status_then_by_name_then_by_key() {
+    ComponentDto windows = newProjectDto(ORG).setUuid("windows").setName("Windows").setKey("project1");
+    ComponentDto apachee = newProjectDto(ORG).setUuid("apachee").setName("apachee").setKey("project2");
+    ComponentDto apache1 = newProjectDto(ORG).setUuid("apache-1").setName("Apache").setKey("project3");
+    ComponentDto apache2 = newProjectDto(ORG).setUuid("apache-2").setName("Apache").setKey("project4");
+    index(
+      newDoc(windows).setQualityGateStatus(WARN.name()),
+      newDoc(apachee).setQualityGateStatus(OK.name()),
+      newDoc(apache1).setQualityGateStatus(OK.name()),
+      newDoc(apache2).setQualityGateStatus(OK.name()));
+
+    assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(true), apache1, apache2, apachee, windows);
+    assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(false), windows, apache1, apache2, apachee);
+  }
+
   @Test
   public void paginate_results() {
     IntStream.rangeClosed(1, 9)
index 1ce078b232f2d2e208d8dc3c57e7e010c307217b..27529639601986f8d708b3085039976ded706ce0 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.server.measure.index.ProjectMeasuresQuery.MetricCriterion;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.groups.Tuple.tuple;
 import static org.sonar.api.measures.Metric.Level.OK;
-import static org.sonar.server.component.ws.FilterParser.Operator;
 import static org.sonar.server.component.ws.FilterParser.Operator.EQ;
 
 public class ProjectMeasuresQueryTest {
@@ -61,13 +60,6 @@ public class ProjectMeasuresQueryTest {
     assertThat(underTest.getQualityGateStatus().get()).isEqualTo(Level.OK);
   }
 
-  @Test
-  public void fail_to_create_operator_from_unknown_value() throws Exception {
-    expectedException.expect(IllegalArgumentException.class);
-
-    Operator.valueOf("UNKNOWN");
-  }
-
   @Test
   public void default_sort_is_by_name() throws Exception {
     assertThat(underTest.getSort()).isEqualTo("name");