]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11238 api/ce/[task|activity|component] return task warnings
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 11 Sep 2018 12:04:57 +0000 (14:04 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 10 Oct 2018 07:23:02 +0000 (09:23 +0200)
all return warnings count, warnings list is only available from api/ce/task on demand

17 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
server/sonar-server/src/main/java/org/sonar/server/ce/ws/ComponentAction.java
server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskAction.java
server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java
server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
server/sonar-server/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
server/sonar-server/src/test/java/org/sonar/server/ce/ws/TaskActionTest.java
server/sonar-server/src/test/java/org/sonar/server/ce/ws/TaskFormatterTest.java
sonar-ws/src/main/protobuf/ws-ce.proto

index af3e004aa750f6403b80c88a1b470e8a4cbce6ad..a3fcffe471a63b59ed05ddbf1b9d295addac5ca4 100644 (file)
@@ -96,9 +96,14 @@ public class CeActivityDto {
    * Flag indicating whether the analysis of the current activity has a scanner context or not.
    * <p>
    * This property can not be populated when inserting but <strong>is populated when reading</strong>.
-   * </p>
    */
   private boolean hasScannerContext;
+  /**
+   * Count of warnings attached to the current activity.
+   * <p>
+   * This property can not be populated when inserting but <strong>is populated when retrieving the activity by UUID</strong>.
+   */
+  private int warningCount = 0;
 
   CeActivityDto() {
     // required for MyBatis
@@ -311,8 +316,19 @@ public class CeActivityDto {
     return hasScannerContext;
   }
 
-  protected void setHasScannerContext(boolean hasScannerContext) {
+  protected CeActivityDto setHasScannerContext(boolean hasScannerContext) {
     this.hasScannerContext = hasScannerContext;
+    return this;
+  }
+
+  public int getWarningCount() {
+    return warningCount;
+  }
+
+  protected CeActivityDto setWarningCount(int warningCount) {
+    checkArgument(warningCount >= 0);
+    this.warningCount = warningCount;
+    return this;
   }
 
   @Override
@@ -339,6 +355,7 @@ public class CeActivityDto {
       ", errorMessage='" + errorMessage + '\'' +
       ", errorStacktrace='" + errorStacktrace + '\'' +
       ", hasScannerContext=" + hasScannerContext +
+      ", warningCount=" + warningCount +
       '}';
   }
 
index 12af5bf80f049059507b22e8e837985ec337eff2..787d73b1d85456e1c2471ffc28ad3f50cb9b6352 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.db.ce;
 
+import java.util.List;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 
@@ -27,6 +28,13 @@ public class CeTaskMessageDao implements Dao {
     getMapper(dbSession).insert(dto);
   }
 
+  /**
+   * @return the messages for the specific task, if any, in ascending order of column {@code CREATED_AT}.
+   */
+  public List<CeTaskMessageDto> selectByTask(DbSession dbSession, String taskUuid) {
+    return getMapper(dbSession).selectByTask(taskUuid);
+  }
+
   private static CeTaskMessageMapper getMapper(DbSession dbSession) {
     return dbSession.getMapper(CeTaskMessageMapper.class);
   }
index ad312927f3af3bea6f389c42a86cef528bf2e9b3..e44c8b61a1038fe7e71dec52b182ff71fa8d14c2 100644 (file)
  */
 package org.sonar.db.ce;
 
+import java.util.List;
 import org.apache.ibatis.annotations.Param;
 
 public interface CeTaskMessageMapper {
   void insert(@Param("dto") CeTaskMessageDto dto);
 
+  List<CeTaskMessageDto> selectByTask(@Param("taskUuid") String taskUuid);
+
 }
index 372c6a04d131ce5fcef9c984c7f717407cfac29f..61cc9cded153cca2d2535c8bdbed6722ce7af42f 100644 (file)
     csc.task_uuid is not null as hasScannerContext
   </sql>
 
+  <sql id="countWarnings">
+    (select count(1) from ce_task_message ctm where ctm.task_uuid = ca.uuid) as warningCount
+  </sql>
+
   <sql id="columns">
     ca.id,
     ca.uuid,
 
   <select id="selectByUuid" parameterType="String" resultType="org.sonar.db.ce.CeActivityDto">
     select
-    <include refid="columns"/>,
-    ca.error_stacktrace as errorStacktrace
+      <include refid="columns"/>,
+      ca.error_stacktrace as errorStacktrace,
+      <include refid="countWarnings"/>
     from ce_activity ca
-    left outer join ce_scanner_context csc on ca.uuid = csc.task_uuid
-    where ca.uuid=#{uuid,jdbcType=VARCHAR}
+    left outer join ce_scanner_context csc on
+      ca.uuid = csc.task_uuid
+    where
+      ca.uuid=#{uuid,jdbcType=VARCHAR}
   </select>
 
   <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.ce.CeActivityDto">
     select
-      <include refid="columns"/>
+      <include refid="columns"/>,
+      <include refid="countWarnings"/>
     <include refid="sqlSelectByQuery" />
-    order by ca.id desc
+    order by
+      ca.id desc
     limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
   </select>
 
   <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.ce.CeActivityDto" databaseId="mssql">
     select * from (
     select row_number() over(order by id desc) as number,
-      <include refid="columns"/>
+      <include refid="columns"/>,
+      <include refid="countWarnings"/>
       <include refid="sqlSelectByQuery" />
       ) as query
     where
@@ -72,7 +82,8 @@
     select * from (
       select rownum as rn, t.* from (
         select
-        <include refid="columns"/>
+        <include refid="columns"/>,
+        <include refid="countWarnings"/>
         <include refid="sqlSelectByQuery" />
         order by ca.id desc
       ) t
index ef9af42a3f339a72ced2ad6b67f2746471cd081e..a3fcf1f92da86a18d7893f6cc104749217bb55d5 100644 (file)
@@ -3,6 +3,24 @@
 
 <mapper namespace="org.sonar.db.ce.CeTaskMessageMapper">
 
+  <sql id="columns">
+    ctm.uuid,
+    ctm.task_uuid as taskUuid,
+    ctm.message as message,
+    ctm.created_at as createdAt
+  </sql>
+
+  <select id="selectByTask" resultType="org.sonar.db.ce.CeTaskMessageDto">
+    select
+      <include refid="columns"/>
+    from
+      ce_task_message ctm
+    where
+      ctm.task_uuid=#{taskUuid,jdbcType=VARCHAR}
+    order by
+      ctm.created_at asc
+  </select>
+
   <insert id="insert" parameterType="org.sonar.db.ce.CeTaskMessageDto" useGeneratedKeys="false">
     insert into ce_task_message
     (
index c38abaec26eb57bd49380313380ddb0bd03a6989..eef82b7b76db40ff315fa636d22e360ddcc625c1 100644 (file)
@@ -29,6 +29,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.Random;
+import java.util.stream.IntStream;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.assertj.core.api.AbstractListAssert;
@@ -75,7 +77,7 @@ public class CeActivityDaoTest {
 
   @Test
   public void test_insert() {
-    CeActivityDto inserted = insert("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    CeActivityDto inserted = insert("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, SUCCESS);
 
     Optional<CeActivityDto> saved = underTest.selectByUuid(db.getSession(), "TASK_1");
     assertThat(saved).isPresent();
@@ -83,7 +85,7 @@ public class CeActivityDaoTest {
     assertThat(dto.getUuid()).isEqualTo("TASK_1");
     assertThat(dto.getMainComponentUuid()).isEqualTo(MAINCOMPONENT_1);
     assertThat(dto.getComponentUuid()).isEqualTo(COMPONENT_1);
-    assertThat(dto.getStatus()).isEqualTo(CeActivityDto.Status.SUCCESS);
+    assertThat(dto.getStatus()).isEqualTo(SUCCESS);
     assertThat(dto.getSubmitterUuid()).isEqualTo("submitter uuid");
     assertThat(dto.getSubmittedAt()).isEqualTo(1_450_000_000_000L);
     assertThat(dto.getWorkerUuid()).isEqualTo("worker uuid");
@@ -101,6 +103,34 @@ public class CeActivityDaoTest {
     assertThat(dto.getErrorStacktrace()).isNull();
     assertThat(dto.getErrorType()).isNull();
     assertThat(dto.isHasScannerContext()).isFalse();
+    assertThat(dto.getWarningCount()).isZero();
+  }
+
+  @Test
+  public void selectByUuid_populates_warning_count() {
+    CeActivityDto[] tasks = {
+      insert("TASK_1", REPORT, "PROJECT_1", SUCCESS),
+      insert("TASK_2", REPORT, "PROJECT_1", SUCCESS),
+      insert("TASK_3", REPORT, "PROJECT_1", SUCCESS)
+    };
+    int moreThan1 = 2 + new Random().nextInt(5);
+    insertWarnings(tasks[0], moreThan1);
+    insertWarnings(tasks[1], 0);
+    insertWarnings(tasks[2], 1);
+
+    assertThat(underTest.selectByUuid(dbSession, tasks[0].getUuid()).get().getWarningCount()).isEqualTo(moreThan1);
+    assertThat(underTest.selectByUuid(dbSession, tasks[1].getUuid()).get().getWarningCount()).isEqualTo(0);
+    assertThat(underTest.selectByUuid(dbSession, tasks[2].getUuid()).get().getWarningCount()).isEqualTo(1);
+  }
+
+  private void insertWarnings(CeActivityDto task, int warningCount) {
+    IntStream.range(0, warningCount).forEach(i -> db.getDbClient().ceTaskMessageDao().insert(dbSession,
+      new CeTaskMessageDto()
+        .setUuid(UuidFactoryFast.getInstance().create())
+        .setTaskUuid(task.getUuid())
+        .setMessage("message_" + task.getUuid() + "_" + i)
+        .setCreatedAt(task.getUuid().hashCode() + i)));
+    db.commit();
   }
 
   @Test
@@ -296,7 +326,7 @@ public class CeActivityDaoTest {
 
   @Test
   public void test_insert_of_errorMessage_of_1_000_chars() {
-    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
+    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, FAILED)
       .setErrorMessage(Strings.repeat("x", 1_000));
     underTest.insert(db.getSession(), dto);
 
@@ -307,7 +337,7 @@ public class CeActivityDaoTest {
   @Test
   public void test_insert_of_errorMessage_of_1_001_chars_is_truncated_to_1000() {
     String expected = Strings.repeat("x", 1_000);
-    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
+    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, FAILED)
       .setErrorMessage(expected + "y");
     underTest.insert(db.getSession(), dto);
 
@@ -317,7 +347,7 @@ public class CeActivityDaoTest {
 
   @Test
   public void test_insert_error_message_and_stacktrace() {
-    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
+    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, FAILED)
       .setErrorStacktrace("error stack");
     underTest.insert(db.getSession(), dto);
 
@@ -330,7 +360,7 @@ public class CeActivityDaoTest {
 
   @Test
   public void test_insert_error_message_only() {
-    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED);
+    CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, FAILED);
     underTest.insert(db.getSession(), dto);
 
     Optional<CeActivityDto> saved = underTest.selectByUuid(db.getSession(), "TASK_1");
@@ -342,11 +372,11 @@ public class CeActivityDaoTest {
   @Test
   public void insert_must_set_relevant_is_last_field() {
     // only a single task on MAINCOMPONENT_1 -> is_last=true
-    insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", REPORT, MAINCOMPONENT_1, SUCCESS);
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_1").get().getIsLast()).isTrue();
 
     // only a single task on MAINCOMPONENT_2 -> is_last=true
-    insert("TASK_2", REPORT, MAINCOMPONENT_2, CeActivityDto.Status.SUCCESS);
+    insert("TASK_2", REPORT, MAINCOMPONENT_2, SUCCESS);
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_2").get().getIsLast()).isTrue();
 
     // two tasks on MAINCOMPONENT_1, the most recent one is TASK_3
@@ -356,7 +386,7 @@ public class CeActivityDaoTest {
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_3").get().getIsLast()).isTrue();
 
     // inserting a cancelled task does not change the last task
-    insert("TASK_4", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.CANCELED);
+    insert("TASK_4", REPORT, MAINCOMPONENT_1, CANCELED);
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_1").get().getIsLast()).isFalse();
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_2").get().getIsLast()).isTrue();
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_3").get().getIsLast()).isTrue();
@@ -365,10 +395,10 @@ public class CeActivityDaoTest {
 
   @Test
   public void test_selectByQuery() {
-    insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", REPORT, MAINCOMPONENT_1, SUCCESS);
     insert("TASK_2", REPORT, MAINCOMPONENT_1, FAILED);
-    insert("TASK_3", REPORT, MAINCOMPONENT_2, CeActivityDto.Status.SUCCESS);
-    insert("TASK_4", "views", null, CeActivityDto.Status.SUCCESS);
+    insert("TASK_3", REPORT, MAINCOMPONENT_2, SUCCESS);
+    insert("TASK_4", "views", null, SUCCESS);
 
     // no filters
     CeTaskQuery query = new CeTaskQuery().setStatuses(Collections.emptyList());
@@ -381,7 +411,7 @@ public class CeActivityDaoTest {
     assertThat(dtos).extracting("uuid").containsExactly("TASK_2", "TASK_1");
 
     // select by status
-    query = new CeTaskQuery().setStatuses(singletonList(CeActivityDto.Status.SUCCESS.name()));
+    query = new CeTaskQuery().setStatuses(singletonList(SUCCESS.name()));
     dtos = underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100));
     assertThat(dtos).extracting("uuid").containsExactly("TASK_4", "TASK_3", "TASK_1");
 
@@ -423,12 +453,62 @@ public class CeActivityDaoTest {
     assertThat(dto.isHasScannerContext()).isTrue();
   }
 
+  @Test
+  public void selectByQuery_populates_warningCount() {
+    CeActivityDto[] tasks = {
+      insert("TASK_1", REPORT, "PROJECT_1", SUCCESS),
+      insert("TASK_2", REPORT, "PROJECT_1", FAILED),
+      insert("TASK_3", REPORT, "PROJECT_2", SUCCESS),
+      insert("TASK_4", "views", null, SUCCESS)
+    };
+    int moreThan1 = 2 + new Random().nextInt(5);
+    insertWarnings(tasks[0], moreThan1);
+    insertWarnings(tasks[1], 3);
+    insertWarnings(tasks[2], 1);
+    insertWarnings(tasks[3], 6);
+
+    // no filters
+    CeTaskQuery query = new CeTaskQuery().setStatuses(Collections.emptyList());
+    assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(10)))
+      .extracting(CeActivityDto::getUuid, CeActivityDto::getWarningCount)
+      .containsExactly(tuple("TASK_4", 6), tuple("TASK_3", 1), tuple("TASK_2", 3), tuple("TASK_1", moreThan1));
+
+    // select by component uuid
+    query = new CeTaskQuery().setMainComponentUuid("PROJECT_1");
+    assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100)))
+      .extracting(CeActivityDto::getUuid, CeActivityDto::getWarningCount)
+      .containsExactly(tuple("TASK_2", 3), tuple("TASK_1", moreThan1));
+
+    // select by status
+    query = new CeTaskQuery().setStatuses(singletonList(SUCCESS.name()));
+    assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100)))
+      .extracting(CeActivityDto::getUuid, CeActivityDto::getWarningCount)
+      .containsExactly(tuple("TASK_4", 6), tuple("TASK_3", 1), tuple("TASK_1", moreThan1));
+
+    // select by type
+    query = new CeTaskQuery().setType(REPORT);
+    assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100)))
+      .extracting(CeActivityDto::getUuid, CeActivityDto::getWarningCount)
+      .containsExactly(tuple("TASK_3", 1), tuple("TASK_2", 3), tuple("TASK_1", moreThan1));
+    query = new CeTaskQuery().setType("views");
+    assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100)))
+      .extracting(CeActivityDto::getUuid, CeActivityDto::getWarningCount)
+      .containsExactly(tuple("TASK_4", 6));
+
+    // select by multiple conditions
+    query = new CeTaskQuery().setType(REPORT).setOnlyCurrents(true).setMainComponentUuid("PROJECT_1");
+    assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100)))
+      .extracting(CeActivityDto::getUuid, CeActivityDto::getWarningCount)
+      .containsExactly(tuple("TASK_2", 3));
+
+  }
+
   @Test
   public void selectByQuery_is_paginated_and_return_results_sorted_from_last_to_first() {
-    insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
-    insert("TASK_2", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.FAILED);
-    insert("TASK_3", REPORT, MAINCOMPONENT_2, CeActivityDto.Status.SUCCESS);
-    insert("TASK_4", "views", null, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", REPORT, MAINCOMPONENT_1, SUCCESS);
+    insert("TASK_2", REPORT, MAINCOMPONENT_1, FAILED);
+    insert("TASK_3", REPORT, MAINCOMPONENT_2, SUCCESS);
+    insert("TASK_4", "views", null, SUCCESS);
 
     assertThat(selectPageOfUuids(forPage(1).andSize(1))).containsExactly("TASK_4");
     assertThat(selectPageOfUuids(forPage(2).andSize(1))).containsExactly("TASK_3");
@@ -441,7 +521,7 @@ public class CeActivityDaoTest {
 
   @Test
   public void selectByQuery_no_results_if_shortcircuited_by_component_uuids() {
-    insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", REPORT, MAINCOMPONENT_1, SUCCESS);
 
     CeTaskQuery query = new CeTaskQuery();
     query.setMainComponentUuids(Collections.emptyList());
@@ -474,7 +554,7 @@ public class CeActivityDaoTest {
     queueDto.setUuid(uuid);
     queueDto.setTaskType("fake");
     CeActivityDto dto = new CeActivityDto(queueDto);
-    dto.setStatus(CeActivityDto.Status.SUCCESS);
+    dto.setStatus(SUCCESS);
     dto.setSubmittedAt(submittedAt);
     dto.setExecutedAt(executedAt);
     underTest.insert(db.getSession(), dto);
@@ -513,11 +593,26 @@ public class CeActivityDaoTest {
       .extracting("errorStacktrace").containsOnly((String) null);
   }
 
+  @Test
+  public void selectOlderThan_does_not_populate_warningCount() {
+    CeActivityDto activity1 = insert("TASK_1", REPORT, "PROJECT_1", FAILED);
+    insertWarnings(activity1, 10);
+    CeActivityDto activity2 = insert("TASK_2", REPORT, "PROJECT_1", SUCCESS);
+    insertWarnings(activity2, 1);
+
+    List<CeActivityDto> dtos = underTest.selectOlderThan(db.getSession(), system2.now() + 1_000_000L);
+
+    assertThat(dtos)
+      .hasSize(2)
+      .extracting(CeActivityDto::getWarningCount)
+      .containsOnly(0);
+  }
+
   @Test
   public void deleteByUuids() {
-    insert("TASK_1", "REPORT", MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
-    insert("TASK_2", "REPORT", MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
-    insert("TASK_3", "REPORT", MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", "REPORT", MAINCOMPONENT_1, SUCCESS);
+    insert("TASK_2", "REPORT", MAINCOMPONENT_1, SUCCESS);
+    insert("TASK_3", "REPORT", MAINCOMPONENT_1, SUCCESS);
 
     underTest.deleteByUuids(db.getSession(), ImmutableSet.of("TASK_1", "TASK_3"));
     assertThat(underTest.selectByUuid(db.getSession(), "TASK_1").isPresent()).isFalse();
@@ -527,7 +622,7 @@ public class CeActivityDaoTest {
 
   @Test
   public void deleteByUuids_does_nothing_if_uuid_does_not_exist() {
-    insert("TASK_1", "REPORT", MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", "REPORT", MAINCOMPONENT_1, SUCCESS);
 
     // must not fail
     underTest.deleteByUuids(db.getSession(), singleton("TASK_2"));
@@ -537,14 +632,14 @@ public class CeActivityDaoTest {
 
   @Test
   public void count_last_by_status_and_main_component_uuid() {
-    insert("TASK_1", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_1", CeTaskTypes.REPORT, MAINCOMPONENT_1, SUCCESS);
     // component 2
-    insert("TASK_2", CeTaskTypes.REPORT, MAINCOMPONENT_2, CeActivityDto.Status.SUCCESS);
+    insert("TASK_2", CeTaskTypes.REPORT, MAINCOMPONENT_2, SUCCESS);
     // status failed
-    insert("TASK_3", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.FAILED);
+    insert("TASK_3", CeTaskTypes.REPORT, MAINCOMPONENT_1, FAILED);
     // status canceled
-    insert("TASK_4", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.CANCELED);
-    insert("TASK_5", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
+    insert("TASK_4", CeTaskTypes.REPORT, MAINCOMPONENT_1, CANCELED);
+    insert("TASK_5", CeTaskTypes.REPORT, MAINCOMPONENT_1, SUCCESS);
     db.commit();
 
     assertThat(underTest.countLastByStatusAndMainComponentUuid(dbSession, SUCCESS, MAINCOMPONENT_1)).isEqualTo(1);
@@ -601,7 +696,7 @@ public class CeActivityDaoTest {
     queueDto.setTaskType("fake");
 
     CeActivityDto dto = new CeActivityDto(queueDto);
-    dto.setStatus(CeActivityDto.Status.SUCCESS);
+    dto.setStatus(SUCCESS);
     dto.setAnalysisUuid(uuid + "_AA");
     system2.setNow(date);
     underTest.insert(db.getSession(), dto);
index 1035ea80d9fce082edd58c02f8a2a9b64641084c..c11d17e14db7571cdda0089efe5bdadb4a75e72c 100644 (file)
@@ -132,6 +132,16 @@ public class CeActivityDtoTest {
     assertThat(underTest.getErrorMessage()).isEqualTo(before + after);
   }
 
+  @Test
+  public void setWarningCount_throws_IAE_if_less_than_0() {
+    underTest.setWarningCount(0);
+    underTest.setWarningCount(1 + new Random().nextInt(10));
+
+    expectedException.expect(IllegalArgumentException.class);
+
+    underTest.setWarningCount(-1 - new Random().nextInt(10));
+  }
+
   @DataProvider
   public static Object[][] stringsWithChar0() {
     return new Object[][] {
index f219e8873ab568721a7a149eb32fff75bcfc1d29..23db043dc962350a253303e3906862e1cd466629 100644 (file)
  */
 package org.sonar.db.ce;
 
+import java.util.List;
 import org.assertj.core.groups.Tuple;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class CeTaskMessageDaoTest {
@@ -49,6 +52,51 @@ public class CeTaskMessageDaoTest {
       .hasSize(1)
       .extracting(t -> t.get("UUID"), t -> t.get("TASK_UUID"), t -> t.get("MESSAGE"), t -> t.get("CREATED_AT"))
       .containsOnly(Tuple.tuple("uuid_1", "task_uuid_1", "message_1", 1_222_333L));
+  }
+
+  @Test
+  public void selectByTask_returns_empty_on_empty_table() {
+    String taskUuid = randomAlphabetic(10);
+
+    List<CeTaskMessageDto> dto = underTest.selectByTask(dbTester.getSession(), taskUuid);
+
+    assertThat(dto).isEmpty();
+  }
+
+  @Test
+  public void selectByTask_returns_message_of_task_ordered_by_CREATED_AT_asc() {
+    String task1 = "task1";
+    String task2 = "task2";
+    CeTaskMessageDto[] messages = {
+      insertMessage(task1, 0, 1_222_333L),
+      insertMessage(task2, 1, 2_222_333L),
+      insertMessage(task2, 2, 1_111_333L),
+      insertMessage(task1, 3, 1_222_111L),
+      insertMessage(task1, 4, 222_111L),
+      insertMessage(task1, 5, 3_222_111L)
+    };
+
+    assertThat(underTest.selectByTask(dbTester.getSession(), task1))
+      .extracting(CeTaskMessageDto::getUuid)
+      .containsExactly(messages[4].getUuid(), messages[3].getUuid(), messages[0].getUuid(), messages[5].getUuid());
+
+    assertThat(underTest.selectByTask(dbTester.getSession(), task2))
+      .extracting(CeTaskMessageDto::getUuid)
+      .containsExactly(messages[2].getUuid(), messages[1].getUuid());
+
+    assertThat(underTest.selectByTask(dbTester.getSession(), randomAlphabetic(5)))
+      .isEmpty();
+  }
 
+  private CeTaskMessageDto insertMessage(String taskUuid, int i, long createdAt) {
+    CeTaskMessageDto res = new CeTaskMessageDto()
+      .setUuid("message_" + i)
+      .setTaskUuid(taskUuid)
+      .setMessage("test_" + i)
+      .setCreatedAt(createdAt);
+    DbSession dbSession = dbTester.getSession();
+    underTest.insert(dbSession, res);
+    dbSession.commit();
+    return res;
   }
 }
index c80223efdf70758630c3485424c9e04ccc441e64..ceb2cbcf15d756c19161cc6efc252c53ae28bfd2 100644 (file)
 package org.sonar.server.ce.ws;
 
 import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -53,6 +53,7 @@ import static java.lang.Boolean.parseBoolean;
 import static java.lang.Integer.parseInt;
 import static java.lang.String.format;
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.apache.commons.lang.StringUtils.defaultString;
 import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime;
@@ -209,16 +210,16 @@ public class ActivityAction implements CeWsAction {
   private Optional<Ce.Task> searchTaskByUuid(DbSession dbSession, Request request) {
     String textQuery = request.getQ();
     if (textQuery == null) {
-      return Optional.absent();
+      return Optional.empty();
     }
 
-    java.util.Optional<CeQueueDto> queue = dbClient.ceQueueDao().selectByUuid(dbSession, textQuery);
+    Optional<CeQueueDto> queue = dbClient.ceQueueDao().selectByUuid(dbSession, textQuery);
     if (queue.isPresent()) {
       return Optional.of(formatter.formatQueue(dbSession, queue.get()));
     }
 
-    java.util.Optional<CeActivityDto> activity = dbClient.ceActivityDao().selectByUuid(dbSession, textQuery);
-    return activity.map(ceActivityDto -> Optional.of(formatter.formatActivity(dbSession, ceActivityDto, null))).orElseGet(Optional::absent);
+    Optional<CeActivityDto> activity = dbClient.ceActivityDao().selectByUuid(dbSession, textQuery);
+    return activity.map(ceActivityDto -> formatter.formatActivity(dbSession, ceActivityDto, null, emptyList()));
   }
 
   private CeTaskQuery buildQuery(DbSession dbSession, Request request, @Nullable ComponentDto component) {
index 8374b8ff42d0ca93c7493f62b41f12aee13da6a6..89a09228a41808ce8753bb1ebbf131d9ccd6aa88 100644 (file)
@@ -38,11 +38,12 @@ import org.sonar.server.ws.KeyExamples;
 import org.sonarqube.ws.Ce;
 import org.sonarqube.ws.Ce.ComponentResponse;
 
+import static java.util.Collections.emptyList;
 import static org.sonar.db.Pagination.forPage;
-import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonar.server.ce.ws.CeWsParameters.PARAM_COMPONENT;
 import static org.sonar.server.ce.ws.CeWsParameters.PARAM_COMPONENT_ID;
+import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class ComponentAction implements CeWsAction {
 
@@ -97,7 +98,7 @@ public class ComponentAction implements CeWsAction {
       Ce.ComponentResponse.Builder wsResponseBuilder = ComponentResponse.newBuilder();
       wsResponseBuilder.addAllQueue(formatter.formatQueue(dbSession, queueDtos));
       if (activityDtos.size() == 1) {
-        wsResponseBuilder.setCurrent(formatter.formatActivity(dbSession, activityDtos.get(0), null));
+        wsResponseBuilder.setCurrent(formatter.formatActivity(dbSession, activityDtos.get(0), null, emptyList()));
       }
       writeProtobuf(wsResponseBuilder.build(), wsRequest, wsResponse);
     }
index 0d1305b62166d0c83b4f253fadca35760818a4dc..6e7d7ae330d653b1335c8eb815c2fff137b644a7 100644 (file)
@@ -38,6 +38,7 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.ce.CeActivityDto;
 import org.sonar.db.ce.CeQueueDto;
+import org.sonar.db.ce.CeTaskMessageDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.server.user.UserSession;
@@ -105,7 +106,9 @@ public class TaskAction implements CeWsAction {
         Set<AdditionalField> additionalFields = AdditionalField.getFromRequest(wsRequest);
         maskErrorStacktrace(ceActivityDto, additionalFields);
         wsTaskResponse.setTask(
-          wsTaskFormatter.formatActivity(dbSession, ceActivityDto, extractScannerContext(dbSession, ceActivityDto, additionalFields)));
+          wsTaskFormatter.formatActivity(dbSession, ceActivityDto,
+            extractScannerContext(dbSession, ceActivityDto, additionalFields),
+            extractWarnings(dbSession, ceActivityDto, additionalFields)));
       }
       writeProtobuf(wsTaskResponse.build(), wsRequest, wsResponse);
     }
@@ -147,9 +150,20 @@ public class TaskAction implements CeWsAction {
     return null;
   }
 
+  private List<String> extractWarnings(DbSession dbSession, CeActivityDto activityDto, Set<AdditionalField> additionalFields) {
+    if (additionalFields.contains(AdditionalField.WARNINGS)) {
+      List<CeTaskMessageDto> dtos = dbClient.ceTaskMessageDao().selectByTask(dbSession, activityDto.getUuid());
+      return dtos.stream()
+        .map(CeTaskMessageDto::getMessage)
+        .collect(MoreCollectors.toList(dtos.size()));
+    }
+    return Collections.emptyList();
+  }
+
   private enum AdditionalField {
     STACKTRACE("stacktrace"),
-    SCANNER_CONTEXT("scannerContext");
+    SCANNER_CONTEXT("scannerContext"),
+    WARNINGS("warnings");
 
     private final String label;
 
index 54e48e3c160c06d14c4cec6bde50c339f700ded6..31c3687e9771eba6cf6b14906ef477d8c4cd46c9 100644 (file)
@@ -46,6 +46,7 @@ import org.sonarqube.ws.Common;
 
 import static com.google.common.base.Preconditions.checkState;
 import static java.lang.String.format;
+import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.core.util.Protobuf.setNullable;
@@ -95,18 +96,18 @@ public class TaskFormatter {
     return builder.build();
   }
 
-  public Ce.Task formatActivity(DbSession dbSession, CeActivityDto dto, @Nullable String scannerContext) {
-    return formatActivity(dto, DtoCache.forActivityDtos(dbClient, dbSession, singletonList(dto)), scannerContext);
+  public Ce.Task formatActivity(DbSession dbSession, CeActivityDto dto, @Nullable String scannerContext, List<String> warnings) {
+    return formatActivity(dto, DtoCache.forActivityDtos(dbClient, dbSession, singletonList(dto)), scannerContext, warnings);
   }
 
   public List<Ce.Task> formatActivity(DbSession dbSession, List<CeActivityDto> dtos) {
     DtoCache cache = DtoCache.forActivityDtos(dbClient, dbSession, dtos);
     return dtos.stream()
-      .map(input -> formatActivity(input, cache, null))
+      .map(input -> formatActivity(input, cache, null, emptyList()))
       .collect(MoreCollectors.toList(dtos.size()));
   }
 
-  private static Ce.Task formatActivity(CeActivityDto dto, DtoCache cache, @Nullable String scannerContext) {
+  private static Ce.Task formatActivity(CeActivityDto dto, DtoCache cache, @Nullable String scannerContext, List<String> warnings) {
     Ce.Task.Builder builder = Ce.Task.newBuilder();
     String organizationKey = cache.getOrganizationKey(dto.getComponentUuid());
     setNullable(organizationKey, builder::setOrganization);
@@ -129,6 +130,9 @@ public class TaskFormatter {
     setNullable(dto.getErrorType(), builder::setErrorType);
     setNullable(scannerContext, builder::setScannerContext);
     builder.setHasScannerContext(dto.isHasScannerContext());
+    builder.setWarningCount(dto.getWarningCount());
+    warnings.forEach(builder::addWarnings);
+
     return builder.build();
   }
 
index 846a358ca912dbf7b26d28b6069888004c331db9..4d0792f809521909bb11f0e03df76a78eebc0b44 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.ce.ws;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
@@ -36,6 +37,7 @@ import org.sonar.db.ce.CeActivityDto;
 import org.sonar.db.ce.CeActivityDto.Status;
 import org.sonar.db.ce.CeQueueDto;
 import org.sonar.db.ce.CeTaskCharacteristicDto;
+import org.sonar.db.ce.CeTaskMessageDto;
 import org.sonar.db.ce.CeTaskTypes;
 import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
@@ -57,6 +59,7 @@ import org.sonarqube.ws.Common;
 import org.sonarqube.ws.MediaTypes;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Mockito.mock;
@@ -116,6 +119,7 @@ public class ActivityActionTest {
     assertThat(task.hasAnalysisId()).isFalse();
     assertThat(task.getExecutionTimeMs()).isEqualTo(500L);
     assertThat(task.getLogs()).isFalse();
+    assertThat(task.getWarningCount()).isZero();
 
     task = activityResponse.getTasks(1);
     assertThat(task.getId()).isEqualTo("T1");
@@ -123,6 +127,7 @@ public class ActivityActionTest {
     assertThat(task.getComponentId()).isEqualTo(project1.uuid());
     assertThat(task.getLogs()).isFalse();
     assertThat(task.getOrganization()).isEqualTo(org1.getKey());
+    assertThat(task.getWarningCount()).isZero();
   }
 
   @Test
@@ -214,6 +219,36 @@ public class ActivityActionTest {
     assertPage(10, asList("T3", "T2", "T1"));
   }
 
+  @Test
+  public void return_warnings_count_on_queue_and_activity_but_no_warnings_list() {
+    logInAsSystemAdministrator();
+    ComponentDto project1 = db.components().insertPrivateProject();
+    ComponentDto project2 = db.components().insertPrivateProject();
+    insertActivity("T1", project1, SUCCESS);
+    insertActivity("T2", project2, FAILED);
+    insertQueue("T3", project1, IN_PROGRESS);
+    insertMessages("T1", 2);
+    insertMessages("T2", 0);
+    insertMessages("T3", 5);
+
+    ActivityResponse activityResponse = call(ws.newRequest()
+      .setParam(Param.PAGE_SIZE, Integer.toString(10))
+      .setParam(PARAM_STATUS, "SUCCESS,FAILED,CANCELED,IN_PROGRESS,PENDING"));
+    assertThat(activityResponse.getTasksList())
+      .extracting(Task::getId, Task::getWarningCount, Task::getWarningsList)
+      .containsOnly(tuple("T1", 2, emptyList()), tuple("T2", 0, emptyList()), tuple("T3", 0, emptyList()));
+  }
+
+  private void insertMessages(String taskUuid, int messageCount) {
+    IntStream.range(0, messageCount)
+      .forEach(i -> db.getDbClient().ceTaskMessageDao().insert(db.getSession(), new CeTaskMessageDto()
+        .setUuid("uuid_" + taskUuid + "_" + i)
+        .setTaskUuid(taskUuid)
+        .setMessage("m_" + taskUuid + "_" + i)
+        .setCreatedAt(taskUuid.hashCode() + i)));
+    db.commit();
+  }
+
   @Test
   public void project_administrator_can_access_his_project_activity() {
     ComponentDto project1 = db.components().insertPrivateProject();
index ff927dc762da8a355b8dd31911d975921c36d73a..4bbbea6cfea3e9a8287e8f830bdbe7169e945105 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.server.ce.ws;
 
 import java.util.Collections;
+import java.util.Random;
+import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
@@ -31,6 +33,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.ce.CeActivityDto;
 import org.sonar.db.ce.CeQueueDto;
 import org.sonar.db.ce.CeTaskCharacteristicDto;
+import org.sonar.db.ce.CeTaskMessageDto;
 import org.sonar.db.ce.CeTaskTypes;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
@@ -44,6 +47,7 @@ import org.sonarqube.ws.Ce;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.MediaTypes;
 
+import static java.util.Collections.emptyList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.db.ce.CeActivityDto.Status.SUCCESS;
@@ -103,12 +107,15 @@ public class ComponentActionTest {
     assertThat(response.getQueue(1).getId()).isEqualTo("T5");
     // T3 is the latest task executed on PROJECT_1
     assertThat(response.hasCurrent()).isTrue();
-    assertThat(response.getCurrent().getId()).isEqualTo("T3");
-    assertThat(response.getCurrent().hasAnalysisId()).isFalse();
+    Ce.Task current = response.getCurrent();
+    assertThat(current.getId()).isEqualTo("T3");
+    assertThat(current.hasAnalysisId()).isFalse();
+    assertThat(current.getWarningCount()).isZero();
+    assertThat(current.getWarningsList()).isEmpty();
     assertThat(response.getQueueList())
       .extracting(Ce.Task::getOrganization)
       .containsOnly(organization.getKey());
-    assertThat(response.getCurrent().getOrganization()).isEqualTo(organization.getKey());
+    assertThat(current.getOrganization()).isEqualTo(organization.getKey());
   }
 
   @Test
@@ -122,8 +129,11 @@ public class ComponentActionTest {
       .setParam(PARAM_COMPONENT, project.getDbKey())
       .executeProtobuf(Ce.ComponentResponse.class);
     assertThat(response.hasCurrent()).isTrue();
-    assertThat(response.getCurrent().getId()).isEqualTo("T1");
-    assertThat(response.getCurrent().getAnalysisId()).isEqualTo(analysis.getUuid());
+    Ce.Task current = response.getCurrent();
+    assertThat(current.getId()).isEqualTo("T1");
+    assertThat(current.getAnalysisId()).isEqualTo(analysis.getUuid());
+    assertThat(current.getWarningCount()).isZero();
+    assertThat(current.getWarningsList()).isEmpty();
   }
 
   @Test
@@ -137,8 +147,11 @@ public class ComponentActionTest {
       .setParam(PARAM_COMPONENT_ID, project.uuid())
       .executeProtobuf(Ce.ComponentResponse.class);
     assertThat(response.hasCurrent()).isTrue();
-    assertThat(response.getCurrent().getId()).isEqualTo("T1");
-    assertThat(response.getCurrent().getAnalysisId()).isEqualTo(analysis.getUuid());
+    Ce.Task current = response.getCurrent();
+    assertThat(current.getId()).isEqualTo("T1");
+    assertThat(current.getAnalysisId()).isEqualTo(analysis.getUuid());
+    assertThat(current.getWarningCount()).isZero();
+    assertThat(current.getWarningsList()).isEmpty();
   }
 
   @Test
@@ -157,7 +170,10 @@ public class ComponentActionTest {
     assertThat(response.getQueueCount()).isEqualTo(0);
     // T3 is the latest task executed on PROJECT_1 ignoring Canceled ones
     assertThat(response.hasCurrent()).isTrue();
-    assertThat(response.getCurrent().getId()).isEqualTo("T3");
+    Ce.Task current = response.getCurrent();
+    assertThat(current.getId()).isEqualTo("T3");
+    assertThat(current.getWarningCount()).isZero();
+    assertThat(current.getWarningsList()).isEmpty();
   }
 
   @Test
@@ -175,9 +191,9 @@ public class ComponentActionTest {
       .executeProtobuf(Ce.ComponentResponse.class);
 
     assertThat(response.getCurrent())
-      .extracting(Ce.Task::getId, Ce.Task::getBranch, Ce.Task::getBranchType, Ce.Task::getStatus, Ce.Task::getComponentKey)
+      .extracting(Ce.Task::getId, Ce.Task::getBranch, Ce.Task::getBranchType, Ce.Task::getStatus, Ce.Task::getComponentKey, Ce.Task::getWarningCount, Ce.Task::getWarningsList)
       .containsOnly(
-        "T1", longLivingBranch.getBranch(), Common.BranchType.LONG, Ce.TaskStatus.SUCCESS, project.getKey());
+        "T1", longLivingBranch.getBranch(), Common.BranchType.LONG, Ce.TaskStatus.SUCCESS, project.getKey(), 0, emptyList());
   }
 
   @Test
@@ -197,10 +213,10 @@ public class ComponentActionTest {
       .executeProtobuf(Ce.ComponentResponse.class);
 
     assertThat(response.getQueueList())
-      .extracting(Ce.Task::getId, Ce.Task::getBranch, Ce.Task::getBranchType, Ce.Task::getStatus, Ce.Task::getComponentKey)
+      .extracting(Ce.Task::getId, Ce.Task::getBranch, Ce.Task::getBranchType, Ce.Task::getStatus, Ce.Task::getComponentKey, Ce.Task::getWarningCount, Ce.Task::getWarningsList)
       .containsOnly(
-        tuple("T1", longLivingBranch.getBranch(), Common.BranchType.LONG, Ce.TaskStatus.IN_PROGRESS, project.getKey()),
-        tuple("T2", longLivingBranch.getBranch(), Common.BranchType.LONG, Ce.TaskStatus.PENDING, project.getKey()));
+        tuple("T1", longLivingBranch.getBranch(), Common.BranchType.LONG, Ce.TaskStatus.IN_PROGRESS, project.getKey(), 0, emptyList()),
+        tuple("T2", longLivingBranch.getBranch(), Common.BranchType.LONG, Ce.TaskStatus.PENDING, project.getKey(), 0, emptyList()));
   }
 
   @Test
@@ -222,11 +238,34 @@ public class ComponentActionTest {
       .executeProtobuf(Ce.ComponentResponse.class);
 
     assertThat(response.getQueueList())
-      .extracting(Ce.Task::getId, Ce.Task::getComponentKey, Ce.Task::getBranch, Ce.Task::getBranchType)
+      .extracting(Ce.Task::getId, Ce.Task::getComponentKey, Ce.Task::getBranch, Ce.Task::getBranchType, Ce.Task::getWarningCount, Ce.Task::getWarningsList)
       .containsOnly(
-        tuple("Main", project.getKey(), "", Common.BranchType.UNKNOWN_BRANCH_TYPE),
-        tuple("Long", longLivingBranch.getKey(), longLivingBranch.getBranch(), Common.BranchType.LONG),
-        tuple("Short", shortLivingBranch.getKey(), shortLivingBranch.getBranch(), Common.BranchType.SHORT));
+        tuple("Main", project.getKey(), "", Common.BranchType.UNKNOWN_BRANCH_TYPE, 0, emptyList()),
+        tuple("Long", longLivingBranch.getKey(), longLivingBranch.getBranch(), Common.BranchType.LONG, 0, emptyList()),
+        tuple("Short", shortLivingBranch.getKey(), shortLivingBranch.getBranch(), Common.BranchType.SHORT, 0, emptyList()));
+  }
+
+  @Test
+  public void populates_warning_count_of_activities_but_not_warnings() {
+    ComponentDto privateProject = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, privateProject);
+    SnapshotDto analysis = db.components().insertSnapshot(privateProject);
+    CeActivityDto activity = insertActivity("Short", privateProject, SUCCESS, analysis);
+    int messageCount = 1 + new Random().nextInt(10);
+    IntStream.range(0, messageCount).forEach(i -> db.getDbClient().ceTaskMessageDao().insert(db.getSession(), new CeTaskMessageDto()
+      .setUuid("uuid_" + i)
+      .setTaskUuid(activity.getUuid())
+      .setMessage("m_" + i)
+      .setCreatedAt(i)));
+    db.commit();
+
+    Ce.ComponentResponse response = ws.newRequest()
+      .setParam(PARAM_COMPONENT, privateProject.getKey())
+      .executeProtobuf(Ce.ComponentResponse.class);
+    assertThat(response.hasCurrent()).isTrue();
+    assertThat(response.getCurrent())
+      .extracting(Ce.Task::getWarningCount, Ce.Task::getWarningsList)
+      .containsOnly(messageCount, emptyList());
   }
 
   @Test
index 44333e126740c80b0f79c96f888df2dbca3b93c0..21e124a1a52050fd38410041f5f599d8b01b885a 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.server.ce.ws;
 
 import java.util.Collections;
+import java.util.Random;
+import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Rule;
@@ -29,14 +31,17 @@ import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.util.CloseableIterator;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.core.util.Uuids;
 import org.sonar.db.DbTester;
 import org.sonar.db.ce.CeActivityDto;
 import org.sonar.db.ce.CeQueueDto;
 import org.sonar.db.ce.CeTaskCharacteristicDto;
+import org.sonar.db.ce.CeTaskMessageDto;
 import org.sonar.db.ce.CeTaskTypes;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -47,6 +52,7 @@ import org.sonarqube.ws.Common;
 
 import static java.util.Collections.singleton;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
 import static org.sonar.db.ce.CeTaskCharacteristicDto.BRANCH_KEY;
 import static org.sonar.db.ce.CeTaskCharacteristicDto.BRANCH_TYPE_KEY;
 import static org.sonar.db.component.BranchType.LONG;
@@ -65,16 +71,18 @@ public class TaskActionTest {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
 
-  private OrganizationDto organizationDto;
-  private ComponentDto project;
+  private OrganizationDto organization;
+  private ComponentDto privateProject;
+  private ComponentDto publicProject;
   private TaskFormatter formatter = new TaskFormatter(db.getDbClient(), System2.INSTANCE);
   private TaskAction underTest = new TaskAction(db.getDbClient(), formatter, userSession);
   private WsActionTester ws = new WsActionTester(underTest);
 
   @Before
   public void setUp() {
-    organizationDto = db.organizations().insert();
-    project = db.components().insertPrivateProject(organizationDto);
+    organization = db.organizations().insert();
+    privateProject = db.components().insertPrivateProject(organization);
+    publicProject = db.components().insertPublicProject(organization);
   }
 
   @Test
@@ -85,7 +93,7 @@ public class TaskActionTest {
     CeQueueDto queueDto = new CeQueueDto();
     queueDto.setTaskType(CeTaskTypes.REPORT);
     queueDto.setUuid(SOME_TASK_UUID);
-    queueDto.setComponentUuid(project.uuid());
+    queueDto.setComponentUuid(privateProject.uuid());
     queueDto.setStatus(CeQueueDto.Status.PENDING);
     queueDto.setSubmitterUuid(user.getUuid());
     persist(queueDto);
@@ -93,15 +101,39 @@ public class TaskActionTest {
     Ce.TaskResponse taskResponse = ws.newRequest()
       .setParam("id", SOME_TASK_UUID)
       .executeProtobuf(Ce.TaskResponse.class);
-    assertThat(taskResponse.getTask().getOrganization()).isEqualTo(organizationDto.getKey());
+    assertThat(taskResponse.getTask().getOrganization()).isEqualTo(organization.getKey());
     assertThat(taskResponse.getTask().getId()).isEqualTo(SOME_TASK_UUID);
     assertThat(taskResponse.getTask().getStatus()).isEqualTo(Ce.TaskStatus.PENDING);
     assertThat(taskResponse.getTask().getSubmitterLogin()).isEqualTo(user.getLogin());
-    assertThat(taskResponse.getTask().getComponentId()).isEqualTo(project.uuid());
-    assertThat(taskResponse.getTask().getComponentKey()).isEqualTo(project.getDbKey());
-    assertThat(taskResponse.getTask().getComponentName()).isEqualTo(project.name());
+    assertThat(taskResponse.getTask().getComponentId()).isEqualTo(privateProject.uuid());
+    assertThat(taskResponse.getTask().getComponentKey()).isEqualTo(privateProject.getDbKey());
+    assertThat(taskResponse.getTask().getComponentName()).isEqualTo(privateProject.name());
     assertThat(taskResponse.getTask().hasExecutionTimeMs()).isFalse();
     assertThat(taskResponse.getTask().getLogs()).isFalse();
+    assertThat(taskResponse.getTask().getWarningCount()).isZero();
+    assertThat(taskResponse.getTask().getWarningsList()).isEmpty();
+  }
+
+  @Test
+  public void no_warning_detail_on_task_in_queue() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user).setRoot();
+    CeQueueDto queueDto = createAndPersistQueueTask(null, user);
+    IntStream.range(0, 1 + new Random().nextInt(5))
+      .forEach(i -> db.getDbClient().ceTaskMessageDao().insert(db.getSession(),
+        new CeTaskMessageDto()
+          .setUuid("u_" + i)
+          .setTaskUuid(queueDto.getUuid())
+          .setMessage("m_" + i)
+          .setCreatedAt(queueDto.getUuid().hashCode() + i)));
+    db.commit();
+
+    Ce.TaskResponse taskResponse = ws.newRequest()
+      .setParam("id", SOME_TASK_UUID)
+      .executeProtobuf(Ce.TaskResponse.class);
+    Ce.Task task = taskResponse.getTask();
+    assertThat(task.getWarningCount()).isZero();
+    assertThat(task.getWarningsList()).isEmpty();
   }
 
   @Test
@@ -116,15 +148,17 @@ public class TaskActionTest {
       .setParam("id", SOME_TASK_UUID)
       .executeProtobuf(Ce.TaskResponse.class);
     Ce.Task task = taskResponse.getTask();
-    assertThat(task.getOrganization()).isEqualTo(organizationDto.getKey());
+    assertThat(task.getOrganization()).isEqualTo(organization.getKey());
     assertThat(task.getId()).isEqualTo(SOME_TASK_UUID);
     assertThat(task.getStatus()).isEqualTo(Ce.TaskStatus.FAILED);
-    assertThat(task.getComponentId()).isEqualTo(project.uuid());
-    assertThat(task.getComponentKey()).isEqualTo(project.getDbKey());
-    assertThat(task.getComponentName()).isEqualTo(project.name());
+    assertThat(task.getComponentId()).isEqualTo(privateProject.uuid());
+    assertThat(task.getComponentKey()).isEqualTo(privateProject.getDbKey());
+    assertThat(task.getComponentName()).isEqualTo(privateProject.name());
     assertThat(task.getAnalysisId()).isEqualTo(activityDto.getAnalysisUuid());
     assertThat(task.getExecutionTimeMs()).isEqualTo(500L);
     assertThat(task.getLogs()).isFalse();
+    assertThat(task.getWarningCount()).isZero();
+    assertThat(task.getWarningsList()).isEmpty();
   }
 
   @Test
@@ -268,8 +302,39 @@ public class TaskActionTest {
   @Test
   public void get_project_queue_task_with_scan_permission_on_project() {
     UserDto user = db.users().insertUser();
-    userSession.logIn(user).addProjectPermission(GlobalPermissions.SCAN_EXECUTION, project);
-    CeQueueDto task = createAndPersistQueueTask(project, user);
+    userSession.logIn(user).addProjectPermission(GlobalPermissions.SCAN_EXECUTION, privateProject);
+    CeQueueDto task = createAndPersistQueueTask(privateProject, user);
+
+    call(task.getUuid());
+  }
+
+  @Test
+  public void getting_project_queue_task_of_public_project_fails_with_ForbiddenException() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn().registerComponents(publicProject);
+    CeQueueDto task = createAndPersistQueueTask(publicProject, user);
+
+    expectedException.expect(ForbiddenException.class);
+
+    call(task.getUuid());
+  }
+
+  @Test
+  public void get_project_queue_task_of_private_project_with_user_permission_fails_with_ForbiddenException() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn().addProjectPermission(UserRole.USER, privateProject);
+    CeQueueDto task = createAndPersistQueueTask(privateProject, user);
+
+    expectedException.expect(ForbiddenException.class);
+
+    call(task.getUuid());
+  }
+
+  @Test
+  public void get_project_queue_task_on_public_project() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, privateProject);
+    CeQueueDto task = createAndPersistQueueTask(privateProject, user);
 
     call(task.getUuid());
   }
@@ -277,8 +342,8 @@ public class TaskActionTest {
   @Test
   public void get_project_queue_task_with_scan_permission_on_organization_but_not_on_project() {
     UserDto user = db.users().insertUser();
-    userSession.logIn(user).addPermission(SCAN, project.getOrganizationUuid());
-    CeQueueDto task = createAndPersistQueueTask(project, user);
+    userSession.logIn(user).addPermission(SCAN, privateProject.getOrganizationUuid());
+    CeQueueDto task = createAndPersistQueueTask(privateProject, user);
 
     call(task.getUuid());
   }
@@ -287,7 +352,7 @@ public class TaskActionTest {
   public void getting_project_queue_task_throws_ForbiddenException_if_no_admin_nor_scan_permissions() {
     UserDto user = db.users().insertUser();
     userSession.logIn(user);
-    CeQueueDto task = createAndPersistQueueTask(project, user);
+    CeQueueDto task = createAndPersistQueueTask(privateProject, user);
 
     expectedException.expect(ForbiddenException.class);
 
@@ -316,16 +381,26 @@ public class TaskActionTest {
 
   @Test
   public void get_project_archived_task_with_scan_permission_on_project() {
-    userSession.logIn().addProjectPermission(GlobalPermissions.SCAN_EXECUTION, project);
-    CeActivityDto task = createAndPersistArchivedTask(project);
+    userSession.logIn().addProjectPermission(GlobalPermissions.SCAN_EXECUTION, privateProject);
+    CeActivityDto task = createAndPersistArchivedTask(privateProject);
+
+    call(task.getUuid());
+  }
+
+  @Test
+  public void getting_archived_task_of_public_project_fails_with_ForbiddenException() {
+    userSession.logIn().registerComponents(publicProject);
+    CeActivityDto task = createAndPersistArchivedTask(publicProject);
 
+    expectedException.expect(ForbiddenException.class);
+    
     call(task.getUuid());
   }
 
   @Test
   public void get_project_archived_task_with_scan_permission_on_organization_but_not_on_project() {
-    userSession.logIn().addPermission(SCAN, project.getOrganizationUuid());
-    CeActivityDto task = createAndPersistArchivedTask(project);
+    userSession.logIn().addPermission(SCAN, privateProject.getOrganizationUuid());
+    CeActivityDto task = createAndPersistArchivedTask(privateProject);
 
     call(task.getUuid());
   }
@@ -333,7 +408,7 @@ public class TaskActionTest {
   @Test
   public void getting_project_archived_task_throws_ForbiddenException_if_no_admin_nor_scan_permissions() {
     userSession.logIn();
-    CeActivityDto task = createAndPersistArchivedTask(project);
+    CeActivityDto task = createAndPersistArchivedTask(privateProject);
 
     expectedException.expect(ForbiddenException.class);
 
@@ -358,6 +433,67 @@ public class TaskActionTest {
     call(task.getUuid());
   }
 
+  @Test
+  public void get_warnings_on_global_archived_task_requires_to_be_system_administrator() {
+    logInAsSystemAdministrator();
+
+    getWarningsImpl(createAndPersistArchivedTask(null));
+  }
+
+  @Test
+  public void get_warnings_on_public_project_archived_task_if_not_admin_fails_with_ForbiddenException() {
+    userSession.logIn().registerComponents(publicProject);
+
+    expectedException.expect(ForbiddenException.class);
+
+    getWarningsImpl(createAndPersistArchivedTask(publicProject));
+  }
+
+  @Test
+  public void get_warnings_on_private_project_archived_task_if_user_fails_with_ForbiddenException() {
+    userSession.logIn().addProjectPermission(UserRole.USER, privateProject);
+
+    expectedException.expect(ForbiddenException.class);
+
+    getWarningsImpl(createAndPersistArchivedTask(privateProject));
+  }
+
+  @Test
+  public void get_warnings_on_private_project_archived_task_if_scan() {
+    userSession.logIn().addProjectPermission(SCAN_EXECUTION, privateProject);
+
+    getWarningsImpl(createAndPersistArchivedTask(privateProject));
+  }
+
+  @Test
+  public void get_warnings_on_private_project_archived_task_if_scan_on_organization() {
+    userSession.logIn().addPermission(OrganizationPermission.SCAN, organization);
+
+    getWarningsImpl(createAndPersistArchivedTask(privateProject));
+  }
+
+  private void getWarningsImpl(CeActivityDto task) {
+    String[] warnings = IntStream.range(0, 1 + new Random().nextInt(10))
+      .mapToObj(i -> insertWarning(task, i))
+      .map(CeTaskMessageDto::getMessage)
+      .toArray(String[]::new);
+
+    Ce.Task taskWithWarnings = callWithWarnings(task.getUuid());
+    assertThat(taskWithWarnings.getWarningCount()).isEqualTo(warnings.length);
+    assertThat(taskWithWarnings.getWarningsList()).containsExactly(warnings);
+  }
+
+  private CeTaskMessageDto insertWarning(CeActivityDto task, int i) {
+    CeTaskMessageDto res = new CeTaskMessageDto()
+      .setUuid(UuidFactoryFast.getInstance().create())
+      .setTaskUuid(task.getUuid())
+      .setMessage("msg_" + task.getUuid() + "_" + i)
+      .setCreatedAt(task.getUuid().hashCode() + i);
+    db.getDbClient().ceTaskMessageDao().insert(db.getSession(), res);
+    db.getSession().commit();
+    return res;
+  }
+
   private CeActivityDto createAndPersistArchivedTask(@Nullable ComponentDto component) {
     CeQueueDto queueDto = new CeQueueDto();
     queueDto.setTaskType(CeTaskTypes.REPORT);
@@ -374,10 +510,7 @@ public class TaskActionTest {
   }
 
   private CeActivityDto createActivityDto(String uuid) {
-    CeQueueDto queueDto = new CeQueueDto();
-    queueDto.setTaskType(CeTaskTypes.REPORT);
-    queueDto.setUuid(uuid);
-    queueDto.setComponentUuid(project.uuid());
+    CeQueueDto queueDto = createQueueDto(uuid);
     CeActivityDto activityDto = new CeActivityDto(queueDto);
     activityDto.setStatus(CeActivityDto.Status.FAILED);
     activityDto.setExecutionTimeMs(500L);
@@ -385,6 +518,14 @@ public class TaskActionTest {
     return activityDto;
   }
 
+  private CeQueueDto createQueueDto(String uuid) {
+    CeQueueDto queueDto = new CeQueueDto();
+    queueDto.setTaskType(CeTaskTypes.REPORT);
+    queueDto.setUuid(uuid);
+    queueDto.setComponentUuid(privateProject.uuid());
+    return queueDto;
+  }
+
   private CeQueueDto createAndPersistQueueTask(@Nullable ComponentDto component, UserDto user) {
     CeQueueDto dto = new CeQueueDto();
     dto.setTaskType(CeTaskTypes.REPORT);
@@ -449,4 +590,14 @@ public class TaskActionTest {
     assertThat(task.getId()).isEqualTo(taskUuid);
   }
 
+  private Ce.Task callWithWarnings(String taskUuid) {
+    Ce.TaskResponse taskResponse = ws.newRequest()
+      .setParam("id", taskUuid)
+      .setParam("additionalFields", "warnings")
+      .executeProtobuf(Ce.TaskResponse.class);
+    Ce.Task task = taskResponse.getTask();
+    assertThat(task.getId()).isEqualTo(taskUuid);
+    return task;
+  }
+
 }
index bcd8b84e7f1e7edaf2edf4bea5d04d4582ffb324..2ce1c6131b10dfe910025a40499460ba7551c65a 100644 (file)
  */
 package org.sonar.server.ce.ws;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
+import java.util.Random;
+import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
@@ -35,6 +39,8 @@ import org.sonar.db.user.UserDto;
 import org.sonarqube.ws.Ce;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Mockito.mock;
@@ -45,6 +51,10 @@ public class TaskFormatterTest {
 
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private int warningCount = new Random().nextInt(10);
 
   private System2 system2 = mock(System2.class);
   private TaskFormatter underTest = new TaskFormatter(db.getDbClient(), system2);
@@ -163,12 +173,22 @@ public class TaskFormatterTest {
     assertThat(wsTasks).extracting("id").containsExactly("UUID1", "UUID2");
   }
 
+  @Test
+  public void formatActivity_throws_NPE_if_warnings_parameter_is_null() {
+    UserDto user = db.users().insertUser();
+    CeActivityDto dto = newActivity("UUID", "COMPONENT_UUID", CeActivityDto.Status.FAILED, user);
+
+    expectedException.expect(NullPointerException.class);
+
+    underTest.formatActivity(db.getSession(), dto, "foo", null);
+  }
+
   @Test
   public void formatActivity() {
     UserDto user = db.users().insertUser();
     CeActivityDto dto = newActivity("UUID", "COMPONENT_UUID", CeActivityDto.Status.FAILED, user);
 
-    Ce.Task wsTask = underTest.formatActivity(db.getSession(), dto, null);
+    Ce.Task wsTask = underTest.formatActivity(db.getSession(), dto, null, emptyList());
 
     assertThat(wsTask.getType()).isEqualTo(CeTaskTypes.REPORT);
     assertThat(wsTask.getId()).isEqualTo("UUID");
@@ -179,6 +199,8 @@ public class TaskFormatterTest {
     assertThat(wsTask.getAnalysisId()).isEqualTo("U1");
     assertThat(wsTask.getLogs()).isFalse();
     assertThat(wsTask.hasScannerContext()).isFalse();
+    assertThat(wsTask.getWarningCount()).isEqualTo(warningCount);
+    assertThat(wsTask.getWarningsList()).isEmpty();
   }
 
   @Test
@@ -186,12 +208,23 @@ public class TaskFormatterTest {
     CeActivityDto dto = newActivity("UUID", "COMPONENT_UUID", CeActivityDto.Status.FAILED, null);
 
     String expected = "scanner context baby!";
-    Ce.Task wsTask = underTest.formatActivity(db.getSession(), dto, expected);
+    Ce.Task wsTask = underTest.formatActivity(db.getSession(), dto, expected, emptyList());
 
     assertThat(wsTask.hasScannerContext()).isTrue();
     assertThat(wsTask.getScannerContext()).isEqualTo(expected);
   }
 
+  @Test
+  public void formatActivity_set_warnings_list_if_argument_is_non_empty_not_checking_consistency_with_warning_count() {
+    CeActivityDto dto = newActivity("UUID", "COMPONENT_UUID", CeActivityDto.Status.FAILED, null);
+    String[] warnings = IntStream.range(0, warningCount + 1 + new Random().nextInt(5)).mapToObj(i -> "warning_" + i).toArray(String[]::new);
+
+    Ce.Task wsTask = underTest.formatActivity(db.getSession(), dto, null, Arrays.stream(warnings).collect(toList()));
+
+    assertThat(wsTask.getWarningCount()).isEqualTo(warningCount);
+    assertThat(wsTask.getWarningsList()).containsExactly(warnings);
+  }
+
   @Test
   public void formatActivities() {
     UserDto user1 = db.users().insertUser();
@@ -251,9 +284,28 @@ public class TaskFormatterTest {
       .setComponentUuid(componentUuid)
       .setSubmitterUuid(user == null ? null : user.getUuid())
       .setUuid(taskUuid);
-    return new CeActivityDto(queueDto)
+    TestActivityDto testActivityDto = new TestActivityDto(queueDto);
+    testActivityDto.setWarningCount(warningCount);
+    return testActivityDto
       .setStatus(status)
       .setExecutionTimeMs(500L)
       .setAnalysisUuid("U1");
   }
+
+  private class TestActivityDto extends CeActivityDto {
+
+    public TestActivityDto(CeQueueDto queueDto) {
+      super(queueDto);
+    }
+
+    @Override
+    public CeActivityDto setHasScannerContext(boolean hasScannerContext) {
+      return super.setHasScannerContext(hasScannerContext);
+    }
+
+    @Override
+    public CeActivityDto setWarningCount(int warningCount) {
+      return super.setWarningCount(warningCount);
+    }
+  }
 }
index 76d8446fcbfbf1494974dd45810cbba3abfde961..3216ab435e0001c8879cb18aedda4d17a254d5c9 100644 (file)
@@ -105,6 +105,8 @@ message Task {
   optional string errorType = 23;
   optional string pullRequest = 24;
   optional string pullRequestTitle = 25;
+  optional int32 warningCount = 26;
+  repeated string warnings = 27;
 }
 
 enum TaskStatus {