]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9476 Add from and to params in api/project_analyses/search
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 3 Jul 2017 16:03:09 +0000 (18:03 +0200)
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>
Thu, 13 Jul 2017 12:34:17 +0000 (14:34 +0200)
server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchData.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchResponseBuilder.java
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchResults.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/ProjectAnalysesWsParameters.java
sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/SearchRequest.java
sonar-ws/src/test/java/org/sonarqube/ws/client/projectanalysis/SearchRequestTest.java

index b916f287e68aa44b3a9e5f2c902c7019ecb749e2..3f5995a005d6d14c61981dbad7d0915e72544e96 100644 (file)
@@ -106,11 +106,11 @@ public class SearchHistoryAction implements MeasuresWsAction {
 
     action.createParam(PARAM_FROM)
       .setDescription("Filter measures created after the given date (inclusive). Format: date or datetime ISO formats")
-      .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
+      .setExampleValue("2013-05-01T13:00:00+0100");
 
     action.createParam(PARAM_TO)
-      .setDescription("Filter issues created before the given date (inclusive). Format: date or datetime ISO formats")
-      .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
+      .setDescription("Filter measures created before the given date (inclusive). Format: date or datetime ISO formats")
+      .setExampleValue("2013-05-01");
 
     action.addPagingParams(DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
   }
index 5505546966d086fa7a2ac8ed77c5b3fea7ab9701..1acead3883eb62d350836e43b43b24efe8d54ef9 100644 (file)
@@ -21,9 +21,6 @@ package org.sonar.server.projectanalysis.ws;
 
 import java.util.EnumSet;
 import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Stream;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
 import org.sonar.api.server.ws.Request;
@@ -45,13 +42,17 @@ import org.sonarqube.ws.client.projectanalysis.EventCategory;
 import org.sonarqube.ws.client.projectanalysis.SearchRequest;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.core.util.stream.MoreCollectors.toOneElement;
+import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime;
+import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime;
+import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE;
 import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.DESC;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.client.projectanalysis.EventCategory.OTHER;
 import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_CATEGORY;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_FROM;
 import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_TO;
 import static org.sonarqube.ws.client.projectanalysis.SearchRequest.DEFAULT_PAGE_SIZE;
 
 public class SearchAction implements ProjectAnalysesWsAction {
@@ -85,69 +86,71 @@ public class SearchAction implements ProjectAnalysesWsAction {
       .setDescription("Event category. Filter analyses that have at least one event of the category specified.")
       .setPossibleValues(EnumSet.allOf(EventCategory.class))
       .setExampleValue(OTHER.name());
+
+    action.createParam(PARAM_FROM)
+      .setDescription("Filter analyses created after the given date (inclusive). Format: date or datetime ISO formats")
+      .setExampleValue("2013-05-01")
+      .setSince("6.5");
+
+    action.createParam(PARAM_TO)
+      .setDescription("Filter analyses created before the given date (inclusive). Format: date or datetime ISO formats")
+      .setExampleValue("2013-05-01T13:00:00+0100")
+      .setSince("6.5");
   }
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    ProjectAnalyses.SearchResponse searchResponse = Stream.of(request)
-      .map(toWsRequest())
-      .map(this::search)
-      .map(new SearchResponseBuilder().buildWsResponse())
-      .collect(toOneElement());
+    SearchData searchData = load(toWsRequest(request));
+    ProjectAnalyses.SearchResponse searchResponse = new SearchResponseBuilder(searchData).build();
     writeProtobuf(searchResponse, request, response);
   }
 
-  private static Function<Request, SearchRequest> toWsRequest() {
-    return request -> {
-      String category = request.param(PARAM_CATEGORY);
-      return SearchRequest.builder()
-        .setProject(request.mandatoryParam(PARAM_PROJECT))
-        .setCategory(category == null ? null : EventCategory.valueOf(category))
-        .setPage(request.mandatoryParamAsInt(Param.PAGE))
-        .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
-        .build();
-    };
+  private static SearchRequest toWsRequest(Request request) {
+    String category = request.param(PARAM_CATEGORY);
+    return SearchRequest.builder()
+      .setProject(request.mandatoryParam(PARAM_PROJECT))
+      .setCategory(category == null ? null : EventCategory.valueOf(category))
+      .setPage(request.mandatoryParamAsInt(Param.PAGE))
+      .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
+      .setFrom(request.param(PARAM_FROM))
+      .setTo(request.param(PARAM_TO))
+      .build();
   }
 
-  private SearchResults search(SearchRequest request) {
+  private SearchData load(SearchRequest request) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      return Stream.of(SearchResults.builder(dbSession, request))
-        .peek(addProject())
-        .peek(checkPermission())
-        .peek(addAnalyses())
-        .peek(addEvents())
-        .map(SearchResults.Builder::build)
-        .collect(toOneElement());
+      SearchData.Builder searchResults = SearchData.builder(dbSession, request);
+      addProject(searchResults);
+      checkPermission(searchResults.getProject());
+      addAnalyses(searchResults);
+      addEvents(searchResults);
+      return searchResults.build();
     }
   }
 
-  private Consumer<SearchResults.Builder> addAnalyses() {
-    return data -> {
-      SnapshotQuery dbQuery = new SnapshotQuery()
-        .setComponentUuid(data.getProject().uuid())
-        .setStatus(SnapshotDto.STATUS_PROCESSED)
-        .setSort(BY_DATE, DESC);
-      data.setAnalyses(dbClient.snapshotDao().selectAnalysesByQuery(data.getDbSession(), dbQuery));
-    };
+  private void addAnalyses(SearchData.Builder data) {
+    SnapshotQuery dbQuery = new SnapshotQuery()
+      .setComponentUuid(data.getProject().uuid())
+      .setStatus(SnapshotDto.STATUS_PROCESSED)
+      .setSort(BY_DATE, DESC);
+    setNullable(data.getRequest().getFrom(), from -> dbQuery.setCreatedAfter(parseStartingDateOrDateTime(from).getTime()));
+    setNullable(data.getRequest().getTo(), to -> dbQuery.setCreatedBefore(parseEndingDateOrDateTime(to).getTime() + 1_000L));
+    data.setAnalyses(dbClient.snapshotDao().selectAnalysesByQuery(data.getDbSession(), dbQuery));
   }
 
-  private Consumer<SearchResults.Builder> addEvents() {
-    return data -> {
-      List<String> analyses = data.getAnalyses().stream().map(SnapshotDto::getUuid).collect(MoreCollectors.toList());
-      data.setEvents(dbClient.eventDao().selectByAnalysisUuids(data.getDbSession(), analyses));
-    };
+  private void addEvents(SearchData.Builder data) {
+    List<String> analyses = data.getAnalyses().stream().map(SnapshotDto::getUuid).collect(MoreCollectors.toList());
+    data.setEvents(dbClient.eventDao().selectByAnalysisUuids(data.getDbSession(), analyses));
   }
 
-  private Consumer<SearchResults.Builder> checkPermission() {
-    return data -> userSession.checkComponentPermission(UserRole.USER, data.getProject());
+  private void checkPermission(ComponentDto project) {
+    userSession.checkComponentPermission(UserRole.USER, project);
   }
 
-  private Consumer<SearchResults.Builder> addProject() {
-    return data -> {
-      ComponentDto project = componentFinder.getByKey(data.getDbSession(), data.getRequest().getProject());
-      checkArgument(Scopes.PROJECT.equals(project.scope()) && Qualifiers.PROJECT.equals(project.qualifier()), "A project is required");
-      data.setProject(project);
-    };
+  private void addProject(SearchData.Builder data) {
+    ComponentDto project = componentFinder.getByKey(data.getDbSession(), data.getRequest().getProject());
+    checkArgument(Scopes.PROJECT.equals(project.scope()) && Qualifiers.PROJECT.equals(project.qualifier()), "A project is required");
+    data.setProject(project);
   }
 
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchData.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchData.java
new file mode 100644 (file)
index 0000000..56ad720
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.server.projectanalysis.ws;
+
+import com.google.common.collect.ListMultimap;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import org.sonar.api.utils.Paging;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.event.EventDto;
+import org.sonarqube.ws.client.projectanalysis.SearchRequest;
+
+import static java.util.Objects.requireNonNull;
+
+class SearchData {
+  final List<SnapshotDto> analyses;
+  final ListMultimap<String, EventDto> eventsByAnalysis;
+  final Paging paging;
+
+  private SearchData(Builder builder) {
+    this.analyses = builder.analyses;
+    this.eventsByAnalysis = buildEvents(builder.events);
+    this.paging = Paging
+      .forPageIndex(builder.getRequest().getPage())
+      .withPageSize(builder.getRequest().getPageSize())
+      .andTotal(builder.countAnalyses);
+  }
+
+  private ListMultimap<String, EventDto> buildEvents(List<EventDto> events) {
+    return events.stream().collect(MoreCollectors.index(EventDto::getAnalysisUuid));
+  }
+
+  static Builder builder(DbSession dbSession, SearchRequest request) {
+    return new Builder(dbSession, request);
+  }
+
+  static class Builder {
+    private final DbSession dbSession;
+    private final SearchRequest request;
+    private ComponentDto project;
+    private List<SnapshotDto> analyses;
+    private int countAnalyses;
+    private List<EventDto> events;
+
+    private Builder(DbSession dbSession, SearchRequest request) {
+      this.dbSession = dbSession;
+      this.request = request;
+    }
+
+    Builder setProject(ComponentDto project) {
+      this.project = project;
+      return this;
+    }
+
+    Builder setAnalyses(List<SnapshotDto> analyses) {
+      Stream<SnapshotDto> stream = analyses.stream();
+      // no filter by category, the pagination can be applied
+      if (request.getCategory() == null) {
+        stream = stream
+          .skip(Paging.offset(request.getPage(), request.getPageSize()))
+          .limit(request.getPageSize());
+      }
+
+      this.analyses = stream.collect(MoreCollectors.toList());
+      this.countAnalyses = analyses.size();
+      return this;
+    }
+
+    Builder setEvents(List<EventDto> events) {
+      this.events = events;
+      return this;
+    }
+
+    DbSession getDbSession() {
+      return dbSession;
+    }
+
+    SearchRequest getRequest() {
+      return request;
+    }
+
+    ComponentDto getProject() {
+      return project;
+    }
+
+    List<SnapshotDto> getAnalyses() {
+      return analyses;
+    }
+
+    private void filterByCategory() {
+      ListMultimap<String, String> eventCategoriesByAnalysisUuid = events.stream()
+        .collect(MoreCollectors.index(EventDto::getAnalysisUuid, EventDto::getCategory));
+      Predicate<SnapshotDto> byCategory = a -> eventCategoriesByAnalysisUuid.get(a.getUuid()).contains(request.getCategory().getLabel());
+      this.countAnalyses = (int) analyses.stream().filter(byCategory).count();
+      this.analyses = analyses.stream()
+        .filter(byCategory)
+        .skip(Paging.offset(request.getPage(), request.getPageSize()))
+        .limit(request.getPageSize())
+        .collect(MoreCollectors.toList());
+    }
+
+    SearchData build() {
+      requireNonNull(analyses);
+      requireNonNull(events);
+      if (request.getCategory() != null) {
+        filterByCategory();
+      }
+      return new SearchData(this);
+    }
+  }
+}
index 07152e97f199f15d74781a15039e40a19da4d066..41c0a746b3e5323041c47ff32e97e9297f3c1e01 100644 (file)
  */
 package org.sonar.server.projectanalysis.ws;
 
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Stream;
-import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.event.EventDto;
 import org.sonarqube.ws.ProjectAnalyses.Analysis;
@@ -34,57 +30,59 @@ import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonarqube.ws.client.projectanalysis.EventCategory.fromLabel;
 
 class SearchResponseBuilder {
-  private final Analysis.Builder analysisBuilder;
-  private final Event.Builder eventBuilder;
+  private final Analysis.Builder wsAnalysis;
+  private final Event.Builder wsEvent;
+  private final SearchData searchData;
 
-  SearchResponseBuilder() {
-    this.analysisBuilder = Analysis.newBuilder();
-    this.eventBuilder = Event.newBuilder();
+  SearchResponseBuilder(SearchData searchData) {
+    this.wsAnalysis = Analysis.newBuilder();
+    this.wsEvent = Event.newBuilder();
+    this.searchData = searchData;
   }
 
-  Function<SearchResults, SearchResponse> buildWsResponse() {
-    return searchResults -> Stream.of(SearchResponse.newBuilder())
-      .peek(addAnalyses(searchResults))
-      .peek(addPagination(searchResults))
-      .map(SearchResponse.Builder::build)
-      .collect(MoreCollectors.toOneElement());
+  SearchResponse build() {
+    SearchResponse.Builder wsResponse = SearchResponse.newBuilder();
+    addAnalyses(wsResponse);
+    addPagination(wsResponse);
+    return wsResponse.build();
   }
 
-  private Consumer<SearchResponse.Builder> addAnalyses(SearchResults searchResults) {
-    return response -> searchResults.analyses.stream()
-      .map(dbToWsAnalysis())
-      .peek(addEvents(searchResults))
-      .forEach(response::addAnalyses);
+  private void addAnalyses(SearchResponse.Builder wsResponse) {
+    searchData.analyses.stream()
+      .map(this::dbToWsAnalysis)
+      .map(this::attachEvents)
+      .forEach(wsResponse::addAnalyses);
   }
 
-  private Function<SnapshotDto, Analysis.Builder> dbToWsAnalysis() {
-    return dbAnalysis -> analysisBuilder.clear()
+  private Analysis.Builder dbToWsAnalysis(SnapshotDto dbAnalysis) {
+    return wsAnalysis.clear()
       .setKey(dbAnalysis.getUuid())
       .setDate(formatDateTime(dbAnalysis.getCreatedAt()));
   }
 
-  private Consumer<Analysis.Builder> addEvents(SearchResults searchResults) {
-    return wsAnalysis -> searchResults.eventsByAnalysis.get(wsAnalysis.getKey()).stream()
-      .map(dbToWsEvent())
-      .forEach(wsAnalysis::addEvents);
+  private Analysis.Builder attachEvents(Analysis.Builder analysis) {
+    searchData.eventsByAnalysis.get(analysis.getKey())
+      .stream()
+      .map(this::dbToWsEvent)
+      .forEach(analysis::addEvents);
+    return analysis;
+
   }
 
-  private Function<EventDto, Event.Builder> dbToWsEvent() {
-    return dbEvent -> {
-      Event.Builder wsEvent = eventBuilder.clear()
-        .setKey(dbEvent.getUuid());
-      setNullable(dbEvent.getName(), wsEvent::setName);
-      setNullable(dbEvent.getDescription(), wsEvent::setDescription);
-      setNullable(dbEvent.getCategory(), cat -> wsEvent.setCategory(fromLabel(cat).name()));
-      return wsEvent;
-    };
+  private Event.Builder dbToWsEvent(EventDto dbEvent) {
+    wsEvent.clear().setKey(dbEvent.getUuid());
+    setNullable(dbEvent.getName(), wsEvent::setName);
+    setNullable(dbEvent.getDescription(), wsEvent::setDescription);
+    setNullable(dbEvent.getCategory(), cat -> wsEvent.setCategory(fromLabel(cat).name()));
+
+    return wsEvent;
   }
 
-  private static Consumer<SearchResponse.Builder> addPagination(SearchResults searchResults) {
-    return response -> response.getPagingBuilder()
-      .setPageIndex(searchResults.paging.pageIndex())
-      .setPageSize(searchResults.paging.pageSize())
-      .setTotal(searchResults.paging.total())
+  private void addPagination(SearchResponse.Builder wsResponse) {
+    wsResponse.getPagingBuilder()
+      .setPageIndex(searchData.paging.pageIndex())
+      .setPageSize(searchData.paging.pageSize())
+      .setTotal(searchData.paging.total())
       .build();
   }
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchResults.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchResults.java
deleted file mode 100644 (file)
index 8e725cc..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.server.projectanalysis.ws;
-
-import com.google.common.collect.ListMultimap;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-import org.sonar.api.utils.Paging;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.event.EventDto;
-import org.sonarqube.ws.client.projectanalysis.SearchRequest;
-
-import static java.util.Objects.requireNonNull;
-
-class SearchResults {
-  final List<SnapshotDto> analyses;
-  final ListMultimap<String, EventDto> eventsByAnalysis;
-  final Paging paging;
-
-  private SearchResults(Builder builder) {
-    this.analyses = builder.analyses;
-    this.eventsByAnalysis = buildEvents(builder.events);
-    this.paging = Paging
-      .forPageIndex(builder.getRequest().getPage())
-      .withPageSize(builder.getRequest().getPageSize())
-      .andTotal(builder.countAnalyses);
-  }
-
-  private ListMultimap<String, EventDto> buildEvents(List<EventDto> events) {
-    return events.stream().collect(MoreCollectors.index(EventDto::getAnalysisUuid));
-  }
-
-  static Builder builder(DbSession dbSession, SearchRequest request) {
-    return new Builder(dbSession, request);
-  }
-
-  static class Builder {
-    private final DbSession dbSession;
-    private final SearchRequest request;
-    private ComponentDto project;
-    private List<SnapshotDto> analyses;
-    private int countAnalyses;
-    private List<EventDto> events;
-
-    private Builder(DbSession dbSession, SearchRequest request) {
-      this.dbSession = dbSession;
-      this.request = request;
-    }
-
-    Builder setProject(ComponentDto project) {
-      this.project = project;
-      return this;
-    }
-
-    Builder setAnalyses(List<SnapshotDto> analyses) {
-      Stream<SnapshotDto> stream = analyses.stream();
-      // no filter by category, the pagination can be applied
-      if (request.getCategory() == null) {
-        stream = stream
-          .skip(Paging.offset(request.getPage(), request.getPageSize()))
-          .limit(request.getPageSize());
-      }
-
-      this.analyses = stream.collect(MoreCollectors.toList());
-      this.countAnalyses = analyses.size();
-      return this;
-    }
-
-    Builder setEvents(List<EventDto> events) {
-      this.events = events;
-      return this;
-    }
-
-    DbSession getDbSession() {
-      return dbSession;
-    }
-
-    SearchRequest getRequest() {
-      return request;
-    }
-
-    ComponentDto getProject() {
-      return project;
-    }
-
-    List<SnapshotDto> getAnalyses() {
-      return analyses;
-    }
-
-    private void filterByCategory() {
-      ListMultimap<String, String> eventCategoriesByAnalysisUuid = events.stream()
-        .collect(MoreCollectors.index(EventDto::getAnalysisUuid, EventDto::getCategory));
-      Predicate<SnapshotDto> byCategory = a -> eventCategoriesByAnalysisUuid.get(a.getUuid()).contains(request.getCategory().getLabel());
-      this.countAnalyses = (int) analyses.stream().filter(byCategory).count();
-      this.analyses = analyses.stream()
-        .filter(byCategory)
-        .skip(Paging.offset(request.getPage(), request.getPageSize()))
-        .limit(request.getPageSize())
-        .collect(MoreCollectors.toList());
-    }
-
-    SearchResults build() {
-      requireNonNull(analyses);
-      requireNonNull(events);
-      if (request.getCategory() != null) {
-        filterByCategory();
-      }
-      return new SearchResults(this);
-    }
-  }
-}
index 5189afc99f9458964744cdaa040666586afe520c..b40b4209ba4d489758acf3d10d529a6903846e81 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.projectanalysis.ws;
 
+import java.util.Date;
 import java.util.List;
 import java.util.function.Function;
 import java.util.stream.IntStream;
@@ -51,6 +52,8 @@ import org.sonarqube.ws.client.projectanalysis.SearchRequest;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.utils.DateUtils.formatDate;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.api.utils.DateUtils.parseDateTime;
 import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
@@ -62,7 +65,9 @@ import static org.sonarqube.ws.client.projectanalysis.EventCategory.OTHER;
 import static org.sonarqube.ws.client.projectanalysis.EventCategory.QUALITY_GATE;
 import static org.sonarqube.ws.client.projectanalysis.EventCategory.VERSION;
 import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_CATEGORY;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_FROM;
 import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_TO;
 
 public class SearchActionTest {
 
@@ -218,6 +223,88 @@ public class SearchActionTest {
       .containsExactly(2, 1, 3);
   }
 
+  @Test
+  public void filter_from_date() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
+    SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
+    SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
+    SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
+
+    SearchResponse result = call(SearchRequest.builder()
+      .setProject(project.key())
+      .setFrom(formatDateTime(2_000_000_000L))
+      .build());
+
+    assertThat(result.getAnalysesList())
+      .extracting(Analysis::getKey)
+      .containsOnly(a2.getUuid(), a3.getUuid(), a4.getUuid())
+      .doesNotContain(a1.getUuid());
+  }
+
+  @Test
+  public void filter_to_date() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
+    SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
+    SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
+    SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
+
+    SearchResponse result = call(SearchRequest.builder()
+      .setProject(project.key())
+      .setTo(formatDateTime(2_000_000_000L))
+      .build());
+
+    assertThat(result.getAnalysesList())
+      .extracting(Analysis::getKey)
+      .containsOnly(a1.getUuid(), a2.getUuid())
+      .doesNotContain(a3.getUuid(), a4.getUuid());
+  }
+
+  @Test
+  public void filter_by_dates_using_datetime_format() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
+    SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
+    SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
+    SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
+
+    SearchResponse result = call(SearchRequest.builder()
+      .setProject(project.key())
+      .setFrom(formatDateTime(2_000_000_000L))
+      .setTo(formatDateTime(3_000_000_000L))
+      .build());
+
+    assertThat(result.getAnalysesList())
+      .extracting(Analysis::getKey)
+      .containsOnly(a2.getUuid(), a3.getUuid())
+    .doesNotContain(a1.getUuid(), a4.getUuid());
+  }
+
+  @Test
+  public void filter_by_dates_using_date_format() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
+    SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
+    SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
+    SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
+
+    SearchResponse result = call(SearchRequest.builder()
+      .setProject(project.key())
+      .setFrom(formatDate(new Date(2_000_000_000L)))
+      .setTo(formatDate(new Date(3_000_000_000L)))
+      .build());
+
+    assertThat(result.getAnalysesList())
+      .extracting(Analysis::getKey)
+      .containsOnly(a2.getUuid(), a3.getUuid())
+      .doesNotContain(a1.getUuid(), a4.getUuid());
+  }
+
   @Test
   public void definition() {
     WebService.Action definition = ws.getDef();
@@ -225,8 +312,14 @@ public class SearchActionTest {
     assertThat(definition.key()).isEqualTo("search");
     assertThat(definition.since()).isEqualTo("6.3");
     assertThat(definition.responseExampleAsString()).isNotEmpty();
-    assertThat(definition.param(PARAM_PROJECT).isRequired()).isTrue();
-    assertThat(definition.param(PARAM_CATEGORY)).isNotNull();
+    assertThat(definition.param("project").isRequired()).isTrue();
+    assertThat(definition.param("category")).isNotNull();
+
+    Param from = definition.param("from");
+    assertThat(from.since()).isEqualTo("6.5");
+
+    Param to = definition.param("to");
+    assertThat(to.since()).isEqualTo("6.5");
   }
 
   @Test
@@ -263,6 +356,8 @@ public class SearchActionTest {
     setNullable(wsRequest.getCategory(), category -> request.setParam(PARAM_CATEGORY, category.name()));
     setNullable(wsRequest.getPage(), page -> request.setParam(Param.PAGE, String.valueOf(page)));
     setNullable(wsRequest.getPageSize(), pageSize -> request.setParam(Param.PAGE_SIZE, String.valueOf(pageSize)));
+    setNullable(wsRequest.getFrom(), from -> request.setParam(PARAM_FROM, from));
+    setNullable(wsRequest.getTo(), to -> request.setParam(PARAM_TO, to));
 
     return request.executeProtobuf(SearchResponse.class);
   }
index 24c180e113cc17121123ea27c5ececaf54c1fe8b..2e7e42e1a0d86794ec168258b8b766f29fd9de5b 100644 (file)
@@ -25,6 +25,8 @@ public class ProjectAnalysesWsParameters {
   public static final String PARAM_NAME = "name";
   public static final String PARAM_EVENT = "event";
   public static final String PARAM_PROJECT = "project";
+  public static final String PARAM_FROM = "from";
+  public static final String PARAM_TO = "to";
 
   private ProjectAnalysesWsParameters() {
     // static access only
index e1fbbd175cc510641e88d23926dabf26fffe3865..c067a12621c659c3b3f44b3165753262e9f9f976 100644 (file)
@@ -33,12 +33,16 @@ public class SearchRequest {
   private final EventCategory category;
   private final int page;
   private final int pageSize;
+  private final String from;
+  private final String to;
 
   private SearchRequest(Builder builder) {
     this.project = builder.project;
     this.category = builder.category;
     this.page = builder.page;
     this.pageSize = builder.pageSize;
+    this.from = builder.from;
+    this.to = builder.to;
   }
 
   public String getProject() {
@@ -58,6 +62,16 @@ public class SearchRequest {
     return pageSize;
   }
 
+  @CheckForNull
+  public String getFrom() {
+    return from;
+  }
+
+  @CheckForNull
+  public String getTo() {
+    return to;
+  }
+
   public static Builder builder() {
     return new Builder();
   }
@@ -67,6 +81,8 @@ public class SearchRequest {
     private EventCategory category;
     private int page = 1;
     private int pageSize = DEFAULT_PAGE_SIZE;
+    private String from;
+    private String to;
 
     private Builder() {
       // enforce static factory method
@@ -92,6 +108,16 @@ public class SearchRequest {
       return this;
     }
 
+    public Builder setFrom(@Nullable String from) {
+      this.from = from;
+      return this;
+    }
+
+    public Builder setTo(@Nullable String to) {
+      this.to = to;
+      return this;
+    }
+
     public SearchRequest build() {
       requireNonNull(project, "Project is required");
       checkArgument(pageSize <= MAX_SIZE, "Page size must be lower than or equal to " + MAX_SIZE);
index d36b8393273889ef8c22e281df8c1f65aa8c8a0e..355fd27e5e852f1d2dca298b66b1cfe6754d310d 100644 (file)
@@ -39,12 +39,16 @@ public class SearchRequestTest {
       .setCategory(QUALITY_GATE)
       .setPage(2)
       .setPageSize(500)
+      .setFrom("2016-01-01")
+      .setTo("2017-07-01")
       .build();
 
     assertThat(result.getProject()).isEqualTo("P1");
     assertThat(result.getPage()).isEqualTo(2);
     assertThat(result.getPageSize()).isEqualTo(500);
     assertThat(result.getCategory()).isEqualTo(QUALITY_GATE);
+    assertThat(result.getFrom()).isEqualTo("2016-01-01");
+    assertThat(result.getTo()).isEqualTo("2017-07-01");
   }
 
   @Test