]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14935 add 'detectedCI' field
authorJacek <jacek.poreda@sonarsource.com>
Fri, 11 Jun 2021 14:01:12 +0000 (16:01 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 17 Jun 2021 20:03:07 +0000 (20:03 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/component/AnalysisPropertiesDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/AnalysisPropertiesMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/AnalysisPropertyDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/AnalysisPropertiesMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/component/AnalysisPropertiesDaoTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/projectanalysis/ws/SearchData.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/projectanalysis/ws/SearchResponseBuilder.java
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/projectanalysis/ws/search-example.json
server/sonar-webserver-webapi/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java
sonar-ws/src/main/protobuf/ws-projectanalyses.proto

index c40ad31952fe6d05808e46f6e84287dcb0e6f51c..14fff32259bf11c4962016a0e09c5a4636a9d28f 100644 (file)
  */
 package org.sonar.db.component;
 
+import java.util.Collection;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbSession;
 
 import static java.util.Objects.requireNonNull;
@@ -41,6 +43,12 @@ public class AnalysisPropertiesDao implements Dao {
     return getMapper(session).selectByAnalysisUuid(analysisUuid);
   }
 
+  public List<AnalysisPropertyDto> selectByKeyAndAnalysisUuids(DbSession session, String key, Collection<String> snapshotUuids) {
+    requireNonNull(snapshotUuids);
+    var mapper = getMapper(session);
+    return DatabaseUtils.executeLargeInputs(snapshotUuids, input -> mapper.selectByKeyAnAnalysisUuids(input, key));
+  }
+
   public void insert(DbSession session, List<AnalysisPropertyDto> analysisPropertyDto) {
     analysisPropertyDto.forEach(a -> insert(session, a));
   }
index 19080f1413b2cfe043f95d3cb7dde4ebe3a4bddd..262d34112ec201dc610e0542afc01cddd8db47e3 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.db.component;
 
+import java.util.Collection;
 import java.util.List;
 import org.apache.ibatis.annotations.Param;
 
@@ -33,4 +34,7 @@ public interface AnalysisPropertiesMapper {
   void insertAsText(@Param("analysisPropertyDto") AnalysisPropertyDto analysisPropertyDto, @Param("createdAt") long createdAt);
 
   List<ProjectCountPerAnalysisPropertyValue> selectProjectCountPerAnalysisPropertyValueInLastAnalysis(@Param("analysisPropertyKey") String analysisPropertyKey);
+
+  List<AnalysisPropertyDto> selectByKeyAnAnalysisUuids(@Param("analysisUuids") Collection<String> analysisUuids, @Param("analysisPropertyKey") String analysisPropertyKey);
+
 }
index 779eb9c4e6817ab4204dd32b5a6eb093878f18a0..24383484bbc99be938d50a4b5000abdddeb3d629 100644 (file)
@@ -83,7 +83,7 @@ public class AnalysisPropertyDto {
 
   @Override
   public String toString() {
-    return "BranchDto{" + "uuid='" + uuid + '\'' +
+    return "AnalysisPropertyDto{" + "uuid='" + uuid + '\'' +
       ", analysisUuid='" + analysisUuid + '\'' +
       ", key='" + key + '\'' +
       ", value='" + value + "'" +
index 124b27aad3d56dded39d83cccad6de34bf5f1c1b..685e1ad0a0582e0b38e36df3b532e34ae5ee989e 100644 (file)
       analysis_uuid = #{analysisUuid}
   </select>
 
+  <select id="selectByKeyAnAnalysisUuids" parameterType="string" resultType="ScrapAnalysisProperty">
+    SELECT
+    <include refid="columns"/>
+    FROM
+    analysis_properties
+    WHERE
+    kee = #{analysisPropertyKey,jdbcType=VARCHAR}
+    AND analysis_uuid in <foreach collection="analysisUuids" open="(" close=")" item="uuid" separator=",">#{uuid,jdbcType=VARCHAR}</foreach>
+  </select>
+
   <select id="selectProjectCountPerAnalysisPropertyValueInLastAnalysis" parameterType="string" resultType="ProjectCountPerAnalysisPropertyValue">
     select
       ap.text_value as "propertyValue",
index 9d49dcecb892d93c641eca30944aa7836a65279a..090ecdddb84148321fe6eca8761f8fe1998de29c 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.db.component;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -161,6 +162,27 @@ public class AnalysisPropertiesDaoTest {
     assertThat(result).containsExactlyInAnyOrder(propertyDtos.toArray(new AnalysisPropertyDto[0]));
   }
 
+  @Test
+  public void selectByKeyAndAnalysisUuids_should_return_correct_values() {
+    String analysisUuid = randomAlphanumeric(40);
+
+    List<AnalysisPropertyDto> propertyDtos = Arrays.asList(
+      newAnalysisPropertyDto(random.nextInt(10), "key1",analysisUuid),
+      newAnalysisPropertyDto(random.nextInt(10), "key2", analysisUuid),
+      newAnalysisPropertyDto(random.nextInt(10), "key3", analysisUuid)
+    );
+
+    underTest.insert(dbSession, propertyDtos);
+    assertThat(dbTester.countRowsOfTable(dbSession, "ANALYSIS_PROPERTIES")).isEqualTo(propertyDtos.size());
+
+    List<AnalysisPropertyDto> result = underTest.selectByKeyAndAnalysisUuids(dbSession, "key1", Set.of(analysisUuid));
+    assertThat(result).contains(propertyDtos.get(0));
+    result = underTest.selectByKeyAndAnalysisUuids(dbSession, "key2", Set.of(analysisUuid));
+    assertThat(result).contains(propertyDtos.get(1));
+    result = underTest.selectByKeyAndAnalysisUuids(dbSession, "key3", Set.of(analysisUuid));
+    assertThat(result).contains(propertyDtos.get(2));
+  }
+
   @Test
   public void selectProjectCountPerAnalysisPropertyValueInLastAnalysis_should_return_correct_values() {
     final String analysisPropertyKey = "key";
@@ -183,8 +205,7 @@ public class AnalysisPropertiesDaoTest {
       .containsExactlyInAnyOrder(
         tuple("git", 3L),
         tuple("svn", 1L),
-        tuple("undetected", 2L)
-      );
+        tuple("undetected", 2L));
   }
 
   private AnalysisPropertyDto insertAnalysisPropertyDto(int valueLength) {
@@ -193,15 +214,19 @@ public class AnalysisPropertiesDaoTest {
     return analysisPropertyDto;
   }
 
-  private AnalysisPropertyDto newAnalysisPropertyDto(int valueLength, String analysisUuid) {
+  private AnalysisPropertyDto newAnalysisPropertyDto(int valueLength, String key, String analysisUuid) {
     return new AnalysisPropertyDto()
       .setAnalysisUuid(analysisUuid)
-      .setKey(randomAlphanumeric(512))
+      .setKey(key)
       .setUuid(randomAlphanumeric(40))
       .setValue(randomAlphanumeric(valueLength))
       .setCreatedAt(1_000L);
   }
 
+  private AnalysisPropertyDto newAnalysisPropertyDto(int valueLength, String analysisUuid) {
+    return newAnalysisPropertyDto(valueLength, randomAlphanumeric(512), analysisUuid);
+  }
+
   private void compareFirstValueWith(AnalysisPropertyDto analysisPropertyDto) {
     AnalysisPropertyDto dtoFromDatabase = underTest.selectByAnalysisUuid(dbSession, analysisPropertyDto.getAnalysisUuid()).get(0);
     assertThat(dtoFromDatabase).isEqualTo(analysisPropertyDto);
index ef6c9de9ed6ecf4d6e030b080735761aad87f50e..706fa91a7f0925502d73d6d8b805902cef4442a3 100644 (file)
  */
 package org.sonar.server.projectanalysis.ws;
 
-import com.google.common.collect.ImmutableSet;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
 import org.sonar.api.server.ws.Change;
@@ -31,6 +31,7 @@ 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.web.UserRole;
+import org.sonar.core.config.CorePropertyDefinitions;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
@@ -59,7 +60,7 @@ import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class SearchAction implements ProjectAnalysesWsAction {
-  private static final Set<String> ALLOWED_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.APP, Qualifiers.VIEW);
+  private static final Set<String> ALLOWED_QUALIFIERS = Set.of(Qualifiers.PROJECT, Qualifiers.APP, Qualifiers.VIEW);
 
   private final DbClient dbClient;
   private final ComponentFinder componentFinder;
@@ -79,8 +80,8 @@ public class SearchAction implements ProjectAnalysesWsAction {
       .setSince("6.3")
       .setResponseExample(getClass().getResource("search-example.json"))
       .setChangelog(
-        new Change("7.5", "Add QualityGate information on Applications")
-      )
+        new Change("9.0", "Add field response 'detectedCI'"),
+        new Change("7.5", "Add QualityGate information on Applications"))
       .setHandler(this);
 
     action.addPagingParams(DEFAULT_PAGE_SIZE, 500);
@@ -160,7 +161,12 @@ public class SearchAction implements ProjectAnalysesWsAction {
       .setSort(BY_DATE, DESC);
     ofNullable(data.getRequest().getFrom()).ifPresent(from -> dbQuery.setCreatedAfter(parseStartingDateOrDateTime(from).getTime()));
     ofNullable(data.getRequest().getTo()).ifPresent(to -> dbQuery.setCreatedBefore(parseEndingDateOrDateTime(to).getTime() + 1_000L));
-    data.setAnalyses(dbClient.snapshotDao().selectAnalysesByQuery(data.getDbSession(), dbQuery));
+    List<SnapshotDto> snapshotDtos = dbClient.snapshotDao().selectAnalysesByQuery(data.getDbSession(), dbQuery);
+    var detectedCIs = dbClient.analysisPropertiesDao().selectByKeyAndAnalysisUuids(data.getDbSession(),
+      CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI,
+      snapshotDtos.stream().map(SnapshotDto::getUuid).collect(Collectors.toList()));
+    data.setAnalyses(snapshotDtos);
+    data.setDetectedCIs(detectedCIs);
   }
 
   private void addEvents(SearchData.Builder data) {
index 2441ba96e1d7dc8a2156a3e7db410d1052f2bca9..ffd67b7f8e26bce7669fedeca907d988bbac3758 100644 (file)
@@ -21,14 +21,17 @@ package org.sonar.server.projectanalysis.ws;
 
 import com.google.common.collect.ListMultimap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.utils.Paging;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.AnalysisPropertyDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.event.EventComponentChangeDto;
@@ -38,6 +41,7 @@ import static java.util.Objects.requireNonNull;
 
 class SearchData {
   final List<SnapshotDto> analyses;
+  final Map<String, String> detectedCIs;
   final ListMultimap<String, EventDto> eventsByAnalysis;
   final ListMultimap<String, EventComponentChangeDto> componentChangesByEventUuid;
   final Paging paging;
@@ -46,6 +50,7 @@ class SearchData {
 
   private SearchData(Builder builder) {
     this.analyses = builder.analyses;
+    this.detectedCIs = builder.detectedCIs;
     this.eventsByAnalysis = buildEvents(builder.events);
     this.componentChangesByEventUuid = buildComponentChanges(builder.componentChanges);
     this.paging = Paging
@@ -76,6 +81,7 @@ class SearchData {
     private final SearchRequest request;
     private ComponentDto project;
     private List<SnapshotDto> analyses;
+    private Map<String, String> detectedCIs;
     private int countAnalyses;
     private String manualBaseline;
     private List<EventDto> events;
@@ -105,6 +111,12 @@ class SearchData {
       return this;
     }
 
+    Builder setDetectedCIs(List<AnalysisPropertyDto> detectedCIs) {
+      this.detectedCIs = detectedCIs.stream().collect(Collectors.toMap(AnalysisPropertyDto::getAnalysisUuid,
+          AnalysisPropertyDto::getValue));
+      return this;
+    }
+
     Builder setEvents(List<EventDto> events) {
       this.events = events;
       return this;
@@ -135,6 +147,10 @@ class SearchData {
       return analyses;
     }
 
+    Map<String, String> getDetectedCIs() {
+      return detectedCIs;
+    }
+
     public Builder setManualBaseline(@Nullable String manualBaseline) {
       this.manualBaseline = manualBaseline;
       return this;
index ad23227aa4c3265f053dcc7b9cde553a49eff209..e5cc5461b4d1aa52685e69b8333744cae09a3937 100644 (file)
@@ -78,7 +78,7 @@ class SearchResponseBuilder {
   }
 
   private Analysis.Builder dbToWsAnalysis(SnapshotDto dbAnalysis) {
-    Analysis.Builder builder = wsAnalysis.clear();
+    var builder = wsAnalysis.clear();
     builder
       .setKey(dbAnalysis.getUuid())
       .setDate(formatDateTime(dbAnalysis.getCreatedAt()))
@@ -86,6 +86,7 @@ class SearchResponseBuilder {
     ofNullable(dbAnalysis.getProjectVersion()).ifPresent(builder::setProjectVersion);
     ofNullable(dbAnalysis.getBuildString()).ifPresent(builder::setBuildString);
     ofNullable(dbAnalysis.getRevision()).ifPresent(builder::setRevision);
+    ofNullable(searchData.detectedCIs.get(dbAnalysis.getUuid())).ifPresent(builder::setDetectedCI);
 
     return builder;
   }
index 841ac0d14a122e88ee2a947449e4761a913a7985..e9dd58e74704ad5b8740b57776b0b699423d340d 100644 (file)
@@ -52,6 +52,7 @@
       "projectVersion": "1.2",
       "buildString": "1.2.0.321",
       "manualNewCodePeriodBaseline": false,
+      "detectedCI": "Jenkins",
       "events": [
         {
           "key": "E31",
index 9a6f6ef211b72ec4991de524aab368e993498050..c4d6e850c0dbc184489b2a18b6518e3c117b00cf 100644 (file)
@@ -29,7 +29,6 @@ import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
@@ -37,9 +36,11 @@ import org.sonar.api.utils.log.LogAndArguments;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.config.CorePropertyDefinitions;
 import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.AnalysisPropertyDto;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
@@ -65,6 +66,7 @@ import org.sonarqube.ws.ProjectAnalyses.SearchResponse;
 import static java.lang.String.format;
 import static java.util.Optional.ofNullable;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.api.utils.DateUtils.formatDate;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
@@ -94,8 +96,6 @@ import static org.sonarqube.ws.client.WsRequest.Method.POST;
 @RunWith(DataProviderRunner.class)
 public class SearchActionTest {
 
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
@@ -138,6 +138,12 @@ public class SearchActionTest {
       .setCreatedAt(parseDateTime("2015-11-11T10:00:00+0100").getTime())
       .setProjectVersion("1.2")
       .setBuildString("1.2.0.321"));
+    db.getDbClient().analysisPropertiesDao().insert(db.getSession(), new AnalysisPropertyDto()
+      .setUuid("P1-prop-uuid")
+      .setAnalysisUuid(a3.getUuid())
+      .setKey(CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI)
+      .setValue("Jenkins")
+      .setCreatedAt(1L));
     BranchDto branchDto = newBranchDto(project, BRANCH);
     db.getDbClient().branchDao().insert(db.getSession(), branchDto);
     db.newCodePeriods().insert(new NewCodePeriodDto()
@@ -628,16 +634,15 @@ public class SearchActionTest {
     userSession.anonymous();
     ComponentDto project = db.components().insertPrivateProject();
 
-    expectedException.expect(ForbiddenException.class);
-
-    call(project.getDbKey());
+    var projectDbKey = project.getDbKey();
+    assertThatThrownBy(() -> call(projectDbKey))
+      .isInstanceOf(ForbiddenException.class);
   }
 
   @Test
   public void fail_if_project_does_not_exist() {
-    expectedException.expect(NotFoundException.class);
-
-    call("P1");
+    assertThatThrownBy(() -> call("P1"))
+      .isInstanceOf(NotFoundException.class);
   }
 
   @Test
@@ -647,10 +652,10 @@ public class SearchActionTest {
     db.components().insertSnapshot(newAnalysis(project));
     userSession.registerComponents(project, file);
 
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("A project, portfolio or application is required");
-
-    call(file.getDbKey());
+    var fileDbKey = file.getDbKey();
+    assertThatThrownBy(() -> call(fileDbKey))
+      .isInstanceOf(IllegalArgumentException.class)
+      .hasMessage("A project, portfolio or application is required");
   }
 
   @Test
@@ -659,13 +664,14 @@ public class SearchActionTest {
     userSession.addProjectPermission(UserRole.USER, project);
     db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
 
-    expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(format("Component '%s' on branch '%s' not found", project.getKey(), "another_branch"));
-
-    call(SearchRequest.builder()
+    var searchRequest = SearchRequest.builder()
       .setProject(project.getKey())
       .setBranch("another_branch")
-      .build());
+      .build();
+
+    assertThatThrownBy(() -> call(searchRequest))
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage(format("Component '%s' on branch '%s' not found", project.getKey(), "another_branch"));
   }
 
   @Test
index c04117ca5400e95137101745ada3fa03f6b8c020..7a8ab2e5dd365762cc402e6a42d233c5ff8d13ac 100644 (file)
@@ -60,6 +60,7 @@ message Analysis {
   optional string buildString = 5;
   optional bool manualNewCodePeriodBaseline = 6;
   optional string revision = 7;
+  optional string detectedCI = 8;
 }
 
 message QualityGate {