aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueue.java4
-rw-r--r--server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java111
-rw-r--r--server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java83
-rw-r--r--server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java250
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java10
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchLoader.java15
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java54
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java28
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java12
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java128
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java18
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java41
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/ce/task/CeTask.java107
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskComponentTest.java105
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskTest.java54
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListener.java2
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java11
-rw-r--r--server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/CeWorkerImpl.java2
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListenerTest.java11
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java110
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeTaskProcessorRepositoryImplTest.java6
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeWorkerImplTest.java4
-rw-r--r--server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl16
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java13
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java52
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java28
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDto.java58
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskQuery.java20
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/QueueCount.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java4
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml54
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml39
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml16
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java110
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java73
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java111
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDtoTest.java20
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueTesting.java1
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskQueryTest.java28
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java3
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java70
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java105
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java6
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java104
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java135
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java83
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java56
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java56
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java62
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java2
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java233
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java216
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java118
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java67
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql26
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql16
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql30
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql87
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql77
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql38
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/CeModule.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupport.java169
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupportDelegate.java55
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java59
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/ws/ComponentAction.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/ws/SubmitAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDataAction.java65
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchReportSubmitterTest.java301
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchSupportTest.java166
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java79
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java35
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java20
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/ws/SubmitActionTest.java9
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java4
86 files changed, 3825 insertions, 657 deletions
diff --git a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueue.java b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueue.java
index cb6810f5a86..62596377a3f 100644
--- a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueue.java
+++ b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueue.java
@@ -57,7 +57,7 @@ public interface CeQueue {
* <p>
* This method is equivalent to calling {@code massSubmit(Collections.singletonList(submission))}.
*
- * @return empty if {@code options} contains {@link SubmitOption#UNIQUE_QUEUE_PER_COMPONENT UNIQUE_QUEUE_PER_COMPONENT}
+ * @return empty if {@code options} contains {@link SubmitOption#UNIQUE_QUEUE_PER_MAIN_COMPONENT UNIQUE_QUEUE_PER_MAIN_COMPONENT}
* and there's already a queued task, otherwise the created task.
*/
Optional<CeTask> submit(CeTaskSubmit submission, SubmitOption... options);
@@ -102,7 +102,7 @@ public interface CeQueue {
WorkersPauseStatus getWorkersPauseStatus();
enum SubmitOption {
- UNIQUE_QUEUE_PER_COMPONENT
+ UNIQUE_QUEUE_PER_MAIN_COMPONENT
}
enum WorkersPauseStatus {
diff --git a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java
index 940c48eed66..3722ace7b96 100644
--- a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java
+++ b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java
@@ -28,9 +28,11 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.log.Loggers;
@@ -48,8 +50,11 @@ import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.property.InternalProperties;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Collections.emptyMap;
import static java.util.Collections.singleton;
-import static org.sonar.ce.queue.CeQueue.SubmitOption.UNIQUE_QUEUE_PER_COMPONENT;
+import static java.util.Optional.of;
+import static java.util.Optional.ofNullable;
+import static org.sonar.ce.queue.CeQueue.SubmitOption.UNIQUE_QUEUE_PER_MAIN_COMPONENT;
import static org.sonar.core.util.stream.MoreCollectors.toEnumSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.db.ce.CeQueueDto.Status.PENDING;
@@ -78,30 +83,45 @@ public class CeQueueImpl implements CeQueue {
}
@Override
- public java.util.Optional<CeTask> submit(CeTaskSubmit submission, SubmitOption... options) {
+ public Optional<CeTask> submit(CeTaskSubmit submission, SubmitOption... options) {
return submit(submission, toSet(options));
}
- private java.util.Optional<CeTask> submit(CeTaskSubmit submission, EnumSet<SubmitOption> submitOptions) {
+ private Optional<CeTask> submit(CeTaskSubmit submission, EnumSet<SubmitOption> submitOptions) {
try (DbSession dbSession = dbClient.openSession(false)) {
- if (submitOptions.contains(UNIQUE_QUEUE_PER_COMPONENT)
- && submission.getComponentUuid() != null
- && dbClient.ceQueueDao().countByStatusAndComponentUuid(dbSession, PENDING, submission.getComponentUuid()) > 0) {
- return java.util.Optional.empty();
+ if (submitOptions.contains(UNIQUE_QUEUE_PER_MAIN_COMPONENT)
+ && submission.getComponent()
+ .map(component -> dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, PENDING, component.getMainComponentUuid()) > 0)
+ .orElse(false)) {
+ return Optional.empty();
}
CeQueueDto taskDto = addToQueueInDb(dbSession, submission);
dbSession.commit();
- ComponentDto component = null;
- String componentUuid = taskDto.getComponentUuid();
- if (componentUuid != null) {
- component = dbClient.componentDao().selectByUuid(dbSession, componentUuid).orElse(null);
+ Map<String, ComponentDto> componentsByUuid = loadComponentDtos(dbSession, taskDto);
+ if (componentsByUuid.isEmpty()) {
+ return of(convertToTask(taskDto, submission.getCharacteristics(), null, null));
}
- CeTask task = convertToTask(taskDto, submission.getCharacteristics(), component);
- return java.util.Optional.of(task);
+
+ return of(convertToTask(taskDto, submission.getCharacteristics(),
+ ofNullable(taskDto.getComponentUuid()).map(componentsByUuid::get).orElse(null),
+ ofNullable(taskDto.getMainComponentUuid()).map(componentsByUuid::get).orElse(null)));
}
}
+ Map<String, ComponentDto> loadComponentDtos(DbSession dbSession, CeQueueDto taskDto) {
+ Set<String> componentUuids = Stream.of(taskDto.getComponentUuid(), taskDto.getMainComponentUuid())
+ .filter(Objects::nonNull)
+ .collect(MoreCollectors.toSet(2));
+ if (componentUuids.isEmpty()) {
+ return emptyMap();
+ }
+
+ return dbClient.componentDao().selectByUuids(dbSession, componentUuids)
+ .stream()
+ .collect(uniqueIndex(ComponentDto::uuid, 2));
+ }
+
@Override
public List<CeTask> massSubmit(Collection<CeTaskSubmit> submissions, SubmitOption... options) {
if (submissions.isEmpty()) {
@@ -109,11 +129,11 @@ public class CeQueueImpl implements CeQueue {
}
try (DbSession dbSession = dbClient.openSession(false)) {
- List<CeQueueDto> taskDto = submissions.stream()
+ List<CeQueueDto> taskDtos = submissions.stream()
.filter(filterBySubmitOptions(options, submissions, dbSession))
.map(submission -> addToQueueInDb(dbSession, submission))
.collect(Collectors.toList());
- List<CeTask> tasks = loadTasks(dbSession, taskDto);
+ List<CeTask> tasks = loadTasks(dbSession, taskDtos);
dbSession.commit();
return tasks;
}
@@ -122,31 +142,34 @@ public class CeQueueImpl implements CeQueue {
private Predicate<CeTaskSubmit> filterBySubmitOptions(SubmitOption[] options, Collection<CeTaskSubmit> submissions, DbSession dbSession) {
EnumSet<SubmitOption> submitOptions = toSet(options);
- if (submitOptions.contains(UNIQUE_QUEUE_PER_COMPONENT)) {
- Set<String> componentUuids = submissions.stream()
- .map(CeTaskSubmit::getComponentUuid)
- .filter(Objects::nonNull)
+ if (submitOptions.contains(UNIQUE_QUEUE_PER_MAIN_COMPONENT)) {
+ Set<String> mainComponentUuids = submissions.stream()
+ .map(CeTaskSubmit::getComponent)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .map(CeTaskSubmit.Component::getMainComponentUuid)
.collect(MoreCollectors.toSet(submissions.size()));
- if (componentUuids.isEmpty()) {
+ if (mainComponentUuids.isEmpty()) {
return t -> true;
}
- return new NoPendingTaskFilter(dbSession, componentUuids);
+ return new NoPendingTaskFilter(dbSession, mainComponentUuids);
}
return t -> true;
}
private class NoPendingTaskFilter implements Predicate<CeTaskSubmit> {
- private final Map<String, Integer> queuedItemsByComponentUuid;
+ private final Map<String, Integer> queuedItemsByMainComponentUuid;
- private NoPendingTaskFilter(DbSession dbSession, Set<String> componentUuids) {
- queuedItemsByComponentUuid = dbClient.ceQueueDao().countByStatusAndComponentUuids(dbSession, PENDING, componentUuids);
+ private NoPendingTaskFilter(DbSession dbSession, Set<String> projectUuids) {
+ queuedItemsByMainComponentUuid = dbClient.ceQueueDao().countByStatusAndMainComponentUuids(dbSession, PENDING, projectUuids);
}
@Override
public boolean test(CeTaskSubmit ceTaskSubmit) {
- String componentUuid = ceTaskSubmit.getComponentUuid();
- return componentUuid == null || queuedItemsByComponentUuid.getOrDefault(componentUuid, 0) == 0;
+ return ceTaskSubmit.getComponent()
+ .map(component -> queuedItemsByMainComponentUuid.getOrDefault(component.getMainComponentUuid(), 0) == 0)
+ .orElse(true);
}
}
@@ -167,7 +190,9 @@ public class CeQueueImpl implements CeQueue {
CeQueueDto dto = new CeQueueDto();
dto.setUuid(submission.getUuid());
dto.setTaskType(submission.getType());
- dto.setComponentUuid(submission.getComponentUuid());
+ submission.getComponent().ifPresent(component -> dto
+ .setComponentUuid(component.getUuid())
+ .setMainComponentUuid(component.getMainComponentUuid()));
dto.setStatus(PENDING);
dto.setSubmitterUuid(submission.getSubmitterUuid());
dbClient.ceQueueDao().insert(dbSession, dto);
@@ -178,7 +203,7 @@ public class CeQueueImpl implements CeQueue {
private List<CeTask> loadTasks(DbSession dbSession, List<CeQueueDto> dtos) {
// load components, if defined
Set<String> componentUuids = dtos.stream()
- .map(CeQueueDto::getComponentUuid)
+ .flatMap(dto -> Stream.of(dto.getComponentUuid(), dto.getMainComponentUuid()))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Map<String, ComponentDto> componentsByUuid = dbClient.componentDao()
@@ -194,13 +219,15 @@ public class CeQueueImpl implements CeQueue {
List<CeTask> result = new ArrayList<>();
for (CeQueueDto dto : dtos) {
- ComponentDto component = null;
- if (dto.getComponentUuid() != null) {
- component = componentsByUuid.get(dto.getComponentUuid());
- }
+ ComponentDto component = ofNullable(dto.getComponentUuid())
+ .map(componentsByUuid::get)
+ .orElse(null);
+ ComponentDto mainComponent = ofNullable(dto.getMainComponentUuid())
+ .map(componentsByUuid::get)
+ .orElse(null);
Map<String, String> characteristics = characteristicsByTaskUuid.get(dto.getUuid()).stream()
.collect(uniqueIndex(CeTaskCharacteristicDto::getKey, CeTaskCharacteristicDto::getValue));
- result.add(convertToTask(dto, characteristics, component));
+ result.add(convertToTask(dto, characteristics, component, mainComponent));
}
return result;
}
@@ -272,7 +299,7 @@ public class CeQueueImpl implements CeQueue {
@Override
public WorkersPauseStatus getWorkersPauseStatus() {
try (DbSession dbSession = dbClient.openSession(false)) {
- java.util.Optional<String> propValue = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.COMPUTE_ENGINE_PAUSE);
+ Optional<String> propValue = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.COMPUTE_ENGINE_PAUSE);
if (!propValue.isPresent() || !propValue.get().equals("true")) {
return WorkersPauseStatus.RESUMED;
}
@@ -284,18 +311,26 @@ public class CeQueueImpl implements CeQueue {
}
}
- CeTask convertToTask(CeQueueDto taskDto, Map<String, String> characteristics, @Nullable ComponentDto component) {
+ CeTask convertToTask(CeQueueDto taskDto, Map<String, String> characteristics, @Nullable ComponentDto component, @Nullable ComponentDto mainComponent) {
CeTask.Builder builder = new CeTask.Builder()
.setUuid(taskDto.getUuid())
.setType(taskDto.getTaskType())
.setSubmitterUuid(taskDto.getSubmitterUuid())
- .setComponentUuid(taskDto.getComponentUuid())
.setCharacteristics(characteristics);
+ String componentUuid = taskDto.getComponentUuid();
if (component != null) {
+ builder.setComponent(new CeTask.Component(component.uuid(), component.getDbKey(), component.name()));
builder.setOrganizationUuid(component.getOrganizationUuid());
- builder.setComponentKey(component.getDbKey());
- builder.setComponentName(component.name());
+ } else if (componentUuid != null) {
+ builder.setComponent(new CeTask.Component(componentUuid, null, null));
+ }
+
+ String mainComponentUuid = taskDto.getMainComponentUuid();
+ if (mainComponent != null) {
+ builder.setMainComponent(new CeTask.Component(mainComponent.uuid(), mainComponent.getDbKey(), mainComponent.name()));
+ } else if (mainComponentUuid != null) {
+ builder.setMainComponent(new CeTask.Component(mainComponentUuid, null, null));
}
// FIXME this should be set from the CeQueueDto
diff --git a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java
index 7ac4669e990..9aaae90ea81 100644
--- a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java
+++ b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java
@@ -20,11 +20,16 @@
package org.sonar.ce.queue;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
+import org.sonar.db.component.ComponentDto;
+import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.nullToEmpty;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
@@ -33,16 +38,16 @@ public final class CeTaskSubmit {
private final String uuid;
private final String type;
- private final String componentUuid;
+ private final Component component;
private final String submitterUuid;
private final Map<String, String> characteristics;
private CeTaskSubmit(Builder builder) {
- this.uuid = requireNonNull(emptyToNull(builder.uuid));
- this.type = requireNonNull(emptyToNull(builder.type));
- this.componentUuid = emptyToNull(builder.componentUuid);
- this.submitterUuid = emptyToNull(builder.submitterUuid);
- this.characteristics = unmodifiableMap(requireNonNull(builder.characteristics));
+ this.uuid = requireNonNull(builder.uuid);
+ this.type = requireNonNull(builder.type);
+ this.component = builder.component;
+ this.submitterUuid = builder.submitterUuid;
+ this.characteristics = unmodifiableMap(builder.characteristics);
}
public String getType() {
@@ -53,9 +58,8 @@ public final class CeTaskSubmit {
return uuid;
}
- @CheckForNull
- public String getComponentUuid() {
- return componentUuid;
+ public Optional<Component> getComponent() {
+ return Optional.ofNullable(component);
}
@CheckForNull
@@ -70,12 +74,12 @@ public final class CeTaskSubmit {
public static final class Builder {
private final String uuid;
private String type;
- private String componentUuid;
+ private Component component;
private String submitterUuid;
private Map<String, String> characteristics = null;
public Builder(String uuid) {
- this.uuid = uuid;
+ this.uuid = emptyToNull(uuid);
}
public String getUuid() {
@@ -83,12 +87,12 @@ public final class CeTaskSubmit {
}
public Builder setType(String s) {
- this.type = s;
+ this.type = emptyToNull(s);
return this;
}
- public Builder setComponentUuid(@Nullable String s) {
- this.componentUuid = s;
+ public Builder setComponent(@Nullable Component component) {
+ this.component = component;
return this;
}
@@ -103,7 +107,58 @@ public final class CeTaskSubmit {
}
public CeTaskSubmit build() {
+ requireNonNull(uuid, "uuid can't be null");
+ requireNonNull(type, "type can't be null");
+ requireNonNull(characteristics, "characteristics can't be null");
return new CeTaskSubmit(this);
}
}
+
+ public static class Component {
+ private String uuid;
+ private String mainComponentUuid;
+
+ public Component(String uuid, String mainComponentUuid) {
+ this.uuid = requireNonNull(nullToEmpty(uuid), "uuid can't be null");
+ this.mainComponentUuid = requireNonNull(nullToEmpty(mainComponentUuid), "mainComponentUuid can't be null");
+ }
+
+ public static Component fromDto(ComponentDto dto) {
+ String uuid = dto.uuid();
+ return new Component(uuid, firstNonNull(dto.getMainBranchProjectUuid(), uuid));
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String getMainComponentUuid() {
+ return mainComponentUuid;
+ }
+
+ @Override
+ public String toString() {
+ return "Component{" +
+ "uuid='" + uuid + '\'' +
+ ", mainComponentUuid='" + mainComponentUuid + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Component component = (Component) o;
+ return uuid.equals(component.uuid) && mainComponentUuid.equals(component.mainComponentUuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, mainComponentUuid);
+ }
+ }
}
diff --git a/server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java b/server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java
index 9d185fcaf3d..b48c0ff9cc6 100644
--- a/server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java
+++ b/server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java
@@ -30,6 +30,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.ce.queue.CeTaskSubmit.Component;
import org.sonar.ce.task.CeTask;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.UuidFactoryFast;
@@ -49,8 +50,9 @@ import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
import static org.hamcrest.Matchers.startsWith;
-import static org.sonar.ce.queue.CeQueue.SubmitOption.UNIQUE_QUEUE_PER_COMPONENT;
+import static org.sonar.ce.queue.CeQueue.SubmitOption.UNIQUE_QUEUE_PER_MAIN_COMPONENT;
public class CeQueueImplTest {
@@ -72,7 +74,9 @@ public class CeQueueImplTest {
@Test
public void submit_returns_task_populated_from_CeTaskSubmit_and_creates_CeQueue_row() {
- CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "submitter uuid");
+ String componentUuid = randomAlphabetic(3);
+ String mainComponentUuid = randomAlphabetic(4);
+ CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, new Component(componentUuid, mainComponentUuid), "submitter uuid");
CeTask task = underTest.submit(taskSubmit);
@@ -83,7 +87,7 @@ public class CeQueueImplTest {
@Test
public void submit_populates_component_name_and_key_of_CeTask_if_component_exists() {
ComponentDto componentDto = insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert(), "PROJECT_1"));
- CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, componentDto.uuid(), null);
+ CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, Component.fromDto(componentDto), null);
CeTask task = underTest.submit(taskSubmit);
@@ -100,11 +104,11 @@ public class CeQueueImplTest {
}
@Test
- public void submit_with_UNIQUE_QUEUE_PER_COMPONENT_creates_task_without_component_when_there_is_a_pending_task_without_component() {
+ public void submit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_without_component_when_there_is_a_pending_task_without_component() {
CeTaskSubmit taskSubmit = createTaskSubmit("no_component");
CeQueueDto dto = insertPendingInQueue(null);
- Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT);
+ Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(task).isNotEmpty();
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -113,13 +117,13 @@ public class CeQueueImplTest {
}
@Test
- public void submit_with_UNIQUE_QUEUE_PER_COMPONENT_creates_task_when_there_is_a_pending_task_for_another_component() {
- String componentUuid = randomAlphabetic(5);
- String otherComponentUuid = randomAlphabetic(6);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
- CeQueueDto dto = insertPendingInQueue(otherComponentUuid);
+ public void submit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_when_there_is_a_pending_task_for_another_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ String otherMainComponentUuid = randomAlphabetic(6);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
+ CeQueueDto dto = insertPendingInQueue(newComponent(otherMainComponentUuid));
- Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT);
+ Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(task).isNotEmpty();
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -128,12 +132,12 @@ public class CeQueueImplTest {
}
@Test
- public void submit_with_UNIQUE_QUEUE_PER_COMPONENT_does_not_create_task_when_there_is_one_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
- CeQueueDto dto = insertPendingInQueue(componentUuid);
+ public void submit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_does_not_create_task_when_there_is_one_pending_task_for_same_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
+ CeQueueDto dto = insertPendingInQueue(newComponent(mainComponentUuid));
- Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT);
+ Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(task).isEmpty();
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -142,15 +146,15 @@ public class CeQueueImplTest {
}
@Test
- public void submit_with_UNIQUE_QUEUE_PER_COMPONENT_does_not_create_task_when_there_is_many_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
+ public void submit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_does_not_create_task_when_there_is_many_pending_task_for_same_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
String[] uuids = IntStream.range(0, 2 + new Random().nextInt(5))
- .mapToObj(i -> insertPendingInQueue(componentUuid))
+ .mapToObj(i -> insertPendingInQueue(newComponent(mainComponentUuid)))
.map(CeQueueDto::getUuid)
.toArray(String[]::new);
- Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT);
+ Optional<CeTask> task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(task).isEmpty();
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -159,10 +163,10 @@ public class CeQueueImplTest {
}
@Test
- public void submit_without_UNIQUE_QUEUE_PER_COMPONENT_creates_task_when_there_is_one_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
- CeQueueDto dto = insertPendingInQueue(componentUuid);
+ public void submit_without_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_when_there_is_one_pending_task_for_same_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
+ CeQueueDto dto = insertPendingInQueue(newComponent(mainComponentUuid));
CeTask task = underTest.submit(taskSubmit);
@@ -172,11 +176,11 @@ public class CeQueueImplTest {
}
@Test
- public void submit_without_UNIQUE_QUEUE_PER_COMPONENT_creates_task_when_there_is_many_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
+ public void submit_without_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_when_there_is_many_pending_task_for_same_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
String[] uuids = IntStream.range(0, 2 + new Random().nextInt(5))
- .mapToObj(i -> insertPendingInQueue(componentUuid))
+ .mapToObj(i -> insertPendingInQueue(newComponent(mainComponentUuid)))
.map(CeQueueDto::getUuid)
.toArray(String[]::new);
@@ -191,7 +195,8 @@ public class CeQueueImplTest {
@Test
public void massSubmit_returns_tasks_for_each_CeTaskSubmit_populated_from_CeTaskSubmit_and_creates_CeQueue_row_for_each() {
- CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "submitter uuid");
+ String mainComponentUuid = randomAlphabetic(10);
+ CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, newComponent(mainComponentUuid), "submitter uuid");
CeTaskSubmit taskSubmit2 = createTaskSubmit("some type");
List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
@@ -204,10 +209,10 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_populates_component_name_and_key_of_CeTask_if_component_exists() {
+ public void massSubmit_populates_component_name_and_key_of_CeTask_if_project_exists() {
ComponentDto componentDto1 = insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "PROJECT_1"));
- CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, componentDto1.uuid(), null);
- CeTaskSubmit taskSubmit2 = createTaskSubmit("something", "non existing component uuid", null);
+ CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, Component.fromDto(componentDto1), null);
+ CeTaskSubmit taskSubmit2 = createTaskSubmit("something", newComponent(randomAlphabetic(12)), null);
List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
@@ -217,11 +222,26 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_with_UNIQUE_QUEUE_PER_COMPONENT_creates_task_without_component_when_there_is_a_pending_task_without_component() {
+ public void massSubmit_populates_component_name_and_key_of_CeTask_if_project_and_branch_exists() {
+ ComponentDto project = insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "PROJECT_1"));
+ ComponentDto branch1 = db.components().insertProjectBranch(project);
+ ComponentDto branch2 = db.components().insertProjectBranch(project);
+ CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, Component.fromDto(branch1), null);
+ CeTaskSubmit taskSubmit2 = createTaskSubmit("something", Component.fromDto(branch2), null);
+
+ List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
+
+ assertThat(tasks).hasSize(2);
+ verifyCeTask(taskSubmit1, tasks.get(0), branch1, project);
+ verifyCeTask(taskSubmit2, tasks.get(1), branch2, project);
+ }
+
+ @Test
+ public void massSubmit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_without_component_when_there_is_a_pending_task_without_component() {
CeTaskSubmit taskSubmit = createTaskSubmit("no_component");
CeQueueDto dto = insertPendingInQueue(null);
- List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT);
+ List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(tasks).hasSize(1);
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -230,13 +250,13 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_with_UNIQUE_QUEUE_PER_COMPONENT_creates_task_when_there_is_a_pending_task_for_another_component() {
- String componentUuid = randomAlphabetic(5);
- String otherComponentUuid = randomAlphabetic(6);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
- CeQueueDto dto = insertPendingInQueue(otherComponentUuid);
+ public void massSubmit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_when_there_is_a_pending_task_for_another_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ String otherMainComponentUuid = randomAlphabetic(6);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
+ CeQueueDto dto = insertPendingInQueue(newComponent(otherMainComponentUuid));
- List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT);
+ List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(tasks).hasSize(1);
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -245,12 +265,12 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_with_UNIQUE_QUEUE_PER_COMPONENT_does_not_create_task_when_there_is_one_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
- CeQueueDto dto = insertPendingInQueue(componentUuid);
+ public void massSubmit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_does_not_create_task_when_there_is_one_pending_task_for_same_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
+ CeQueueDto dto = insertPendingInQueue(newComponent(mainComponentUuid));
- List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT);
+ List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(tasks).isEmpty();
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -259,15 +279,15 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_with_UNIQUE_QUEUE_PER_COMPONENT_does_not_create_task_when_there_is_many_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
+ public void massSubmit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_does_not_create_task_when_there_is_many_pending_task_for_same_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
String[] uuids = IntStream.range(0, 2 + new Random().nextInt(5))
- .mapToObj(i -> insertPendingInQueue(componentUuid))
+ .mapToObj(i -> insertPendingInQueue(newComponent(mainComponentUuid)))
.map(CeQueueDto::getUuid)
.toArray(String[]::new);
- List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT);
+ List<CeTask> tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(tasks).isEmpty();
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
@@ -276,10 +296,10 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_without_UNIQUE_QUEUE_PER_COMPONENT_creates_task_when_there_is_one_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
- CeQueueDto dto = insertPendingInQueue(componentUuid);
+ public void massSubmit_without_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_when_there_is_one_pending_task_for_other_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
+ CeQueueDto dto = insertPendingInQueue(newComponent(mainComponentUuid));
List<CeTask> tasks = underTest.massSubmit(of(taskSubmit));
@@ -290,11 +310,11 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_without_UNIQUE_QUEUE_PER_COMPONENT_creates_task_when_there_is_many_pending_task_for_component() {
- String componentUuid = randomAlphabetic(5);
- CeTaskSubmit taskSubmit = createTaskSubmit("with_component", componentUuid, null);
+ public void massSubmit_without_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_task_when_there_is_many_pending_task_for_other_main_component() {
+ String mainComponentUuid = randomAlphabetic(5);
+ CeTaskSubmit taskSubmit = createTaskSubmit("with_component", newComponent(mainComponentUuid), null);
String[] uuids = IntStream.range(0, 2 + new Random().nextInt(5))
- .mapToObj(i -> insertPendingInQueue(componentUuid))
+ .mapToObj(i -> insertPendingInQueue(newComponent(mainComponentUuid)))
.map(CeQueueDto::getUuid)
.toArray(String[]::new);
@@ -309,30 +329,33 @@ public class CeQueueImplTest {
}
@Test
- public void massSubmit_with_UNIQUE_QUEUE_PER_COMPONENT_creates_tasks_depending_on_whether_there_is_pending_task_for_component() {
- String componentUuid1 = randomAlphabetic(5);
- String componentUuid2 = randomAlphabetic(6);
- String componentUuid3 = randomAlphabetic(7);
- String componentUuid4 = randomAlphabetic(8);
- String componentUuid5 = randomAlphabetic(9);
- CeTaskSubmit taskSubmit1 = createTaskSubmit("with_one_pending", componentUuid1, null);
- CeQueueDto dto1 = insertPendingInQueue(componentUuid1);
- CeTaskSubmit taskSubmit2 = createTaskSubmit("no_pending", componentUuid2, null);
- CeTaskSubmit taskSubmit3 = createTaskSubmit("with_many_pending", componentUuid3, null);
+ public void massSubmit_with_UNIQUE_QUEUE_PER_MAIN_COMPONENT_creates_tasks_depending_on_whether_there_is_pending_task_for_same_main_component() {
+ String mainComponentUuid1 = randomAlphabetic(5);
+ String mainComponentUuid2 = randomAlphabetic(6);
+ String mainComponentUuid3 = randomAlphabetic(7);
+ String mainComponentUuid4 = randomAlphabetic(8);
+ String mainComponentUuid5 = randomAlphabetic(9);
+ CeTaskSubmit taskSubmit1 = createTaskSubmit("with_one_pending", newComponent(mainComponentUuid1), null);
+ CeQueueDto dto1 = insertPendingInQueue(newComponent(mainComponentUuid1));
+ Component componentForMainComponentUuid2 = newComponent(mainComponentUuid2);
+ CeTaskSubmit taskSubmit2 = createTaskSubmit("no_pending", componentForMainComponentUuid2, null);
+ CeTaskSubmit taskSubmit3 = createTaskSubmit("with_many_pending", newComponent(mainComponentUuid3), null);
String[] uuids3 = IntStream.range(0, 2 + new Random().nextInt(5))
- .mapToObj(i -> insertPendingInQueue(componentUuid3))
+ .mapToObj(i -> insertPendingInQueue(newComponent(mainComponentUuid3)))
.map(CeQueueDto::getUuid)
.toArray(String[]::new);
- CeTaskSubmit taskSubmit4 = createTaskSubmit("no_pending_2", componentUuid4, null);
- CeTaskSubmit taskSubmit5 = createTaskSubmit("with_pending_2", componentUuid5, null);
- CeQueueDto dto5 = insertPendingInQueue(componentUuid5);
+ Component componentForMainComponentUuid4 = newComponent(mainComponentUuid4);
+ CeTaskSubmit taskSubmit4 = createTaskSubmit("no_pending_2", componentForMainComponentUuid4, null);
+ CeTaskSubmit taskSubmit5 = createTaskSubmit("with_pending_2", newComponent(mainComponentUuid5), null);
+ CeQueueDto dto5 = insertPendingInQueue(newComponent(mainComponentUuid5));
- List<CeTask> tasks = underTest.massSubmit(of(taskSubmit1, taskSubmit2, taskSubmit3, taskSubmit4, taskSubmit5), UNIQUE_QUEUE_PER_COMPONENT);
+ List<CeTask> tasks = underTest.massSubmit(of(taskSubmit1, taskSubmit2, taskSubmit3, taskSubmit4, taskSubmit5), UNIQUE_QUEUE_PER_MAIN_COMPONENT);
assertThat(tasks)
.hasSize(2)
- .extracting(CeTask::getComponentUuid)
- .containsOnly(componentUuid2, componentUuid4);
+ .extracting(task -> task.getComponent().get().getUuid(),task -> task.getMainComponent().get().getUuid())
+ .containsOnly(tuple(componentForMainComponentUuid2.getUuid(), componentForMainComponentUuid2.getMainComponentUuid()),
+ tuple(componentForMainComponentUuid4.getUuid(), componentForMainComponentUuid4.getMainComponentUuid()));
assertThat(db.getDbClient().ceQueueDao().selectAllInAscOrder(db.getSession()))
.extracting(CeQueueDto::getUuid)
.hasSize(1 + uuids3.length + 1 + tasks.size())
@@ -344,7 +367,7 @@ public class CeQueueImplTest {
@Test
public void cancel_pending() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
CeQueueDto queueDto = db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), task.getUuid()).get();
underTest.cancel(db.getSession(), queueDto);
@@ -356,7 +379,7 @@ public class CeQueueImplTest {
@Test
public void fail_to_cancel_if_in_progress() {
- submit(CeTaskTypes.REPORT, "PROJECT_1");
+ submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(11)));
CeQueueDto ceQueueDto = db.getDbClient().ceQueueDao().peek(session, WORKER_UUID).get();
expectedException.expect(IllegalStateException.class);
@@ -367,9 +390,9 @@ public class CeQueueImplTest {
@Test
public void cancelAll_pendings_but_not_in_progress() {
- CeTask inProgressTask = submit(CeTaskTypes.REPORT, "PROJECT_1");
- CeTask pendingTask1 = submit(CeTaskTypes.REPORT, "PROJECT_2");
- CeTask pendingTask2 = submit(CeTaskTypes.REPORT, "PROJECT_3");
+ CeTask inProgressTask = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
+ CeTask pendingTask1 = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(13)));
+ CeTask pendingTask2 = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(14)));
db.getDbClient().ceQueueDao().peek(session, WORKER_UUID);
@@ -386,7 +409,7 @@ public class CeQueueImplTest {
@Test
public void pauseWorkers_marks_workers_as_paused_if_zero_tasks_in_progress() {
- submit(CeTaskTypes.REPORT, "PROJECT_1");
+ submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
// task is pending
assertThat(underTest.getWorkersPauseStatus()).isEqualTo(CeQueue.WorkersPauseStatus.RESUMED);
@@ -397,7 +420,7 @@ public class CeQueueImplTest {
@Test
public void pauseWorkers_marks_workers_as_pausing_if_some_tasks_in_progress() {
- submit(CeTaskTypes.REPORT, "PROJECT_1");
+ submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
db.getDbClient().ceQueueDao().peek(session, WORKER_UUID);
// task is in-progress
@@ -418,7 +441,7 @@ public class CeQueueImplTest {
@Test
public void resumeWorkers_resumes_pausing_workers() {
- submit(CeTaskTypes.REPORT, "PROJECT_1");
+ submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
db.getDbClient().ceQueueDao().peek(session, WORKER_UUID);
// task is in-progress
@@ -439,21 +462,37 @@ public class CeQueueImplTest {
}
private void verifyCeTask(CeTaskSubmit taskSubmit, CeTask task, @Nullable ComponentDto componentDto) {
+ verifyCeTask(taskSubmit, task, componentDto, componentDto);
+ }
+
+ private void verifyCeTask(CeTaskSubmit taskSubmit, CeTask task, @Nullable ComponentDto componentDto, @Nullable ComponentDto mainComponentDto) {
if (componentDto == null) {
assertThat(task.getOrganizationUuid()).isEqualTo(defaultOrganizationProvider.get().getUuid());
} else {
assertThat(task.getOrganizationUuid()).isEqualTo(componentDto.getOrganizationUuid());
}
assertThat(task.getUuid()).isEqualTo(taskSubmit.getUuid());
- assertThat(task.getComponentUuid()).isEqualTo(task.getComponentUuid());
- assertThat(task.getType()).isEqualTo(taskSubmit.getType());
- if (componentDto == null) {
- assertThat(task.getComponentKey()).isNull();
- assertThat(task.getComponentName()).isNull();
+ if (componentDto != null) {
+ CeTask.Component component = task.getComponent().get();
+ assertThat(component.getUuid()).isEqualTo(componentDto.uuid());
+ assertThat(component.getKey()).contains(componentDto.getDbKey());
+ assertThat(component.getName()).contains(componentDto.name());
+ } else if (taskSubmit.getComponent().isPresent()) {
+ assertThat(task.getComponent()).contains(new CeTask.Component(taskSubmit.getComponent().get().getUuid(), null, null));
+ } else {
+ assertThat(task.getComponent()).isEmpty();
+ }
+ if (mainComponentDto != null) {
+ CeTask.Component component = task.getMainComponent().get();
+ assertThat(component.getUuid()).isEqualTo(mainComponentDto.uuid());
+ assertThat(component.getKey()).contains(mainComponentDto.getDbKey());
+ assertThat(component.getName()).contains(mainComponentDto.name());
+ } else if (taskSubmit.getComponent().isPresent()) {
+ assertThat(task.getMainComponent()).contains(new CeTask.Component(taskSubmit.getComponent().get().getMainComponentUuid(), null, null));
} else {
- assertThat(task.getComponentKey()).isEqualTo(componentDto.getDbKey());
- assertThat(task.getComponentName()).isEqualTo(componentDto.name());
+ assertThat(task.getMainComponent()).isEmpty();
}
+ assertThat(task.getType()).isEqualTo(taskSubmit.getType());
assertThat(task.getSubmitterUuid()).isEqualTo(taskSubmit.getSubmitterUuid());
}
@@ -461,23 +500,30 @@ public class CeQueueImplTest {
Optional<CeQueueDto> queueDto = db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), taskSubmit.getUuid());
assertThat(queueDto.isPresent()).isTrue();
assertThat(queueDto.get().getTaskType()).isEqualTo(taskSubmit.getType());
- assertThat(queueDto.get().getComponentUuid()).isEqualTo(taskSubmit.getComponentUuid());
+ Optional<Component> component = taskSubmit.getComponent();
+ if (component.isPresent()) {
+ assertThat(queueDto.get().getComponentUuid()).isEqualTo(component.get().getUuid());
+ assertThat(queueDto.get().getMainComponentUuid()).isEqualTo(component.get().getMainComponentUuid());
+ } else {
+ assertThat(queueDto.get().getComponentUuid()).isNull();
+ assertThat(queueDto.get().getComponentUuid()).isNull();
+ }
assertThat(queueDto.get().getSubmitterUuid()).isEqualTo(taskSubmit.getSubmitterUuid());
assertThat(queueDto.get().getCreatedAt()).isEqualTo(1_450_000_000_000L);
}
- private CeTask submit(String reportType, String componentUuid) {
- return underTest.submit(createTaskSubmit(reportType, componentUuid, null));
+ private CeTask submit(String reportType, Component component) {
+ return underTest.submit(createTaskSubmit(reportType, component, null));
}
private CeTaskSubmit createTaskSubmit(String type) {
return createTaskSubmit(type, null, null);
}
- private CeTaskSubmit createTaskSubmit(String type, @Nullable String componentUuid, @Nullable String submitterUuid) {
+ private CeTaskSubmit createTaskSubmit(String type, @Nullable Component component, @Nullable String submitterUuid) {
return underTest.prepareSubmit()
.setType(type)
- .setComponentUuid(componentUuid)
+ .setComponent(component)
.setSubmitterUuid(submitterUuid)
.setCharacteristics(emptyMap())
.build();
@@ -489,14 +535,22 @@ public class CeQueueImplTest {
return componentDto;
}
- private CeQueueDto insertPendingInQueue(@Nullable String componentUuid) {
+ private CeQueueDto insertPendingInQueue(@Nullable Component component) {
CeQueueDto dto = new CeQueueDto()
.setUuid(UuidFactoryFast.getInstance().create())
.setTaskType("some type")
- .setComponentUuid(componentUuid)
.setStatus(CeQueueDto.Status.PENDING);
+ if (component != null) {
+ dto.setComponentUuid(component.getUuid())
+ .setMainComponentUuid(component.getMainComponentUuid());
+ }
db.getDbClient().ceQueueDao().insert(db.getSession(), dto);
db.commit();
return dto;
}
+
+ private static int newComponentIdGenerator = new Random().nextInt(8_999_333);
+ private static Component newComponent(String mainComponentUuid) {
+ return new Component("uuid_" + newComponentIdGenerator++, mainComponentUuid);
+ }
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
index 3c6f89c7c85..fbd3db946d2 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
@@ -148,10 +148,12 @@ public class PostProjectAnalysisTasksExecutor implements ComputationStepExecutor
}
private static Project createProject(org.sonar.ce.task.CeTask ceTask) {
- return new ProjectImpl(
- ceTask.getComponentUuid(),
- ceTask.getComponentKey(),
- ceTask.getComponentName());
+ return ceTask.getMainComponent()
+ .map(c -> new ProjectImpl(
+ c.getUuid(),
+ c.getKey().orElseThrow(() -> new IllegalStateException("Missing project key")),
+ c.getName().orElseThrow(() -> new IllegalStateException("Missing project name"))))
+ .orElseThrow(() -> new IllegalStateException("Report processed for a task of a deleted component"));
}
@CheckForNull
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchLoader.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchLoader.java
index 9341e29a09d..e6629463e6c 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchLoader.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchLoader.java
@@ -21,10 +21,11 @@ package org.sonar.ce.task.projectanalysis.component;
import javax.annotation.Nullable;
import org.sonar.api.utils.MessageException;
-import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
+import org.sonar.scanner.protocol.output.ScannerReport;
import static org.apache.commons.lang.StringUtils.trimToNull;
+import static org.sonar.scanner.protocol.output.ScannerReport.Metadata.BranchType.UNSET;
public class BranchLoader {
private final MutableAnalysisMetadataHolder metadataHolder;
@@ -47,10 +48,22 @@ public class BranchLoader {
throw MessageException.of("Properties sonar.branch and sonar.branch.name can't be set together");
}
+ if (delegate == null && hasBranchProperties(metadata)) {
+ throw MessageException.of("Current edition does not support branch feature");
+ }
+
if (delegate != null && deprecatedBranch == null) {
delegate.load(metadata);
} else {
metadataHolder.setBranch(new DefaultBranchImpl(deprecatedBranch));
}
}
+
+ private static boolean hasBranchProperties(ScannerReport.Metadata metadata) {
+ return !metadata.getBranchName().isEmpty()
+ || !metadata.getPullRequestKey().isEmpty()
+ || !metadata.getMergeBranchName().isEmpty()
+ || metadata.getBranchType() != UNSET;
+ }
+
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java
index 8af513738c0..6efa7d0d9a1 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java
@@ -19,10 +19,7 @@
*/
package org.sonar.ce.task.projectanalysis.component;
-import java.util.Date;
-import java.util.Optional;
import javax.annotation.Nullable;
-import org.sonar.api.utils.System2;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
import org.sonar.db.DbClient;
@@ -32,18 +29,16 @@ import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.protobuf.DbProjectBranches;
-import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
-import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
-
+/**
+ * Creates or updates the data in table {@code PROJECT_BRANCHES} for the current root.
+ */
public class BranchPersisterImpl implements BranchPersister {
private final DbClient dbClient;
- private final System2 system2;
private final TreeRootHolder treeRootHolder;
private final AnalysisMetadataHolder analysisMetadataHolder;
- public BranchPersisterImpl(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, AnalysisMetadataHolder analysisMetadataHolder) {
+ public BranchPersisterImpl(DbClient dbClient, TreeRootHolder treeRootHolder, AnalysisMetadataHolder analysisMetadataHolder) {
this.dbClient = dbClient;
- this.system2 = system2;
this.treeRootHolder = treeRootHolder;
this.analysisMetadataHolder = analysisMetadataHolder;
}
@@ -52,32 +47,14 @@ public class BranchPersisterImpl implements BranchPersister {
Branch branch = analysisMetadataHolder.getBranch();
String branchUuid = treeRootHolder.getRoot().getUuid();
- Optional<ComponentDto> branchComponentDtoOpt = dbClient.componentDao().selectByUuid(dbSession, branchUuid);
-
- ComponentDto branchComponentDto;
- if (branch.isMain()) {
- checkState(branchComponentDtoOpt.isPresent(), "Project has been deleted by end-user during analysis");
- branchComponentDto = branchComponentDtoOpt.get();
- } else {
- // inserts new row in table projects if it's the first time branch is analyzed
- branchComponentDto = branchComponentDtoOpt.orElseGet(() -> insertIntoProjectsTable(dbSession, branchUuid));
- }
+ ComponentDto branchComponentDto = dbClient.componentDao().selectByUuid(dbSession, branchUuid)
+ .orElseThrow(() -> new IllegalStateException("Component has been deleted by end-user during analysis"));
// insert or update in table project_branches
dbClient.branchDao().upsert(dbSession, toBranchDto(branchComponentDto, branch));
}
- private static void checkState(boolean condition, String msg) {
- if (!condition) {
- throw new IllegalStateException(msg);
- }
- }
-
- private static <T> T firstNonNull(@Nullable T first, T second) {
- return (first != null) ? first : second;
- }
-
- private BranchDto toBranchDto(ComponentDto componentDto, Branch branch) {
+ protected BranchDto toBranchDto(ComponentDto componentDto, Branch branch) {
BranchDto dto = new BranchDto();
dto.setUuid(componentDto.uuid());
@@ -103,19 +80,8 @@ public class BranchPersisterImpl implements BranchPersister {
return dto;
}
- private ComponentDto insertIntoProjectsTable(DbSession dbSession, String branchUuid) {
- String mainBranchProjectUuid = analysisMetadataHolder.getProject().getUuid();
- ComponentDto project = dbClient.componentDao().selectOrFailByUuid(dbSession, mainBranchProjectUuid);
- ComponentDto branchDto = project.copy();
- branchDto.setUuid(branchUuid);
- branchDto.setProjectUuid(branchUuid);
- branchDto.setRootUuid(branchUuid);
- branchDto.setUuidPath(UUID_PATH_OF_ROOT);
- branchDto.setModuleUuidPath(UUID_PATH_SEPARATOR + branchUuid + UUID_PATH_SEPARATOR);
- branchDto.setMainBranchProjectUuid(mainBranchProjectUuid);
- branchDto.setDbKey(treeRootHolder.getRoot().getDbKey());
- branchDto.setCreatedAt(new Date(system2.now()));
- dbClient.componentDao().insert(dbSession, branchDto);
- return branchDto;
+ private static <T> T firstNonNull(@Nullable T first, T second) {
+ return (first != null) ? first : second;
}
+
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java
index 6de4c30557a..80963133fc5 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java
@@ -41,18 +41,19 @@ public class PopulateFileSourceLineCount extends DataChange implements ProjectAn
@Override
protected void execute(Context context) throws SQLException {
+ String componentUuid = ceTask.getComponent().get().getUuid();
Long unInitializedFileSources = context.prepareSelect("select count(1) from file_sources where line_count = ? and project_uuid = ?")
.setInt(1, LINE_COUNT_NOT_POPULATED)
- .setString(2, ceTask.getComponentUuid())
+ .setString(2, componentUuid)
.get(row -> row.getLong(1));
if (unInitializedFileSources != null && unInitializedFileSources > 0) {
MassUpdate massUpdate = context.prepareMassUpdate();
massUpdate.select("select id,line_hashes from file_sources where line_count = ? and project_uuid = ?")
.setInt(1, LINE_COUNT_NOT_POPULATED)
- .setString(2, ceTask.getComponentUuid());
+ .setString(2, componentUuid);
massUpdate.update("update file_sources set line_count = ? where id = ?");
- massUpdate.rowPluralName("line counts of sources of project " + ceTask.getComponentUuid());
+ massUpdate.rowPluralName("line counts of sources of project " + componentUuid);
massUpdate.execute(PopulateFileSourceLineCount::handle);
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
index 9a0c3b1592e..99709304ebd 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
@@ -104,30 +104,40 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
*/
private Runnable loadProject(ScannerReport.Metadata reportMetadata, Organization organization) {
String reportProjectKey = projectKeyFromReport(reportMetadata);
- String componentKey = ceTask.getComponentKey();
- if (componentKey == null) {
- throw MessageException.of(format(
+ CeTask.Component mainComponent = mandatoryComponent(ceTask.getMainComponent());
+ String mainComponentKey = mainComponent.getKey()
+ .orElseThrow(() -> MessageException.of(format(
+ "Compute Engine task main component key is null. Project with UUID %s must have been deleted since report was uploaded. Can not proceed.",
+ mainComponent.getUuid())));
+ CeTask.Component component = mandatoryComponent(ceTask.getComponent());
+ String componentKey = component.getKey()
+ .orElseThrow(() -> MessageException.of(format(
"Compute Engine task component key is null. Project with UUID %s must have been deleted since report was uploaded. Can not proceed.",
- ceTask.getComponentUuid()));
- }
+ component.getUuid())));
ComponentDto dto = toProject(reportProjectKey);
+
analysisMetadata.setProject(Project.from(dto));
return () -> {
- if (!componentKey.equals(reportProjectKey)) {
+ if (!mainComponentKey.equals(reportProjectKey)) {
throw MessageException.of(format(
"ProjectKey in report (%s) is not consistent with projectKey under which the report has been submitted (%s)",
reportProjectKey,
- componentKey));
+ mainComponentKey));
}
if (!dto.getOrganizationUuid().equals(organization.getUuid())) {
throw MessageException.of(format("Project is not in the expected organization: %s", organization.getKey()));
}
- if (dto.getMainBranchProjectUuid() != null) {
- throw MessageException.of("Project should not reference a branch");
+ if (componentKey.equals(mainComponentKey) && dto.getMainBranchProjectUuid() != null) {
+ throw MessageException.of("Component should not reference a branch");
}
};
}
+ private static CeTask.Component mandatoryComponent(Optional<CeTask.Component> mainComponent) {
+ return mainComponent
+ .orElseThrow(() -> new IllegalStateException("component missing on ce task"));
+ }
+
private Organization loadOrganization(ScannerReport.Metadata reportMetadata) {
try (DbSession dbSession = dbClient.openSession(false)) {
Organization organization = toOrganization(dbSession, ceTask.getOrganizationUuid());
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java
index fabd35b9dbc..af0eafd471e 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java
@@ -87,13 +87,13 @@ public class PostProjectAnalysisTasksExecutorTest {
private String organizationName = organizationUuid + "_name";
private System2 system2 = mock(System2.class);
private ArgumentCaptor<PostProjectAnalysisTask.ProjectAnalysis> projectAnalysisArgumentCaptor = ArgumentCaptor.forClass(PostProjectAnalysisTask.ProjectAnalysis.class);
+ private CeTask.Component component = new CeTask.Component("component uuid", "component key", "component name");
private CeTask ceTask = new CeTask.Builder()
.setOrganizationUuid(organizationUuid)
.setType("type")
.setUuid("uuid")
- .setComponentKey("component key")
- .setComponentName("component name")
- .setComponentUuid("component uuid")
+ .setComponent(component)
+ .setMainComponent(component)
.build();
private PostProjectAnalysisTask postProjectAnalysisTask = mock(PostProjectAnalysisTask.class);
private PostProjectAnalysisTasksExecutor underTest = new PostProjectAnalysisTasksExecutor(
@@ -203,9 +203,9 @@ public class PostProjectAnalysisTasksExecutorTest {
verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture());
Project project = projectAnalysisArgumentCaptor.getValue().getProject();
- assertThat(project.getUuid()).isEqualTo(ceTask.getComponentUuid());
- assertThat(project.getKey()).isEqualTo(ceTask.getComponentKey());
- assertThat(project.getName()).isEqualTo(ceTask.getComponentName());
+ assertThat(project.getUuid()).isEqualTo(ceTask.getComponent().get().getUuid());
+ assertThat(project.getKey()).isEqualTo(ceTask.getComponent().get().getKey().get());
+ assertThat(project.getName()).isEqualTo(ceTask.getComponent().get().getName().get());
}
@Test
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java
index 237b7b3efc1..7cfcbd5c116 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java
@@ -19,25 +19,36 @@
*/
package org.sonar.ce.task.projectanalysis.component;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Optional;
+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.utils.System2;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.protobuf.DbProjectBranches;
import org.sonar.server.project.Project;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.db.component.BranchType.LONG;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+@RunWith(DataProviderRunner.class)
public class BranchPersisterImplTest {
private final static Component MAIN = builder(PROJECT, 1).setUuid("PROJECT_UUID").setKey("PROJECT_KEY").build();
private final static Component BRANCH = builder(PROJECT, 1).setUuid("BRANCH_UUID").setKey("BRANCH_KEY").build();
@@ -51,66 +62,133 @@ public class BranchPersisterImplTest {
@Rule
public ExpectedException exception = ExpectedException.none();
- BranchPersister underTest = new BranchPersisterImpl(dbTester.getDbClient(), System2.INSTANCE, treeRootHolder, analysisMetadataHolder);
+ BranchPersister underTest = new BranchPersisterImpl(dbTester.getDbClient(), treeRootHolder, analysisMetadataHolder);
@Test
- public void fail_if_no_component_for_main_branches() {
- analysisMetadataHolder.setBranch(createBranch(BranchType.LONG, true, "master"));
+ public void persist_fails_with_ISE_if_no_component_for_main_branches() {
+ analysisMetadataHolder.setBranch(createBranch(LONG, true, "master"));
treeRootHolder.setRoot(MAIN);
- exception.expect(IllegalStateException.class);
- exception.expectMessage("Project has been deleted by end-user during analysis");
+ expectMissingComponentISE();
+
+ underTest.persist(dbTester.getSession());
+ }
+
+ @Test
+ public void persist_fails_with_ISE_if_no_component_for_long_branches() {
+ analysisMetadataHolder.setBranch(createBranch(LONG, false, "foo"));
+ treeRootHolder.setRoot(BRANCH);
+
+ expectMissingComponentISE();
underTest.persist(dbTester.getSession());
}
@Test
- public void persist_secondary_branch() {
- analysisMetadataHolder.setBranch(createBranch(BranchType.LONG, false, "branch"));
+ public void persist_fails_with_ISE_if_no_component_for_short_branches() {
+ analysisMetadataHolder.setBranch(createBranch(BranchType.SHORT, false, "foo"));
treeRootHolder.setRoot(BRANCH);
- // add main branch in project table and in metadata
- ComponentDto dto = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getDbKey());
- analysisMetadataHolder.setProject(Project.from(dto));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), dto);
+ expectMissingComponentISE();
+
+ underTest.persist(dbTester.getSession());
+ }
+
+ @Test
+ public void persist_fails_with_ISE_if_no_component_for_pull_request() {
+ analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, "12"));
+ treeRootHolder.setRoot(BRANCH);
+
+ expectMissingComponentISE();
+
+ underTest.persist(dbTester.getSession());
+ }
+
+ @Test
+ @UseDataProvider("nullOrNotNullString")
+ public void persist_creates_row_in_PROJECTS_BRANCHES_for_long_branch(@Nullable String mergeBranchUuid) {
+ String branchName = "branch";
+
+ // add project and branch in table PROJECTS
+ ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getKey());
+ ComponentDto component = ComponentTesting.newProjectBranch(mainComponent, new BranchDto().setUuid(BRANCH.getUuid()).setKey(BRANCH.getKey()).setBranchType(LONG));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), mainComponent, component);
+ dbTester.commit();
+ // set project in metadata
+ treeRootHolder.setRoot(BRANCH);
+ analysisMetadataHolder.setBranch(createBranch(LONG, false, branchName, mergeBranchUuid));
+ analysisMetadataHolder.setProject(Project.from(mainComponent));
- // this should add new columns in project and project_branches
underTest.persist(dbTester.getSession());
dbTester.getSession().commit();
assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(2);
- assertThat(dbTester.countRowsOfTable("project_branches")).isEqualTo(1);
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().getBranchType()).isEqualTo(LONG);
+ assertThat(branchDto.get().getKey()).isEqualTo(branchName);
+ assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
+ assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
+ assertThat(branchDto.get().getPullRequestData()).isNull();
+ }
+
+ @DataProvider
+ public static Object[][] nullOrNotNullString() {
+ return new Object[][] {
+ {null},
+ {randomAlphabetic(12)}
+ };
}
@Test
- public void persist_pull_request_data() {
+ @UseDataProvider("nullOrNotNullString")
+ public void persist_creates_row_in_PROJECTS_BRANCHES_for_pull_request(@Nullable String mergeBranchUuid) {
String pullRequestId = "pr-123";
- analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, pullRequestId));
- analysisMetadataHolder.setPullRequestKey(pullRequestId);
- treeRootHolder.setRoot(BRANCH);
- // add main branch in project table and in metadata
- ComponentDto dto = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getDbKey());
- analysisMetadataHolder.setProject(Project.from(dto));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), dto);
+ // add project and branch in table PROJECTS
+ ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getKey());
+ ComponentDto component = ComponentTesting.newProjectBranch(mainComponent, new BranchDto().setUuid(BRANCH.getUuid()).setKey(BRANCH.getKey()).setBranchType(PULL_REQUEST));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), mainComponent, component);
+ dbTester.commit();
+ // set project in metadata
+ treeRootHolder.setRoot(BRANCH);
+ analysisMetadataHolder.setBranch(createBranch(PULL_REQUEST, false, pullRequestId, mergeBranchUuid));
+ analysisMetadataHolder.setProject(Project.from(mainComponent));
+ analysisMetadataHolder.setPullRequestKey(pullRequestId);
- // this should add new columns in project and project_branches
underTest.persist(dbTester.getSession());
dbTester.getSession().commit();
assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(2);
- assertThat(dbTester.countRowsOfTable("project_branches")).isEqualTo(1);
- assertThat(dbTester.countSql("select count(*) from project_branches where pull_request_binary is not null")).isEqualTo(1);
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().getBranchType()).isEqualTo(PULL_REQUEST);
+ assertThat(branchDto.get().getKey()).isEqualTo(pullRequestId);
+ assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
+ assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
+ assertThat(branchDto.get().getPullRequestData()).isEqualTo(DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(pullRequestId)
+ .setTitle(pullRequestId)
+ .build());
}
private static Branch createBranch(BranchType type, boolean isMain, String name) {
+ return createBranch(type, isMain, name, null);
+ }
+
+ private static Branch createBranch(BranchType type, boolean isMain, String name, @Nullable String mergeBranchUuid) {
Branch branch = mock(Branch.class);
when(branch.getType()).thenReturn(type);
when(branch.getName()).thenReturn(name);
when(branch.isMain()).thenReturn(isMain);
- when(branch.getMergeBranchUuid()).thenReturn(Optional.empty());
+ when(branch.getMergeBranchUuid()).thenReturn(Optional.ofNullable(mergeBranchUuid));
return branch;
}
+
+ private void expectMissingComponentISE() {
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Component has been deleted by end-user during analysis");
+ }
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java
index 2b103c4b3a9..502d6aa0e0f 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java
@@ -23,6 +23,7 @@ import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.sql.SQLException;
+import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -57,6 +58,9 @@ public class PopulateFileSourceLineCountTest {
@Test
public void execute_has_no_effect_on_empty_table() throws SQLException {
+ String projectUuid = randomAlphanumeric(4);
+ when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
+
underTest.execute();
}
@@ -65,7 +69,7 @@ public class PopulateFileSourceLineCountTest {
public void execute_populates_line_count_of_any_type(String type) throws SQLException {
String projectUuid = randomAlphanumeric(4);
String fileUuid = randomAlphanumeric(5);
- when(ceTask.getComponentUuid()).thenReturn(projectUuid);
+ when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
int lineCount = 1 + random.nextInt(15);
insertUnpopulatedFileSource(projectUuid, fileUuid, type, lineCount);
assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(LINE_COUNT_NOT_POPULATED);
@@ -86,7 +90,7 @@ public class PopulateFileSourceLineCountTest {
int lineCountFile2 = 50 + random.nextInt(15);
int lineCountFile3 = 150 + random.nextInt(15);
- when(ceTask.getComponentUuid()).thenReturn(projectUuid);
+ when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
insertPopulatedFileSource(projectUuid, fileUuid1, type, lineCountFile1);
int badLineCountFile2 = insertInconsistentPopulatedFileSource(projectUuid, fileUuid2, type, lineCountFile2);
insertUnpopulatedFileSource(projectUuid, fileUuid3, type, lineCountFile3);
@@ -111,7 +115,7 @@ public class PopulateFileSourceLineCountTest {
int lineCountFile1 = 100 + random.nextInt(15);
int lineCountFile2 = 30 + random.nextInt(15);
- when(ceTask.getComponentUuid()).thenReturn(projectUuid1);
+ when(ceTask.getComponent()).thenReturn(newComponent(projectUuid1));
insertUnpopulatedFileSource(projectUuid1, fileUuid1, type, lineCountFile1);
insertUnpopulatedFileSource(projectUuid2, fileUuid2, type, lineCountFile2);
@@ -127,7 +131,7 @@ public class PopulateFileSourceLineCountTest {
String projectUuid = randomAlphanumeric(4);
String fileUuid1 = randomAlphanumeric(5);
- when(ceTask.getComponentUuid()).thenReturn(projectUuid);
+ when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
insertFileSource(projectUuid, fileUuid1, type, null, LINE_COUNT_NOT_POPULATED);
underTest.execute();
@@ -141,7 +145,7 @@ public class PopulateFileSourceLineCountTest {
String projectUuid = randomAlphanumeric(4);
String fileUuid1 = randomAlphanumeric(5);
- when(ceTask.getComponentUuid()).thenReturn(projectUuid);
+ when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
insertFileSource(projectUuid, fileUuid1, type, "", LINE_COUNT_NOT_POPULATED);
underTest.execute();
@@ -204,4 +208,8 @@ public class PopulateFileSourceLineCountTest {
"UPDATED_AT", 1_222_333L);
db.commit();
}
+
+ private static Optional<CeTask.Component> newComponent(String projectUuid) {
+ return Optional.of(new CeTask.Component(projectUuid, "key_" + projectUuid, "name_" + projectUuid));
+ }
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
index 41e3a4f8b73..3bf4c6fa7fe 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
@@ -25,6 +25,7 @@ import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
@@ -166,9 +167,43 @@ public class LoadReportAnalysisMetadataHolderStepTest {
}
@Test
+ public void execute_fails_with_ISE_if_component_is_null_in_CE_task() {
+ CeTask res = mock(CeTask.class);
+ when(res.getComponent()).thenReturn(Optional.empty());
+ when(res.getOrganizationUuid()).thenReturn(defaultOrganizationProvider.get().getUuid());
+ reportReader.setMetadata(ScannerReport.Metadata.newBuilder().build());
+
+ ComputationStep underTest = createStep(res);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("component missing on ce task");
+
+ underTest.execute(new TestComputationStepContext());
+ }
+
+ @Test
+ public void execute_fails_with_MessageException_if_main_projectKey_is_null_in_CE_task() {
+ CeTask res = mock(CeTask.class);
+ Optional<CeTask.Component> component = Optional.of(new CeTask.Component("main_prj_uuid", null, null));
+ when(res.getComponent()).thenReturn(component);
+ when(res.getMainComponent()).thenReturn(component);
+ when(res.getOrganizationUuid()).thenReturn(defaultOrganizationProvider.get().getUuid());
+ reportReader.setMetadata(ScannerReport.Metadata.newBuilder().build());
+
+ ComputationStep underTest = createStep(res);
+
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("Compute Engine task main component key is null. Project with UUID main_prj_uuid must have been deleted since report was uploaded. Can not proceed.");
+
+ underTest.execute(new TestComputationStepContext());
+ }
+
+ @Test
public void execute_fails_with_MessageException_if_projectKey_is_null_in_CE_task() {
CeTask res = mock(CeTask.class);
- when(res.getComponentUuid()).thenReturn("prj_uuid");
+ Optional<CeTask.Component> component = Optional.of(new CeTask.Component("prj_uuid", null, null));
+ when(res.getComponent()).thenReturn(component);
+ when(res.getMainComponent()).thenReturn(Optional.of(new CeTask.Component("main_prj_uuid", "main_prj_key", null)));
when(res.getOrganizationUuid()).thenReturn(defaultOrganizationProvider.get().getUuid());
reportReader.setMetadata(ScannerReport.Metadata.newBuilder().build());
@@ -407,8 +442,10 @@ public class LoadReportAnalysisMetadataHolderStepTest {
private CeTask createCeTask(String projectKey, String organizationUuid) {
CeTask res = mock(CeTask.class);
+ Optional<CeTask.Component> component = Optional.of(new CeTask.Component(projectKey + "_uuid", projectKey, projectKey + "_name"));
when(res.getOrganizationUuid()).thenReturn(organizationUuid);
- when(res.getComponentKey()).thenReturn(projectKey);
+ when(res.getComponent()).thenReturn(component);
+ when(res.getMainComponent()).thenReturn(component);
return res;
}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/ce/task/CeTask.java b/server/sonar-ce-task/src/main/java/org/sonar/ce/task/CeTask.java
index d71f9d036d0..3ea246612fa 100644
--- a/server/sonar-ce-task/src/main/java/org/sonar/ce/task/CeTask.java
+++ b/server/sonar-ce-task/src/main/java/org/sonar/ce/task/CeTask.java
@@ -22,10 +22,13 @@ package org.sonar.ce.task;
import com.google.common.base.MoreObjects;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.emptyToNull;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
@@ -37,9 +40,8 @@ public class CeTask {
private final String organizationUuid;
private final String type;
private final String uuid;
- private final String componentUuid;
- private final String componentKey;
- private final String componentName;
+ private final Component component;
+ private final Component mainComponent;
private final String submitterUuid;
private final Map<String, String> characteristics;
@@ -47,9 +49,10 @@ public class CeTask {
this.organizationUuid = requireNonNull(emptyToNull(builder.organizationUuid), "organizationUuid can't be null nor empty");
this.uuid = requireNonNull(emptyToNull(builder.uuid), "uuid can't be null nor empty");
this.type = requireNonNull(emptyToNull(builder.type), "type can't be null nor empty");
- this.componentUuid = emptyToNull(builder.componentUuid);
- this.componentKey = emptyToNull(builder.componentKey);
- this.componentName = emptyToNull(builder.componentName);
+ checkArgument((builder.component == null) == (builder.mainComponent == null),
+ "None or both component and main component must be non null");
+ this.component = builder.component;
+ this.mainComponent = builder.mainComponent;
this.submitterUuid = emptyToNull(builder.submitterUuid);
if (builder.characteristics == null) {
this.characteristics = emptyMap();
@@ -70,19 +73,12 @@ public class CeTask {
return type;
}
- @CheckForNull
- public String getComponentUuid() {
- return componentUuid;
- }
-
- @CheckForNull
- public String getComponentKey() {
- return componentKey;
+ public Optional<Component> getComponent() {
+ return Optional.ofNullable(component);
}
- @CheckForNull
- public String getComponentName() {
- return componentName;
+ public Optional<Component> getMainComponent() {
+ return Optional.ofNullable(mainComponent);
}
@CheckForNull
@@ -100,9 +96,8 @@ public class CeTask {
.add("organizationUuid", organizationUuid)
.add("type", type)
.add("uuid", uuid)
- .add("componentUuid", componentUuid)
- .add("componentKey", componentKey)
- .add("componentName", componentName)
+ .add("component", component)
+ .add("mainComponent", mainComponent)
.add("submitterUuid", submitterUuid)
.toString();
}
@@ -128,9 +123,8 @@ public class CeTask {
private String organizationUuid;
private String uuid;
private String type;
- private String componentUuid;
- private String componentKey;
- private String componentName;
+ private Component component;
+ private Component mainComponent;
private String submitterUuid;
private Map<String, String> characteristics;
@@ -154,18 +148,13 @@ public class CeTask {
return this;
}
- public Builder setComponentUuid(@Nullable String componentUuid) {
- this.componentUuid = componentUuid;
- return this;
- }
-
- public Builder setComponentKey(@Nullable String s) {
- this.componentKey = s;
+ public Builder setComponent(@Nullable Component component) {
+ this.component = component;
return this;
}
- public Builder setComponentName(@Nullable String s) {
- this.componentName = s;
+ public Builder setMainComponent(@Nullable Component mainComponent) {
+ this.mainComponent = mainComponent;
return this;
}
@@ -183,4 +172,58 @@ public class CeTask {
return new CeTask(this);
}
}
+
+ public static final class Component {
+ private final String uuid;
+ @CheckForNull
+ private final String key;
+ @CheckForNull
+ private final String name;
+
+ public Component(String uuid, @Nullable String key, @Nullable String name) {
+ this.uuid = requireNonNull(emptyToNull(uuid), "uuid can't be null nor empty");
+ this.key = emptyToNull(key);
+ this.name = emptyToNull(name);
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public Optional<String> getKey() {
+ return Optional.ofNullable(key);
+ }
+
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Component component = (Component) o;
+ return Objects.equals(uuid, component.uuid) &&
+ Objects.equals(key, component.key) &&
+ Objects.equals(name, component.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, key, name);
+ }
+
+ @Override
+ public String toString() {
+ return "Component{" +
+ "uuid='" + uuid + '\'' +
+ ", key='" + key + '\'' +
+ ", name='" + name + '\'' +
+ '}';
+ }
+ }
}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskComponentTest.java b/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskComponentTest.java
new file mode 100644
index 00000000000..556b9f76cc2
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskComponentTest.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.task;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class CeTaskComponentTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ @UseDataProvider("nullOrEmpty")
+ public void constructor_fails_with_NPE_if_uuid_is_null_or_empty(String str) {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null nor empty");
+
+ new CeTask.Component(str, "foo", "bar");
+ }
+
+ @Test
+ @UseDataProvider("nullOrEmpty")
+ public void constructor_considers_empty_as_null_and_accept_it_for_key(String str) {
+ CeTask.Component underTest = new CeTask.Component("foo", str, "bar");
+
+ assertThat(underTest.getKey()).isEmpty();
+ }
+
+ @Test
+ @UseDataProvider("nullOrEmpty")
+ public void constructor_considers_empty_as_null_and_accept_it_for_name(String str) {
+ CeTask.Component underTest = new CeTask.Component("foo", "bar", str);
+
+ assertThat(underTest.getName()).isEmpty();
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ String uuid = randomAlphabetic(2);
+ String key = randomAlphabetic(3);
+ String name = randomAlphabetic(4);
+ String somethingElse = randomAlphabetic(5);
+ CeTask.Component underTest = new CeTask.Component(uuid, key, name);
+
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new CeTask.Component(uuid, key, name));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new CeTask.Component(somethingElse, key, name));
+ assertThat(underTest).isNotEqualTo(new CeTask.Component(uuid, somethingElse, name));
+ assertThat(underTest).isNotEqualTo(new CeTask.Component(uuid, key, somethingElse));
+ assertThat(underTest).isNotEqualTo(new CeTask.Component(uuid, key, null));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ String uuid = randomAlphabetic(2);
+ String key = randomAlphabetic(3);
+ String name = randomAlphabetic(4);
+ String somethingElse = randomAlphabetic(5);
+ CeTask.Component underTest = new CeTask.Component(uuid, key, name);
+
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new CeTask.Component(uuid, key, name).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new CeTask.Component(somethingElse, key, name).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new CeTask.Component(uuid, somethingElse, name).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new CeTask.Component(uuid, key, somethingElse).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new CeTask.Component(uuid, key, null).hashCode());
+ }
+
+ @DataProvider
+ public static Object[][] nullOrEmpty() {
+ return new Object[][] {
+ {null},
+ {""},
+ };
+ }
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskTest.java b/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskTest.java
index dbb26c47ca3..a72e0b39b59 100644
--- a/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskTest.java
+++ b/server/sonar-ce-task/src/test/java/org/sonar/ce/task/CeTaskTest.java
@@ -20,13 +20,18 @@
package org.sonar.ce.task;
import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
+@RunWith(DataProviderRunner.class)
public class CeTaskTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -92,14 +97,39 @@ public class CeTaskTest {
}
@Test
+ @UseDataProvider("oneAndOnlyOneOfComponentAndMainComponent")
+ public void build_fails_with_IAE_if_only_one_of_component_and_main_component_is_non_null(CeTask.Component component, CeTask.Component mainComponent) {
+ underTest.setOrganizationUuid("org1");
+ underTest.setType("TYPE_1");
+ underTest.setUuid("UUID_1");
+ underTest.setComponent(component);
+ underTest.setMainComponent(mainComponent);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("None or both component and main component must be non null");
+
+ underTest.build();
+ }
+
+ @DataProvider
+ public static Object[][] oneAndOnlyOneOfComponentAndMainComponent() {
+ CeTask.Component component = new CeTask.Component("COMPONENT_UUID_1", "COMPONENT_KEY_1", "The component");
+ return new Object[][] {
+ {component, null},
+ {null, component}
+ };
+ }
+
+ @Test
public void verify_getters() {
+ CeTask.Component component = new CeTask.Component("COMPONENT_UUID_1", "COMPONENT_KEY_1", "The component");
+ CeTask.Component mainComponent = new CeTask.Component("MAIN_COMPONENT_UUID_1", "MAIN_COMPONENT_KEY_1", "The main component");
underTest.setOrganizationUuid("org1");
underTest.setType("TYPE_1");
underTest.setUuid("UUID_1");
underTest.setSubmitterUuid("LOGIN_1");
- underTest.setComponentKey("COMPONENT_KEY_1");
- underTest.setComponentUuid("COMPONENT_UUID_1");
- underTest.setComponentName("The component");
+ underTest.setComponent(component);
+ underTest.setMainComponent(mainComponent);
underTest.setCharacteristics(ImmutableMap.of("k1", "v1", "k2", "v2"));
CeTask task = underTest.build();
@@ -108,26 +138,12 @@ public class CeTaskTest {
assertThat(task.getUuid()).isEqualTo("UUID_1");
assertThat(task.getType()).isEqualTo("TYPE_1");
assertThat(task.getSubmitterUuid()).isEqualTo("LOGIN_1");
- assertThat(task.getComponentKey()).isEqualTo("COMPONENT_KEY_1");
- assertThat(task.getComponentUuid()).isEqualTo("COMPONENT_UUID_1");
- assertThat(task.getComponentName()).isEqualTo("The component");
+ assertThat(task.getComponent()).contains(component);
+ assertThat(task.getMainComponent()).contains(mainComponent);
assertThat(task.getCharacteristics()).containsExactly(entry("k1", "v1"), entry("k2", "v2"));
}
@Test
- public void empty_in_component_properties_is_considered_as_null() {
- CeTask ceTask = underTest.setOrganizationUuid("org1").setUuid("uuid").setType("type")
- .setComponentKey("")
- .setComponentName("")
- .setComponentUuid("")
- .build();
-
- assertThat(ceTask.getComponentKey()).isNull();
- assertThat(ceTask.getComponentName()).isNull();
- assertThat(ceTask.getComponentUuid()).isNull();
- }
-
- @Test
public void empty_in_submitterLogin_is_considered_as_null() {
CeTask ceTask = underTest.setOrganizationUuid("org1").setUuid("uuid").setType("type")
.setSubmitterUuid("")
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListener.java b/server/sonar-ce/src/main/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListener.java
index db4c94834cc..e414da45fc0 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListener.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListener.java
@@ -64,7 +64,7 @@ public class ReportAnalysisFailureNotificationExecutionListener implements CeWor
if (status == CeActivityDto.Status.SUCCESS) {
return;
}
- String projectUuid = ceTask.getComponentUuid();
+ String projectUuid = ceTask.getComponent().map(CeTask.Component::getUuid).orElse(null);
if (!CeTaskTypes.REPORT.equals(ceTask.getType()) || projectUuid == null) {
return;
}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java
index 61c801947bc..eb6928877d3 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java
@@ -53,6 +53,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
@ComputeEngineSide
@@ -90,15 +91,13 @@ public class InternalCeQueueImpl extends CeQueueImpl implements InternalCeQueue
Optional<CeQueueDto> opt = ceQueueDao.peek(dbSession, workerUuid);
if (opt.isPresent()) {
CeQueueDto taskDto = opt.get();
- ComponentDto component = null;
- String componentUuid = taskDto.getComponentUuid();
- if (componentUuid != null) {
- component = dbClient.componentDao().selectByUuid(dbSession, componentUuid).orElse(null);
- }
+ Map<String, ComponentDto> componentsByUuid = loadComponentDtos(dbSession, taskDto);
Map<String, String> characteristics = dbClient.ceTaskCharacteristicsDao().selectByTaskUuids(dbSession, singletonList(taskDto.getUuid())).stream()
.collect(uniqueIndex(CeTaskCharacteristicDto::getKey, CeTaskCharacteristicDto::getValue));
- CeTask task = convertToTask(taskDto, characteristics, component);
+ CeTask task = convertToTask(taskDto, characteristics,
+ ofNullable(taskDto.getComponentUuid()).map(componentsByUuid::get).orElse(null),
+ ofNullable(taskDto.getMainComponentUuid()).map(componentsByUuid::get).orElse(null));
queueStatus.addInProgress();
return Optional.of(task);
}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/CeWorkerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/CeWorkerImpl.java
index e49748b73d9..ba71928c658 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/CeWorkerImpl.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/CeWorkerImpl.java
@@ -180,7 +180,7 @@ public class CeWorkerImpl implements CeWorker {
private static Profiler startLogProfiler(CeTask task) {
Profiler profiler = Profiler.create(LOG)
.logTimeLast(true)
- .addContext("project", task.getComponentKey())
+ .addContext("project", task.getMainComponent().flatMap(CeTask.Component::getKey).orElse(null))
.addContext("type", task.getType());
for (Map.Entry<String, String> characteristic : task.getCharacteristics().entrySet()) {
profiler.addContext(characteristic.getKey(), characteristic.getValue());
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListenerTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListenerTest.java
index fd341a467dd..69cd2813096 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListenerTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/notification/ReportAnalysisFailureNotificationExecutionListenerTest.java
@@ -20,6 +20,7 @@
package org.sonar.ce.notification;
import java.util.Arrays;
+import java.util.Optional;
import java.util.Random;
import javax.annotation.Nullable;
import org.junit.Before;
@@ -121,7 +122,7 @@ public class ReportAnalysisFailureNotificationExecutionListenerTest {
public void onEnd_has_no_effect_if_there_is_no_subscriber_for_ReportAnalysisFailureNotification_type() {
String componentUuid = randomAlphanumeric(6);
when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
- when(ceTaskMock.getComponentUuid()).thenReturn(componentUuid);
+ when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(componentUuid, null, null)));
when(notificationService.hasProjectSubscribersForTypes(componentUuid, singleton(ReportAnalysisFailureNotification.TYPE)))
.thenReturn(false);
@@ -134,7 +135,7 @@ public class ReportAnalysisFailureNotificationExecutionListenerTest {
public void onEnd_fails_with_RowNotFoundException_if_component_does_not_exist_in_DB() {
String componentUuid = randomAlphanumeric(6);
when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
- when(ceTaskMock.getComponentUuid()).thenReturn(componentUuid);
+ when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(componentUuid, null, null)));
when(notificationService.hasProjectSubscribersForTypes(componentUuid, singleton(ReportAnalysisFailureNotification.TYPE)))
.thenReturn(true);
@@ -160,7 +161,7 @@ public class ReportAnalysisFailureNotificationExecutionListenerTest {
Arrays.asList(module, directory, file, view, subView, projectCopy, application)
.forEach(component -> {
try {
- when(ceTaskMock.getComponentUuid()).thenReturn(component.uuid());
+ when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(component.uuid(), null, null)));
when(notificationService.hasProjectSubscribersForTypes(component.uuid(), singleton(ReportAnalysisFailureNotification.TYPE)))
.thenReturn(true);
@@ -180,7 +181,7 @@ public class ReportAnalysisFailureNotificationExecutionListenerTest {
String taskUuid = randomAlphanumeric(6);
when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
when(ceTaskMock.getUuid()).thenReturn(taskUuid);
- when(ceTaskMock.getComponentUuid()).thenReturn(componentUuid);
+ when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(componentUuid, null, null)));
when(notificationService.hasProjectSubscribersForTypes(componentUuid, singleton(ReportAnalysisFailureNotification.TYPE)))
.thenReturn(true);
dbTester.components().insertPrivateProject(s -> s.setUuid(componentUuid));
@@ -293,7 +294,7 @@ public class ReportAnalysisFailureNotificationExecutionListenerTest {
private ComponentDto initMocksToPassConditions(String taskUuid, int createdAt, @Nullable Long executedAt) {
ComponentDto project = random.nextBoolean() ? dbTester.components().insertPrivateProject() : dbTester.components().insertPublicProject();
when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
- when(ceTaskMock.getComponentUuid()).thenReturn(project.uuid());
+ when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(project.uuid(), null, null)));
when(ceTaskMock.getUuid()).thenReturn(taskUuid);
when(notificationService.hasProjectSubscribersForTypes(project.uuid(), singleton(ReportAnalysisFailureNotification.TYPE)))
.thenReturn(true);
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java
index 2b7229ee4d2..82678dd8699 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java
@@ -99,7 +99,7 @@ public class InternalCeQueueImplTest {
@Test
public void submit_returns_task_populated_from_CeTaskSubmit_and_creates_CeQueue_row() {
- CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob");
+ CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"), "rob");
CeTask task = underTest.submit(taskSubmit);
verifyCeTask(taskSubmit, task, null);
@@ -108,8 +108,8 @@ public class InternalCeQueueImplTest {
@Test
public void submit_populates_component_name_and_key_of_CeTask_if_component_exists() {
- ComponentDto componentDto = insertComponent(newComponentDto("PROJECT_1"));
- CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, componentDto.uuid(), null);
+ ComponentDto componentDto = insertComponent(newProjectDto("PROJECT_1"));
+ CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, componentDto, null);
CeTask task = underTest.submit(taskSubmit);
@@ -127,7 +127,7 @@ public class InternalCeQueueImplTest {
@Test
public void massSubmit_returns_tasks_for_each_CeTaskSubmit_populated_from_CeTaskSubmit_and_creates_CeQueue_row_for_each() {
- CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob");
+ CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"), "rob");
CeTaskSubmit taskSubmit2 = createTaskSubmit("some type");
List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
@@ -141,9 +141,9 @@ public class InternalCeQueueImplTest {
@Test
public void massSubmit_populates_component_name_and_key_of_CeTask_if_component_exists() {
- ComponentDto componentDto1 = insertComponent(newComponentDto("PROJECT_1"));
- CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, componentDto1.uuid(), null);
- CeTaskSubmit taskSubmit2 = createTaskSubmit("something", "non existing component uuid", null);
+ ComponentDto componentDto1 = insertComponent(newProjectDto("PROJECT_1"));
+ CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, componentDto1, null);
+ CeTaskSubmit taskSubmit2 = createTaskSubmit("something", newProjectDto("non existing component uuid"), null);
List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
@@ -162,7 +162,7 @@ public class InternalCeQueueImplTest {
@Test
public void test_remove() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, null, null);
@@ -196,7 +196,7 @@ public class InternalCeQueueImplTest {
@Test
public void remove_does_not_set_analysisUuid_in_CeActivity_when_CeTaskResult_has_no_analysis_uuid() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, newTaskResult(null), null);
@@ -208,7 +208,7 @@ public class InternalCeQueueImplTest {
@Test
public void remove_sets_analysisUuid_in_CeActivity_when_CeTaskResult_has_analysis_uuid() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
Optional<CeTask> peek = underTest.peek(WORKER_UUID_2);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, newTaskResult(AN_ANALYSIS_UUID), null);
@@ -223,7 +223,7 @@ public class InternalCeQueueImplTest {
public void remove_saves_error_message_and_stacktrace_when_exception_is_provided() {
Throwable error = new NullPointerException("Fake NPE to test persistence to DB");
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.FAILED, null, error);
@@ -239,7 +239,7 @@ public class InternalCeQueueImplTest {
public void remove_saves_error_when_TypedMessageException_is_provided() {
Throwable error = new TypedExceptionImpl("aType", "aMessage");
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.FAILED, null, error);
@@ -253,7 +253,7 @@ public class InternalCeQueueImplTest {
public void remove_updates_queueStatus_success_even_if_task_does_not_exist_in_DB() {
CEQueueStatus queueStatus = mock(CEQueueStatus.class);
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
db.getDbClient().ceQueueDao().deleteByUuid(db.getSession(), task.getUuid());
db.commit();
@@ -271,7 +271,7 @@ public class InternalCeQueueImplTest {
public void remove_updates_queueStatus_failure_even_if_task_does_not_exist_in_DB() {
CEQueueStatus queueStatusMock = mock(CEQueueStatus.class);
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
db.getDbClient().ceQueueDao().deleteByUuid(db.getSession(), task.getUuid());
db.commit();
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatusMock, null, null);
@@ -288,7 +288,7 @@ public class InternalCeQueueImplTest {
public void cancelWornOuts_does_not_update_queueStatus() {
CEQueueStatus queueStatusMock = mock(CEQueueStatus.class);
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
db.executeUpdateSql("update ce_queue set status = 'PENDING', started_at = 123 where uuid = '" + task.getUuid() + "'");
db.commit();
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatusMock, null, null);
@@ -334,7 +334,7 @@ public class InternalCeQueueImplTest {
@Test
public void fail_to_remove_if_not_in_queue() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
underTest.remove(task, CeActivityDto.Status.SUCCESS, null, null);
expectedException.expect(IllegalStateException.class);
@@ -344,13 +344,32 @@ public class InternalCeQueueImplTest {
@Test
public void test_peek() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
assertThat(peek.isPresent()).isTrue();
assertThat(peek.get().getUuid()).isEqualTo(task.getUuid());
assertThat(peek.get().getType()).isEqualTo(CeTaskTypes.REPORT);
- assertThat(peek.get().getComponentUuid()).isEqualTo("PROJECT_1");
+ assertThat(peek.get().getComponent()).contains(new CeTask.Component("PROJECT_1", null, null));
+ assertThat(peek.get().getMainComponent()).contains(peek.get().getComponent().get());
+
+ // no more pending tasks
+ peek = underTest.peek(WORKER_UUID_2);
+ assertThat(peek.isPresent()).isFalse();
+ }
+
+ @Test
+ public void peek_populates_name_and_key_for_existing_component_and_main_component() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ CeTask task = submit(CeTaskTypes.REPORT, branch);
+
+ Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
+ assertThat(peek.isPresent()).isTrue();
+ assertThat(peek.get().getUuid()).isEqualTo(task.getUuid());
+ assertThat(peek.get().getType()).isEqualTo(CeTaskTypes.REPORT);
+ assertThat(peek.get().getComponent()).contains(new CeTask.Component(branch.uuid(), branch.getDbKey(), branch.name()));
+ assertThat(peek.get().getMainComponent()).contains(new CeTask.Component(project.uuid(), project.getDbKey(), project.name()));
// no more pending tasks
peek = underTest.peek(WORKER_UUID_2);
@@ -359,7 +378,7 @@ public class InternalCeQueueImplTest {
@Test
public void peek_is_paused_then_resumed() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
underTest.pauseWorkers();
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
@@ -385,7 +404,7 @@ public class InternalCeQueueImplTest {
@Test
public void peek_nothing_if_application_status_stopping() {
- submit(CeTaskTypes.REPORT, "PROJECT_1");
+ submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
when(computeEngineStatus.getStatus()).thenReturn(STOPPING);
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
@@ -468,7 +487,7 @@ public class InternalCeQueueImplTest {
@Test
public void cancel_pending() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
CeQueueDto queueDto = db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), task.getUuid()).get();
underTest.cancel(db.getSession(), queueDto);
@@ -480,7 +499,7 @@ public class InternalCeQueueImplTest {
@Test
public void fail_to_cancel_if_in_progress() {
- CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
+ CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
underTest.peek(WORKER_UUID_2);
CeQueueDto queueDto = db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), task.getUuid()).get();
@@ -492,9 +511,9 @@ public class InternalCeQueueImplTest {
@Test
public void cancelAll_pendings_but_not_in_progress() {
- CeTask inProgressTask = submit(CeTaskTypes.REPORT, "PROJECT_1");
- CeTask pendingTask1 = submit(CeTaskTypes.REPORT, "PROJECT_2");
- CeTask pendingTask2 = submit(CeTaskTypes.REPORT, "PROJECT_3");
+ CeTask inProgressTask = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
+ CeTask pendingTask1 = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_2"));
+ CeTask pendingTask2 = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_3"));
underTest.peek(WORKER_UUID_2);
int canceledCount = underTest.cancelAll();
@@ -608,14 +627,16 @@ public class InternalCeQueueImplTest {
assertThat(task.getOrganizationUuid()).isEqualTo(componentDto.getOrganizationUuid());
}
assertThat(task.getUuid()).isEqualTo(taskSubmit.getUuid());
- assertThat(task.getComponentUuid()).isEqualTo(task.getComponentUuid());
assertThat(task.getType()).isEqualTo(taskSubmit.getType());
- if (componentDto == null) {
- assertThat(task.getComponentKey()).isNull();
- assertThat(task.getComponentName()).isNull();
+ if (componentDto != null) {
+ CeTask.Component component = task.getComponent().get();
+ assertThat(component.getUuid()).isEqualTo(componentDto.uuid());
+ assertThat(component.getKey()).contains(componentDto.getDbKey());
+ assertThat(component.getName()).contains(componentDto.name());
+ } else if (taskSubmit.getComponent().isPresent()) {
+ assertThat(task.getComponent()).contains(new CeTask.Component(taskSubmit.getComponent().get().getUuid(), null, null));
} else {
- assertThat(task.getComponentKey()).isEqualTo(componentDto.getDbKey());
- assertThat(task.getComponentName()).isEqualTo(componentDto.name());
+ assertThat(task.getComponent()).isEmpty();
}
assertThat(task.getSubmitterUuid()).isEqualTo(taskSubmit.getSubmitterUuid());
}
@@ -625,30 +646,39 @@ public class InternalCeQueueImplTest {
assertThat(queueDto.isPresent()).isTrue();
CeQueueDto dto = queueDto.get();
assertThat(dto.getTaskType()).isEqualTo(taskSubmit.getType());
- assertThat(dto.getComponentUuid()).isEqualTo(taskSubmit.getComponentUuid());
+ Optional<CeTaskSubmit.Component> component = taskSubmit.getComponent();
+ if (component.isPresent()) {
+ assertThat(dto.getMainComponentUuid()).isEqualTo(component.get().getMainComponentUuid());
+ assertThat(dto.getComponentUuid()).isEqualTo(component.get().getUuid());
+ } else {
+ assertThat(dto.getMainComponentUuid()).isNull();
+ assertThat(dto.getComponentUuid()).isNull();
+ }
assertThat(dto.getSubmitterUuid()).isEqualTo(taskSubmit.getSubmitterUuid());
assertThat(dto.getCreatedAt()).isEqualTo(dto.getUpdatedAt()).isNotNull();
}
- private ComponentDto newComponentDto(String uuid) {
+ private ComponentDto newProjectDto(String uuid) {
return ComponentTesting.newPublicProjectDto(db.getDefaultOrganization(), uuid).setName("name_" + uuid).setDbKey("key_" + uuid);
}
- private CeTask submit(String reportType, String componentUuid) {
- return underTest.submit(createTaskSubmit(reportType, componentUuid, null));
+ private CeTask submit(String reportType, ComponentDto componentDto) {
+ return underTest.submit(createTaskSubmit(reportType, componentDto, null));
}
private CeTaskSubmit createTaskSubmit(String type) {
return createTaskSubmit(type, null, null);
}
- private CeTaskSubmit createTaskSubmit(String type, @Nullable String componentUuid, @Nullable String submitterUuid) {
- return underTest.prepareSubmit()
+ private CeTaskSubmit createTaskSubmit(String type, @Nullable ComponentDto componentDto, @Nullable String submitterUuid) {
+ CeTaskSubmit.Builder builder = underTest.prepareSubmit()
.setType(type)
- .setComponentUuid(componentUuid)
.setSubmitterUuid(submitterUuid)
- .setCharacteristics(emptyMap())
- .build();
+ .setCharacteristics(emptyMap());
+ if (componentDto != null) {
+ builder.setComponent(CeTaskSubmit.Component.fromDto(componentDto));
+ }
+ return builder.build();
}
private CeTaskResult newTaskResult(@Nullable String analysisUuid) {
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeTaskProcessorRepositoryImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeTaskProcessorRepositoryImplTest.java
index acd80691712..57524a13fa6 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeTaskProcessorRepositoryImplTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeTaskProcessorRepositoryImplTest.java
@@ -107,13 +107,13 @@ public class CeTaskProcessorRepositoryImplTest {
}
private static CeTask createCeTask(String ceTaskType, String key) {
+ CeTask.Component component = new CeTask.Component("uuid_" + key, key, "name_" + key);
return new CeTask.Builder()
.setOrganizationUuid("org1")
.setType(ceTaskType)
.setUuid("task_uuid_" + key)
- .setComponentKey(key)
- .setComponentUuid("uuid_" + key)
- .setComponentName("name_" + key)
+ .setComponent(component)
+ .setMainComponent(component)
.build();
}
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeWorkerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeWorkerImplTest.java
index b207eef4556..5a1166a16c6 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeWorkerImplTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/CeWorkerImplTest.java
@@ -554,10 +554,12 @@ public class CeWorkerImplTest {
for (int i = 0; i < characteristics.length; i += 2) {
characteristicMap.put(characteristics[i], characteristics[i + 1]);
}
+ CeTask.Component component = new CeTask.Component("PROJECT_1", null, null);
return new CeTask.Builder()
.setOrganizationUuid("org1")
.setUuid("TASK_1").setType(CeTaskTypes.REPORT)
- .setComponentUuid("PROJECT_1")
+ .setComponent(component)
+ .setMainComponent(component)
.setSubmitterUuid(submitterLogin)
.setCharacteristics(characteristicMap)
.build();
diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
index 3bae61a4f50..ed0e309f56e 100644
--- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
+++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
@@ -669,6 +669,8 @@ CREATE TABLE "CE_QUEUE" (
"UUID" VARCHAR(40) NOT NULL,
"TASK_TYPE" VARCHAR(15) NOT NULL,
"COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
"STATUS" VARCHAR(15) NOT NULL,
"SUBMITTER_UUID" VARCHAR(255) NULL,
"WORKER_UUID" VARCHAR(40) NULL,
@@ -679,6 +681,8 @@ CREATE TABLE "CE_QUEUE" (
);
CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_TMP_CPNT_UUID" ON "CE_QUEUE" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_TMP_MAIN_CPNT_UUID" ON "CE_QUEUE" ("TMP_MAIN_COMPONENT_UUID");
CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
@@ -687,10 +691,16 @@ CREATE TABLE "CE_ACTIVITY" (
"UUID" VARCHAR(40) NOT NULL,
"TASK_TYPE" VARCHAR(15) NOT NULL,
"COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
"ANALYSIS_UUID" VARCHAR(50) NULL,
"STATUS" VARCHAR(15) NOT NULL,
"IS_LAST" BOOLEAN NOT NULL,
"IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "TMP_IS_LAST" BOOLEAN,
+ "TMP_IS_LAST_KEY" VARCHAR(55),
+ "TMP_MAIN_IS_LAST" BOOLEAN,
+ "TMP_MAIN_IS_LAST_KEY" VARCHAR(55),
"SUBMITTER_UUID" VARCHAR(255) NULL,
"WORKER_UUID" VARCHAR(40) NULL,
"EXECUTION_COUNT" INTEGER NOT NULL,
@@ -706,8 +716,14 @@ CREATE TABLE "CE_ACTIVITY" (
);
CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CPNT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CPNT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_T_ISLAST_KEY" ON "CE_ACTIVITY" ("TMP_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_T_ISLAST" ON "CE_ACTIVITY" ("TMP_IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_T_MAIN_ISLAST_KEY" ON "CE_ACTIVITY" ("TMP_MAIN_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_T_MAIN_ISLAST" ON "CE_ACTIVITY" ("TMP_MAIN_IS_LAST", "STATUS");
CREATE TABLE "CE_TASK_CHARACTERISTICS" (
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java
index 4cd6ada6da0..6791b86ee08 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java
@@ -46,11 +46,12 @@ public class CeActivityDao implements Dao {
public void insert(DbSession dbSession, CeActivityDto dto) {
dto.setCreatedAt(system2.now());
dto.setUpdatedAt(system2.now());
- dto.setIsLast(dto.getStatus() != CeActivityDto.Status.CANCELED);
+ boolean isLast = dto.getStatus() != CeActivityDto.Status.CANCELED;
+ dto.setIsLast(isLast);
CeActivityMapper ceActivityMapper = mapper(dbSession);
- if (dto.getIsLast()) {
- ceActivityMapper.updateIsLastToFalseForLastKey(dto.getIsLastKey(), dto.getUpdatedAt());
+ if (isLast) {
+ ceActivityMapper.clearIsLast(dto.getIsLastKey(), dto.getMainIsLastKey(), dto.getUpdatedAt());
}
ceActivityMapper.insert(dto);
}
@@ -67,15 +68,15 @@ public class CeActivityDao implements Dao {
* Ordered by id desc -> newest to oldest
*/
public List<CeActivityDto> selectByQuery(DbSession dbSession, CeTaskQuery query, Pagination pagination) {
- if (query.isShortCircuitedByComponentUuids()) {
+ if (query.isShortCircuitedByMainComponentUuids()) {
return Collections.emptyList();
}
return mapper(dbSession).selectByQuery(query, pagination);
}
- public int countLastByStatusAndComponentUuid(DbSession dbSession, CeActivityDto.Status status, @Nullable String componentUuid) {
- return mapper(dbSession).countLastByStatusAndComponentUuid(status, componentUuid);
+ public int countLastByStatusAndMainComponentUuid(DbSession dbSession, CeActivityDto.Status status, @Nullable String mainComponentUuid) {
+ return mapper(dbSession).countLastByStatusAndMainComponentUuid(status, mainComponentUuid);
}
private static CeActivityMapper mapper(DbSession dbSession) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java
index 19be3c4951a..af3e004aa75 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java
@@ -23,6 +23,7 @@ import com.google.common.base.Strings;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
@@ -36,12 +37,28 @@ public class CeActivityDto {
}
private String uuid;
+ /**
+ * Can be {@code null} when task is not associated to any data in table PROJECTS, but must always be non {@code null}
+ * at the same time as {@link #mainComponentUuid}.
+ * <p>
+ * The component uuid of a any component (project or not) is its own UUID.
+ */
private String componentUuid;
+ /**
+ * Can be {@code null} when task is not associated to any data in table PROJECTS, but must always be non {@code null}
+ * at the same time as {@link #componentUuid}.
+ * <p>
+ * The main component uuid of the main branch of project is its own UUID. For other branches of a project, it is the
+ * project UUID of the main branch of that project ({@link ComponentDto#getMainBranchProjectUuid()}).
+ */
+ private String mainComponentUuid;
private String analysisUuid;
private Status status;
private String taskType;
private boolean isLast;
private String isLastKey;
+ private boolean mainIsLast;
+ private String mainIsLastKey;
private String submitterUuid;
private String workerUuid;
private long submittedAt;
@@ -91,7 +108,9 @@ public class CeActivityDto {
this.uuid = queueDto.getUuid();
this.taskType = queueDto.getTaskType();
this.componentUuid = queueDto.getComponentUuid();
+ this.mainComponentUuid = queueDto.getMainComponentUuid();
this.isLastKey = format("%s%s", taskType, Strings.nullToEmpty(componentUuid));
+ this.mainIsLastKey = format("%s%s", taskType, Strings.nullToEmpty(mainComponentUuid));
this.submitterUuid = queueDto.getSubmitterUuid();
this.workerUuid = queueDto.getWorkerUuid();
this.submittedAt = queueDto.getCreatedAt();
@@ -102,8 +121,8 @@ public class CeActivityDto {
return uuid;
}
- public CeActivityDto setUuid(String s) {
- checkArgument(s.length() <= 40, "Value is too long for column CE_ACTIVITY.UUID: %s", s);
+ public CeActivityDto setUuid(@Nullable String s) {
+ validateUuid(s, "UUID");
this.uuid = s;
return this;
}
@@ -123,11 +142,26 @@ public class CeActivityDto {
}
public CeActivityDto setComponentUuid(@Nullable String s) {
- checkArgument(s == null || s.length() <= 40, "Value is too long for column CE_ACTIVITY.COMPONENT_UUID: %s", s);
+ validateUuid(s, "COMPONENT_UUID");
this.componentUuid = s;
return this;
}
+ @CheckForNull
+ public String getMainComponentUuid() {
+ return mainComponentUuid;
+ }
+
+ public CeActivityDto setMainComponentUuid(@Nullable String s) {
+ validateUuid(s, "MAIN_COMPONENT_UUID");
+ this.mainComponentUuid = s;
+ return this;
+ }
+
+ private static void validateUuid(@Nullable String s, String columnName) {
+ checkArgument(s == null || s.length() <= 40, "Value is too long for column CE_ACTIVITY.%s: %s", columnName, s);
+ }
+
public Status getStatus() {
return status;
}
@@ -143,6 +177,7 @@ public class CeActivityDto {
CeActivityDto setIsLast(boolean b) {
this.isLast = b;
+ this.mainIsLast = b;
return this;
}
@@ -150,6 +185,14 @@ public class CeActivityDto {
return isLastKey;
}
+ public boolean getMainIsLast() {
+ return mainIsLast;
+ }
+
+ public String getMainIsLastKey() {
+ return mainIsLastKey;
+ }
+
@CheckForNull
public String getSubmitterUuid() {
return submitterUuid;
@@ -277,11 +320,14 @@ public class CeActivityDto {
return "CeActivityDto{" +
"uuid='" + uuid + '\'' +
", componentUuid='" + componentUuid + '\'' +
+ ", mainComponentUuid='" + mainComponentUuid + '\'' +
", analysisUuid='" + analysisUuid + '\'' +
", status=" + status +
", taskType='" + taskType + '\'' +
", isLast=" + isLast +
", isLastKey='" + isLastKey + '\'' +
+ ", mainIsLast=" + mainIsLast +
+ ", mainIsLastKey='" + mainIsLastKey + '\'' +
", submitterUuid='" + submitterUuid + '\'' +
", workerUuid='" + workerUuid + '\'' +
", submittedAt=" + submittedAt +
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java
index 838f1d022f1..bc36fd2d9dd 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java
@@ -30,17 +30,15 @@ public interface CeActivityMapper {
@CheckForNull
CeActivityDto selectByUuid(@Param("uuid") String uuid);
- List<CeActivityDto> selectByComponentUuid(@Param("componentUuid") String componentUuid);
-
List<CeActivityDto> selectByQuery(@Param("query") CeTaskQuery query, @Param("pagination") Pagination pagination);
List<CeActivityDto> selectOlderThan(@Param("beforeDate") long beforeDate);
- int countLastByStatusAndComponentUuid(@Param("status") CeActivityDto.Status status, @Nullable @Param("componentUuid") String componentUuid);
+ int countLastByStatusAndMainComponentUuid(@Param("status") CeActivityDto.Status status, @Nullable @Param("mainComponentUuid") String mainComponentUuid);
void insert(CeActivityDto dto);
- void updateIsLastToFalseForLastKey(@Param("isLastKey") String isLastKey, @Param("updatedAt") long updatedAt);
+ void clearIsLast(@Param("isLastKey") String isLastKey, @Param("mainIsLastKey") String mainIsLastKey, @Param("updatedAt") long updatedAt);
void deleteByUuids(@Param("uuids") List<String> uuids);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java
index 3aabaa68ca4..02fab58ce36 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java
@@ -55,7 +55,7 @@ public class CeQueueDao implements Dao {
}
public List<CeQueueDto> selectByQueryInDescOrder(DbSession dbSession, CeTaskQuery query, int pageSize) {
- if (query.isShortCircuitedByComponentUuids()
+ if (query.isShortCircuitedByMainComponentUuids()
|| query.isOnlyCurrents()
|| query.getMaxExecutedAt() != null) {
return emptyList();
@@ -65,7 +65,7 @@ public class CeQueueDao implements Dao {
}
public int countByQuery(DbSession dbSession, CeTaskQuery query) {
- if (query.isShortCircuitedByComponentUuids()
+ if (query.isShortCircuitedByMainComponentUuids()
|| query.isOnlyCurrents()
|| query.getMaxExecutedAt() != null) {
return 0;
@@ -77,8 +77,8 @@ public class CeQueueDao implements Dao {
/**
* Ordered by ascending id: oldest to newest
*/
- public List<CeQueueDto> selectByComponentUuid(DbSession session, String componentUuid) {
- return mapper(session).selectByComponentUuid(componentUuid);
+ public List<CeQueueDto> selectByMainComponentUuid(DbSession session, String projectUuid) {
+ return mapper(session).selectByMainComponentUuid(projectUuid);
}
public Optional<CeQueueDto> selectByUuid(DbSession session, String uuid) {
@@ -131,30 +131,30 @@ public class CeQueueDao implements Dao {
}
public int countByStatus(DbSession dbSession, CeQueueDto.Status status) {
- return mapper(dbSession).countByStatusAndComponentUuid(status, null);
+ return mapper(dbSession).countByStatusAndMainComponentUuid(status, null);
}
- public int countByStatusAndComponentUuid(DbSession dbSession, CeQueueDto.Status status, @Nullable String componentUuid) {
- return mapper(dbSession).countByStatusAndComponentUuid(status, componentUuid);
+ public int countByStatusAndMainComponentUuid(DbSession dbSession, CeQueueDto.Status status, @Nullable String mainComponentUuid) {
+ return mapper(dbSession).countByStatusAndMainComponentUuid(status, mainComponentUuid);
}
/**
- * Counts entries in the queue with the specified status for each specified component uuid.
+ * Counts entries in the queue with the specified status for each specified main component uuid.
*
- * The returned map doesn't contain any entry for component uuid for which there is no entry in the queue (ie.
+ * The returned map doesn't contain any entry for main component uuids for which there is no entry in the queue (ie.
* all entries have a value >= 0).
*/
- public Map<String, Integer> countByStatusAndComponentUuids(DbSession dbSession, CeQueueDto.Status status, Set<String> componentUuids) {
- if (componentUuids.isEmpty()) {
+ public Map<String, Integer> countByStatusAndMainComponentUuids(DbSession dbSession, CeQueueDto.Status status, Set<String> projectUuids) {
+ if (projectUuids.isEmpty()) {
return emptyMap();
}
ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder();
executeLargeUpdates(
- componentUuids,
+ projectUuids,
uuids -> {
- List<QueueCount> i = mapper(dbSession).countByStatusAndComponentUuids(status, componentUuids);
- i.forEach(o -> builder.put(o.getComponentUuid(), o.getTotal()));
+ List<QueueCount> i = mapper(dbSession).countByStatusAndMainComponentUuids(status, projectUuids);
+ i.forEach(o -> builder.put(o.getMainComponentUuid(), o.getTotal()));
});
return builder.build();
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDto.java
index b31c0bece83..32362261c14 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDto.java
@@ -21,8 +21,11 @@ package org.sonar.db.ce;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.sonar.db.component.ComponentDto;
+import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
public class CeQueueDto {
@@ -32,7 +35,21 @@ public class CeQueueDto {
private String uuid;
private String taskType;
+ /**
+ * Can be {@code null} when task is not associated to any data in table PROJECTS, but must always be non {@code null}
+ * at the same time as {@link #mainComponentUuid}.
+ * <p>
+ * The component uuid of a any component (project or not) is its own UUID.
+ */
private String componentUuid;
+ /**
+ * Can be {@code null} when task is not associated to any data in table PROJECTS, but must always be non {@code null}
+ * at the same time as {@link #componentUuid}.
+ * <p>
+ * The main component uuid of the main branch of project is its own UUID. For other branches of a project, it is the
+ * project UUID of the main branch of that project ({@link ComponentDto#getMainBranchProjectUuid()}).
+ */
+ private String mainComponentUuid;
private Status status;
private String submitterUuid;
/**
@@ -48,22 +65,52 @@ public class CeQueueDto {
}
public CeQueueDto setUuid(String s) {
- checkArgument(s.length() <= 40, "Value of UUID is too long: %s", s);
+ checkUuid(s, "UUID");
this.uuid = s;
return this;
}
+ /**
+ * Helper methods which sets both {@link #componentUuid} and {@link #mainComponentUuid} from the specified
+ * {@link ComponentDto}.
+ */
+ public CeQueueDto setComponent(@Nullable ComponentDto component) {
+ if (component == null) {
+ this.componentUuid = null;
+ this.mainComponentUuid = null;
+ } else {
+ this.componentUuid = requireNonNull(component.uuid());
+ this.mainComponentUuid = firstNonNull(component.getMainBranchProjectUuid(), component.uuid());
+ }
+ return this;
+ }
+
@CheckForNull
public String getComponentUuid() {
return componentUuid;
}
public CeQueueDto setComponentUuid(@Nullable String s) {
- checkArgument(s == null || s.length() <= 40, "Value of component UUID is too long: %s", s);
+ checkUuid(s, "COMPONENT_UUID");
this.componentUuid = s;
return this;
}
+ @CheckForNull
+ public String getMainComponentUuid() {
+ return mainComponentUuid;
+ }
+
+ public CeQueueDto setMainComponentUuid(@Nullable String s) {
+ checkUuid(s, "MAIN_COMPONENT_UUID");
+ this.mainComponentUuid = s;
+ return this;
+ }
+
+ private static void checkUuid(@Nullable String s, String columnName) {
+ checkArgument(s == null || s.length() <= 40, "Value is too long for column CE_QUEUE.%s: %s", columnName, s);
+ }
+
public Status getStatus() {
return status;
}
@@ -101,8 +148,9 @@ public class CeQueueDto {
/**
* Accessed by MyBatis through reflexion. Field is otherwise read-only.
*/
- private void setWorkerUuid(@Nullable String workerUuid) {
+ protected CeQueueDto setWorkerUuid(@Nullable String workerUuid) {
this.workerUuid = workerUuid;
+ return this;
}
@CheckForNull
@@ -113,8 +161,9 @@ public class CeQueueDto {
/**
* Accessed by MyBatis through reflexion. Field is otherwise read-only.
*/
- private void setStartedAt(@Nullable Long l) {
+ protected CeQueueDto setStartedAt(@Nullable Long l) {
this.startedAt = l;
+ return this;
}
public long getCreatedAt() {
@@ -141,6 +190,7 @@ public class CeQueueDto {
"uuid='" + uuid + '\'' +
", taskType='" + taskType + '\'' +
", componentUuid='" + componentUuid + '\'' +
+ ", mainComponentUuid='" + mainComponentUuid + '\'' +
", status=" + status +
", submitterLogin='" + submitterUuid + '\'' +
", workerUuid='" + workerUuid + '\'' +
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java
index 190d1f5818d..3ee7f9ebeb0 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java
@@ -29,7 +29,7 @@ import org.sonar.db.Pagination;
public interface CeQueueMapper {
- List<CeQueueDto> selectByComponentUuid(@Param("componentUuid") String componentUuid);
+ List<CeQueueDto> selectByMainComponentUuid(@Param("mainComponentUuid") String mainComponentUuid);
List<CeQueueDto> selectAllInAscOrder();
@@ -62,9 +62,9 @@ public interface CeQueueMapper {
*/
void resetAllInProgressTasks(@Param("updatedAt") long updatedAt);
- int countByStatusAndComponentUuid(@Param("status") CeQueueDto.Status status, @Nullable @Param("componentUuid") String componentUuid);
+ int countByStatusAndMainComponentUuid(@Param("status") CeQueueDto.Status status, @Nullable @Param("mainComponentUuid") String mainComponentUuid);
- List<QueueCount> countByStatusAndComponentUuids(@Param("status") CeQueueDto.Status status, @Param("componentUuids") Set<String> componentUuids);
+ List<QueueCount> countByStatusAndMainComponentUuids(@Param("status") CeQueueDto.Status status, @Param("mainComponentUuids") Set<String> mainComponentUuids);
void insert(CeQueueDto dto);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskQuery.java
index f559bfa56f8..99e80085838 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskQuery.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskQuery.java
@@ -36,31 +36,31 @@ public class CeTaskQuery {
private boolean onlyCurrents = false;
// SONAR-7681 a public implementation of List must be used in MyBatis - potential concurrency exceptions otherwise
- private ArrayList<String> componentUuids;
+ private ArrayList<String> mainComponentUuids;
private ArrayList<String> statuses;
private String type;
private Long minSubmittedAt;
private Long maxExecutedAt;
@CheckForNull
- public List<String> getComponentUuids() {
- return componentUuids;
+ public List<String> getMainComponentUuids() {
+ return mainComponentUuids;
}
- public CeTaskQuery setComponentUuids(@Nullable List<String> l) {
- this.componentUuids = l == null ? null : newArrayList(l);
+ public CeTaskQuery setMainComponentUuids(@Nullable List<String> l) {
+ this.mainComponentUuids = l == null ? null : newArrayList(l);
return this;
}
- public boolean isShortCircuitedByComponentUuids() {
- return componentUuids != null && (componentUuids.isEmpty() || componentUuids.size() > MAX_COMPONENT_UUIDS);
+ public boolean isShortCircuitedByMainComponentUuids() {
+ return mainComponentUuids != null && (mainComponentUuids.isEmpty() || mainComponentUuids.size() > MAX_COMPONENT_UUIDS);
}
- public CeTaskQuery setComponentUuid(@Nullable String s) {
+ public CeTaskQuery setMainComponentUuid(@Nullable String s) {
if (s == null) {
- this.componentUuids = null;
+ this.mainComponentUuids = null;
} else {
- this.componentUuids = newArrayList(s);
+ this.mainComponentUuids = newArrayList(s);
}
return this;
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/QueueCount.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/QueueCount.java
index 5b948c38df9..f73c9699ca8 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/QueueCount.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/QueueCount.java
@@ -21,11 +21,11 @@ package org.sonar.db.ce;
public class QueueCount {
// set by reflection by MyBatis
- private String componentUuid;
+ private String mainComponentUuid;
private int total;
- public String getComponentUuid() {
- return componentUuid;
+ public String getMainComponentUuid() {
+ return mainComponentUuid;
}
public int getTotal() {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
index 413ddf5dfe0..411a98dd614 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
@@ -112,7 +112,7 @@ public class ComponentDao implements Dao {
/**
* @throws IllegalArgumentException if parameter query#getComponentIds() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
* @throws IllegalArgumentException if parameter query#getComponentKeys() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
- * @throws IllegalArgumentException if parameter query#getComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ * @throws IllegalArgumentException if parameter query#getMainComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
*/
public List<ComponentDto> selectByQuery(DbSession dbSession, String organizationUuid, ComponentQuery query, int offset, int limit) {
requireNonNull(organizationUuid, "organizationUuid can't be null");
@@ -130,7 +130,7 @@ public class ComponentDao implements Dao {
/**
* @throws IllegalArgumentException if parameter query#getComponentIds() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
* @throws IllegalArgumentException if parameter query#getComponentKeys() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
- * @throws IllegalArgumentException if parameter query#getComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ * @throws IllegalArgumentException if parameter query#getMainComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
*/
public int countByQuery(DbSession session, String organizationUuid, ComponentQuery query) {
requireNonNull(organizationUuid, "organizationUuid can't be null");
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
index 6c4779f6e93..69c6504dead 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
@@ -19,7 +19,8 @@
ca.id,
ca.uuid,
ca.task_type as taskType,
- ca.component_uuid as componentUuid,
+ ca.tmp_component_uuid as componentUuid,
+ ca.tmp_main_component_uuid as mainComponentUuid,
ca.analysis_uuid as analysisUuid,
ca.status as status,
ca.submitter_uuid as submitterUuid,
@@ -29,8 +30,10 @@
ca.executed_at as executedAt,
ca.created_at as createdAt,
ca.updated_at as updatedAt,
- ca.is_last as isLast,
- ca.is_last_key as isLastKey,
+ ca.tmp_is_last as isLast,
+ ca.tmp_is_last_key as isLastKey,
+ ca.tmp_main_is_last as mainIsLast,
+ ca.tmp_main_is_last_key as mainIsLastKey,
ca.execution_time_ms as executionTimeMs,
ca.error_message as errorMessage,
ca.error_type as errorType,
@@ -83,11 +86,11 @@
left outer join ce_scanner_context csc on csc.task_uuid = ca.uuid
<where>
<if test="query.onlyCurrents">
- and ca.is_last=${_true}
+ and ca.tmp_main_is_last=${_true}
</if>
- <if test="query.componentUuids != null and query.componentUuids.size()>0">
- and ca.component_uuid in
- <foreach collection="query.componentUuids" open="(" close=")" item="cUuid" separator=",">
+ <if test="query.mainComponentUuids != null and query.mainComponentUuids.size()>0">
+ and ca.tmp_main_component_uuid in
+ <foreach collection="query.mainComponentUuids" open="(" close=")" item="cUuid" separator=",">
#{cUuid,jdbcType=VARCHAR}
</foreach>
</if>
@@ -118,26 +121,32 @@
ca.created_at &lt; #{beforeDate,jdbcType=BIGINT}
</select>
- <select id="countLastByStatusAndComponentUuid" resultType="int">
+ <select id="countLastByStatusAndMainComponentUuid" resultType="int">
select
count(1)
from
ce_activity
where
- status=#{status,jdbcType=VARCHAR}
- and is_last=${_true}
- <if test="componentUuid!=null">
- and component_uuid=#{componentUuid,jdbcType=VARCHAR}
+ tmp_main_is_last=${_true}
+ and status=#{status,jdbcType=VARCHAR}
+ <if test="mainComponentUuid!=null">
+ and tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
</if>
</select>
<insert id="insert" parameterType="org.sonar.db.ce.CeActivityDto" useGeneratedKeys="false">
insert into ce_activity (
uuid,
+ tmp_component_uuid,
+ tmp_main_component_uuid,
component_uuid,
analysis_uuid,
status,
task_type,
+ tmp_is_last,
+ tmp_is_last_key,
+ tmp_main_is_last,
+ tmp_main_is_last_key,
is_last,
is_last_key,
submitter_uuid,
@@ -156,11 +165,17 @@
values (
#{uuid,jdbcType=VARCHAR},
#{componentUuid,jdbcType=VARCHAR},
+ #{mainComponentUuid,jdbcType=VARCHAR},
+ #{mainComponentUuid,jdbcType=VARCHAR},
#{analysisUuid,jdbcType=VARCHAR},
#{status,jdbcType=VARCHAR},
#{taskType,jdbcType=VARCHAR},
#{isLast,jdbcType=BOOLEAN},
#{isLastKey,jdbcType=VARCHAR},
+ #{mainIsLast,jdbcType=BOOLEAN},
+ #{mainIsLastKey,jdbcType=VARCHAR},
+ #{mainIsLast,jdbcType=BOOLEAN},
+ #{mainIsLastKey,jdbcType=VARCHAR},
#{submitterUuid,jdbcType=VARCHAR},
#{submittedAt,jdbcType=BIGINT},
#{workerUuid,jdbcType=VARCHAR},
@@ -176,11 +191,16 @@
)
</insert>
- <update id="updateIsLastToFalseForLastKey" parameterType="map">
- update ce_activity
- set is_last=${_false},
- updated_at=#{updatedAt,jdbcType=BIGINT}
- where is_last=${_true} and is_last_key=#{isLastKey,jdbcType=VARCHAR}
+ <update id="clearIsLast" parameterType="map">
+ update ce_activity set
+ tmp_is_last=${_false},
+ tmp_main_is_last=${_false},
+ is_last=${_false},
+ updated_at=#{updatedAt,jdbcType=BIGINT}
+ where
+ (tmp_is_last=${_true} and tmp_is_last_key=#{isLastKey,jdbcType=VARCHAR})
+ or
+ (tmp_main_is_last=${_true} and tmp_main_is_last_key=#{mainIsLastKey,jdbcType=VARCHAR})
</update>
<delete id="deleteByUuids" parameterType="string">
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml
index de6508b2133..6eab90a67f8 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml
@@ -6,7 +6,8 @@
<sql id="columns">
cq.uuid,
cq.task_type as taskType,
- cq.component_uuid as componentUuid,
+ cq.tmp_component_uuid as componentUuid,
+ cq.tmp_main_component_uuid as mainComponentUuid,
cq.status as status,
cq.submitter_uuid as submitterUuid,
cq.worker_uuid as workerUuid,
@@ -36,31 +37,31 @@
cq.uuid=#{uuid,jdbcType=VARCHAR}
</select>
- <select id="countByStatusAndComponentUuid" parameterType="map" resultType="int">
+ <select id="countByStatusAndMainComponentUuid" parameterType="map" resultType="int">
select
count(1)
from
ce_queue
where
status=#{status,jdbcType=VARCHAR}
- <if test="componentUuid!=null">
- and component_uuid=#{componentUuid,jdbcType=VARCHAR}
+ <if test="mainComponentUuid!=null">
+ and tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
</if>
</select>
- <select id="countByStatusAndComponentUuids" resultType="org.sonar.db.ce.QueueCount">
+ <select id="countByStatusAndMainComponentUuids" resultType="org.sonar.db.ce.QueueCount">
select
- component_uuid as componentUuid,
+ tmp_main_component_uuid as mainComponentUuid,
count(1) as total
from
ce_queue
where
status=#{status,jdbcType=VARCHAR}
- and component_uuid in
- <foreach collection="componentUuids" open="(" close=")" item="cUuid" separator=",">
- #{cUuid,jdbcType=VARCHAR}
+ and tmp_main_component_uuid in
+ <foreach collection="mainComponentUuids" open="(" close=")" item="mainComponentUuid" separator=",">
+ #{mainComponentUuid,jdbcType=VARCHAR}
</foreach>
- group by component_uuid
+ group by tmp_main_component_uuid
</select>
<select id="countAll" resultType="int">
@@ -70,13 +71,13 @@
ce_queue
</select>
- <select id="selectByComponentUuid" parameterType="String" resultType="org.sonar.db.ce.CeQueueDto">
+ <select id="selectByMainComponentUuid" parameterType="String" resultType="org.sonar.db.ce.CeQueueDto">
select
<include refid="columns"/>
from
ce_queue cq
where
- cq.component_uuid=#{componentUuid,jdbcType=VARCHAR}
+ cq.tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
<include refid="orderByDateAndId"/>
</select>
@@ -105,10 +106,10 @@
from
ce_queue cq
<where>
- <if test="query.componentUuids != null and query.componentUuids.size()>0">
- and cq.component_uuid in
- <foreach collection="query.componentUuids" open="(" close=")" item="cUuid" separator=",">
- #{cUuid,jdbcType=VARCHAR}
+ <if test="query.mainComponentUuids != null and query.mainComponentUuids.size()>0">
+ and cq.tmp_main_component_uuid in
+ <foreach collection="query.mainComponentUuids" open="(" close=")" item="mainComponentUuid" separator=",">
+ #{mainComponentUuid,jdbcType=VARCHAR}
</foreach>
</if>
<if test="query.statuses != null">
@@ -176,7 +177,7 @@
from
ce_queue cq2
where
- cq.component_uuid=cq2.component_uuid
+ cq.tmp_main_component_uuid=cq2.tmp_main_component_uuid
and cq2.status &lt;&gt; 'PENDING'
)
</sql>
@@ -211,7 +212,9 @@
(
uuid,
task_type,
+ tmp_component_uuid,
component_uuid,
+ tmp_main_component_uuid,
status,
submitter_uuid,
execution_count,
@@ -222,6 +225,8 @@
#{uuid,jdbcType=VARCHAR},
#{taskType,jdbcType=VARCHAR},
#{componentUuid,jdbcType=VARCHAR},
+ #{mainComponentUuid,jdbcType=VARCHAR},
+ #{mainComponentUuid,jdbcType=VARCHAR},
#{status,jdbcType=VARCHAR},
#{submitterUuid,jdbcType=VARCHAR},
0,
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
index 5051ad485c5..1cca92d67d3 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
@@ -315,45 +315,45 @@
<delete id="deleteCeScannerContextOfCeActivityByProjectUuid">
delete from ce_scanner_context
where
- task_uuid in (select uuid from ce_activity where component_uuid=#{projectUuid,jdbcType=VARCHAR})
+ task_uuid in (select uuid from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
</delete>
<delete id="deleteCeTaskCharacteristicsOfCeActivityByProjectUuid">
delete from ce_task_characteristics
where
- task_uuid in (select uuid from ce_activity where component_uuid=#{projectUuid,jdbcType=VARCHAR})
+ task_uuid in (select uuid from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
</delete>
<delete id="deleteCeTaskInputOfCeActivityByProjectUuid">
delete from ce_task_input
where
- task_uuid in (select uuid from ce_activity where component_uuid=#{projectUuid,jdbcType=VARCHAR})
+ task_uuid in (select uuid from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
</delete>
<delete id="deleteCeActivityByProjectUuid">
- delete from ce_activity where component_uuid=#{projectUuid,jdbcType=VARCHAR}
+ delete from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR}
</delete>
<delete id="deleteCeScannerContextOfCeQueueByProjectUuid">
delete from ce_scanner_context
where
- task_uuid in (select uuid from ce_queue where component_uuid=#{projectUuid,jdbcType=VARCHAR})
+ task_uuid in (select uuid from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
</delete>
<delete id="deleteCeTaskCharacteristicsOfCeQueueByProjectUuid">
delete from ce_task_characteristics
where
- task_uuid in (select uuid from ce_queue where component_uuid=#{projectUuid,jdbcType=VARCHAR})
+ task_uuid in (select uuid from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
</delete>
<delete id="deleteCeTaskInputOfCeQueueByProjectUuid">
delete from ce_task_input
where
- task_uuid in (select uuid from ce_queue where component_uuid=#{projectUuid,jdbcType=VARCHAR})
+ task_uuid in (select uuid from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
</delete>
<delete id="deleteCeQueueByProjectUuid">
- delete from ce_queue where component_uuid=#{projectUuid,jdbcType=VARCHAR}
+ delete from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR}
</delete>
<delete id="deleteWebhooksByProjectUuid">
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
index f24e8b5a14a..ff8226a5235 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
@@ -26,6 +26,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.internal.TestSystem2;
@@ -37,6 +38,7 @@ import org.sonar.db.Pagination;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.Pagination.forPage;
import static org.sonar.db.ce.CeActivityDto.Status.FAILED;
@@ -47,6 +49,10 @@ import static org.sonar.db.ce.CeTaskTypes.REPORT;
public class CeActivityDaoTest {
+ private static final String MAINCOMPONENT_1 = randomAlphabetic(12);
+ private static final String MAINCOMPONENT_2 = randomAlphabetic(13);
+ private static final String COMPONENT_1 = randomAlphabetic(14);
+
private TestSystem2 system2 = new TestSystem2().setNow(1_450_000_000_000L);
@Rule
@@ -57,19 +63,22 @@ public class CeActivityDaoTest {
@Test
public void test_insert() {
- CeActivityDto inserted = insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
+ CeActivityDto inserted = insert("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
Optional<CeActivityDto> saved = underTest.selectByUuid(db.getSession(), "TASK_1");
assertThat(saved).isPresent();
CeActivityDto dto = saved.get();
assertThat(dto.getUuid()).isEqualTo("TASK_1");
- assertThat(dto.getComponentUuid()).isEqualTo("PROJECT_1");
+ assertThat(dto.getMainComponentUuid()).isEqualTo(MAINCOMPONENT_1);
+ assertThat(dto.getComponentUuid()).isEqualTo(COMPONENT_1);
assertThat(dto.getStatus()).isEqualTo(CeActivityDto.Status.SUCCESS);
assertThat(dto.getSubmitterUuid()).isEqualTo("submitter uuid");
assertThat(dto.getSubmittedAt()).isEqualTo(1_450_000_000_000L);
assertThat(dto.getWorkerUuid()).isEqualTo("worker uuid");
assertThat(dto.getIsLast()).isTrue();
- assertThat(dto.getIsLastKey()).isEqualTo("REPORTPROJECT_1");
+ assertThat(dto.getMainIsLast()).isTrue();
+ assertThat(dto.getIsLastKey()).isEqualTo("REPORT" + COMPONENT_1);
+ assertThat(dto.getMainIsLastKey()).isEqualTo("REPORT" + MAINCOMPONENT_1);
assertThat(dto.getCreatedAt()).isEqualTo(1_450_000_000_000L);
assertThat(dto.getStartedAt()).isEqualTo(1_500_000_000_000L);
assertThat(dto.getExecutedAt()).isEqualTo(1_500_000_000_500L);
@@ -84,7 +93,7 @@ public class CeActivityDaoTest {
@Test
public void test_insert_of_errorMessage_of_1_000_chars() {
- CeActivityDto dto = createActivityDto("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.FAILED)
+ CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
.setErrorMessage(Strings.repeat("x", 1_000));
underTest.insert(db.getSession(), dto);
@@ -95,7 +104,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, "PROJECT_1", CeActivityDto.Status.FAILED)
+ CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
.setErrorMessage(expected + "y");
underTest.insert(db.getSession(), dto);
@@ -105,7 +114,7 @@ public class CeActivityDaoTest {
@Test
public void test_insert_error_message_and_stacktrace() {
- CeActivityDto dto = createActivityDto("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.FAILED)
+ CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
.setErrorStacktrace("error stack");
underTest.insert(db.getSession(), dto);
@@ -118,7 +127,7 @@ public class CeActivityDaoTest {
@Test
public void test_insert_error_message_only() {
- CeActivityDto dto = createActivityDto("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.FAILED);
+ CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED);
underTest.insert(db.getSession(), dto);
Optional<CeActivityDto> saved = underTest.selectByUuid(db.getSession(), "TASK_1");
@@ -129,22 +138,22 @@ public class CeActivityDaoTest {
@Test
public void insert_must_set_relevant_is_last_field() {
- // only a single task on PROJECT_1 -> is_last=true
- insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
+ // only a single task on MAINCOMPONENT_1 -> is_last=true
+ insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
assertThat(underTest.selectByUuid(db.getSession(), "TASK_1").get().getIsLast()).isTrue();
- // only a single task on PROJECT_2 -> is_last=true
- insert("TASK_2", REPORT, "PROJECT_2", CeActivityDto.Status.SUCCESS);
+ // only a single task on MAINCOMPONENT_2 -> is_last=true
+ insert("TASK_2", REPORT, MAINCOMPONENT_2, CeActivityDto.Status.SUCCESS);
assertThat(underTest.selectByUuid(db.getSession(), "TASK_2").get().getIsLast()).isTrue();
- // two tasks on PROJECT_1, the most recent one is TASK_3
- insert("TASK_3", REPORT, "PROJECT_1", FAILED);
+ // two tasks on MAINCOMPONENT_1, the most recent one is TASK_3
+ insert("TASK_3", REPORT, MAINCOMPONENT_1, FAILED);
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();
// inserting a cancelled task does not change the last task
- insert("TASK_4", REPORT, "PROJECT_1", CeActivityDto.Status.CANCELED);
+ insert("TASK_4", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.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();
@@ -153,9 +162,9 @@ public class CeActivityDaoTest {
@Test
public void test_selectByQuery() {
- insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
- insert("TASK_2", REPORT, "PROJECT_1", FAILED);
- insert("TASK_3", REPORT, "PROJECT_2", CeActivityDto.Status.SUCCESS);
+ insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.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);
// no filters
@@ -164,7 +173,7 @@ public class CeActivityDaoTest {
assertThat(dtos).extracting("uuid").containsExactly("TASK_4", "TASK_3", "TASK_2", "TASK_1");
// select by component uuid
- query = new CeTaskQuery().setComponentUuid("PROJECT_1");
+ query = new CeTaskQuery().setMainComponentUuid(MAINCOMPONENT_1);
dtos = underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100));
assertThat(dtos).extracting("uuid").containsExactly("TASK_2", "TASK_1");
@@ -182,17 +191,17 @@ public class CeActivityDaoTest {
assertThat(dtos).extracting("uuid").containsExactly("TASK_4");
// select by multiple conditions
- query = new CeTaskQuery().setType(REPORT).setOnlyCurrents(true).setComponentUuid("PROJECT_1");
+ query = new CeTaskQuery().setType(REPORT).setOnlyCurrents(true).setMainComponentUuid(MAINCOMPONENT_1);
dtos = underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(100));
assertThat(dtos).extracting("uuid").containsExactly("TASK_2");
}
@Test
public void selectByQuery_does_not_populate_errorStacktrace_field() {
- insert("TASK_1", REPORT, "PROJECT_1", FAILED);
- underTest.insert(db.getSession(), createActivityDto("TASK_2", REPORT, "PROJECT_1", FAILED).setErrorStacktrace("some stack"));
+ insert("TASK_1", REPORT, MAINCOMPONENT_1, FAILED);
+ underTest.insert(db.getSession(), createActivityDto("TASK_2", REPORT, COMPONENT_1, MAINCOMPONENT_1, FAILED).setErrorStacktrace("some stack"));
- List<CeActivityDto> dtos = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setComponentUuid("PROJECT_1"), forPage(1).andSize(100));
+ List<CeActivityDto> dtos = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setMainComponentUuid(MAINCOMPONENT_1), forPage(1).andSize(100));
assertThat(dtos)
.hasSize(2)
@@ -201,21 +210,21 @@ public class CeActivityDaoTest {
@Test
public void selectByQuery_populates_hasScannerContext_flag() {
- insert("TASK_1", REPORT, "PROJECT_1", SUCCESS);
- CeActivityDto dto2 = insert("TASK_2", REPORT, "PROJECT_2", SUCCESS);
+ insert("TASK_1", REPORT, MAINCOMPONENT_1, SUCCESS);
+ CeActivityDto dto2 = insert("TASK_2", REPORT, MAINCOMPONENT_2, SUCCESS);
insertScannerContext(dto2.getUuid());
- CeActivityDto dto = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setComponentUuid("PROJECT_1"), forPage(1).andSize(100)).iterator().next();
+ CeActivityDto dto = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setMainComponentUuid(MAINCOMPONENT_1), forPage(1).andSize(100)).iterator().next();
assertThat(dto.isHasScannerContext()).isFalse();
- dto = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setComponentUuid("PROJECT_2"), forPage(1).andSize(100)).iterator().next();
+ dto = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setMainComponentUuid(MAINCOMPONENT_2), forPage(1).andSize(100)).iterator().next();
assertThat(dto.isHasScannerContext()).isTrue();
}
@Test
public void selectByQuery_is_paginated_and_return_results_sorted_from_last_to_first() {
- insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
- insert("TASK_2", REPORT, "PROJECT_1", CeActivityDto.Status.FAILED);
- insert("TASK_3", REPORT, "PROJECT_2", CeActivityDto.Status.SUCCESS);
+ 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);
assertThat(selectPageOfUuids(forPage(1).andSize(1))).containsExactly("TASK_4");
@@ -229,10 +238,10 @@ public class CeActivityDaoTest {
@Test
public void selectByQuery_no_results_if_shortcircuited_by_component_uuids() {
- insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
+ insert("TASK_1", REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
CeTaskQuery query = new CeTaskQuery();
- query.setComponentUuids(Collections.emptyList());
+ query.setMainComponentUuids(Collections.emptyList());
assertThat(underTest.selectByQuery(db.getSession(), query, forPage(1).andSize(1))).isEmpty();
}
@@ -291,8 +300,8 @@ public class CeActivityDaoTest {
@Test
public void selectOlderThan_does_not_populate_errorStacktrace() {
- insert("TASK_1", REPORT, "PROJECT_1", FAILED);
- underTest.insert(db.getSession(), createActivityDto("TASK_2", REPORT, "PROJECT_1", FAILED).setErrorStacktrace("some stack"));
+ insert("TASK_1", REPORT, MAINCOMPONENT_1, FAILED);
+ underTest.insert(db.getSession(), createActivityDto("TASK_2", REPORT, COMPONENT_1, MAINCOMPONENT_1, FAILED).setErrorStacktrace("some stack"));
List<CeActivityDto> dtos = underTest.selectOlderThan(db.getSession(), system2.now() + 1_000_000L);
@@ -303,9 +312,9 @@ public class CeActivityDaoTest {
@Test
public void deleteByUuids() {
- insert("TASK_1", "REPORT", "COMPONENT1", CeActivityDto.Status.SUCCESS);
- insert("TASK_2", "REPORT", "COMPONENT1", CeActivityDto.Status.SUCCESS);
- insert("TASK_3", "REPORT", "COMPONENT1", CeActivityDto.Status.SUCCESS);
+ 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);
underTest.deleteByUuids(db.getSession(), ImmutableSet.of("TASK_1", "TASK_3"));
assertThat(underTest.selectByUuid(db.getSession(), "TASK_1").isPresent()).isFalse();
@@ -315,7 +324,7 @@ public class CeActivityDaoTest {
@Test
public void deleteByUuids_does_nothing_if_uuid_does_not_exist() {
- insert("TASK_1", "REPORT", "COMPONENT1", CeActivityDto.Status.SUCCESS);
+ insert("TASK_1", "REPORT", MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
// must not fail
underTest.deleteByUuids(db.getSession(), singleton("TASK_2"));
@@ -324,33 +333,38 @@ public class CeActivityDaoTest {
}
@Test
- public void count_last_by_status_and_component_uuid() {
- insert("TASK_1", CeTaskTypes.REPORT, "COMPONENT1", CeActivityDto.Status.SUCCESS);
+ public void count_last_by_status_and_main_component_uuid() {
+ insert("TASK_1", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
// component 2
- insert("TASK_2", CeTaskTypes.REPORT, "COMPONENT2", CeActivityDto.Status.SUCCESS);
+ insert("TASK_2", CeTaskTypes.REPORT, MAINCOMPONENT_2, CeActivityDto.Status.SUCCESS);
// status failed
- insert("TASK_3", CeTaskTypes.REPORT, "COMPONENT1", CeActivityDto.Status.FAILED);
+ insert("TASK_3", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.FAILED);
// status canceled
- insert("TASK_4", CeTaskTypes.REPORT, "COMPONENT1", CeActivityDto.Status.CANCELED);
- insert("TASK_5", CeTaskTypes.REPORT, "COMPONENT1", CeActivityDto.Status.SUCCESS);
+ insert("TASK_4", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.CANCELED);
+ insert("TASK_5", CeTaskTypes.REPORT, MAINCOMPONENT_1, CeActivityDto.Status.SUCCESS);
db.commit();
- assertThat(underTest.countLastByStatusAndComponentUuid(dbSession, SUCCESS, "COMPONENT1")).isEqualTo(1);
- assertThat(underTest.countLastByStatusAndComponentUuid(dbSession, SUCCESS, null)).isEqualTo(2);
+ assertThat(underTest.countLastByStatusAndMainComponentUuid(dbSession, SUCCESS, MAINCOMPONENT_1)).isEqualTo(1);
+ assertThat(underTest.countLastByStatusAndMainComponentUuid(dbSession, SUCCESS, null)).isEqualTo(2);
+ }
+
+ private CeActivityDto insert(String uuid, String type, @Nullable String mainComponentUuid, CeActivityDto.Status status) {
+ return insert(uuid, type, mainComponentUuid, mainComponentUuid, status);
}
- private CeActivityDto insert(String uuid, String type, String componentUuid, CeActivityDto.Status status) {
- CeActivityDto dto = createActivityDto(uuid, type, componentUuid, status);
+ private CeActivityDto insert(String uuid, String type, String componentUuid, @Nullable String mainComponentUuid, CeActivityDto.Status status) {
+ CeActivityDto dto = createActivityDto(uuid, type, componentUuid, mainComponentUuid, status);
underTest.insert(db.getSession(), dto);
return dto;
}
- private CeActivityDto createActivityDto(String uuid, String type, String componentUuid, CeActivityDto.Status status) {
+ private CeActivityDto createActivityDto(String uuid, String type, @Nullable String componentUuid, @Nullable String mainComponentUuid, CeActivityDto.Status status) {
CeQueueDto creating = new CeQueueDto();
creating.setUuid(uuid);
creating.setStatus(PENDING);
creating.setTaskType(type);
creating.setComponentUuid(componentUuid);
+ creating.setMainComponentUuid(mainComponentUuid);
creating.setSubmitterUuid("submitter uuid");
creating.setCreatedAt(1_300_000_000_000L);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java
index d5991d9a70d..1035ea80d9f 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java
@@ -23,7 +23,9 @@ import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Random;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
@@ -31,9 +33,80 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(DataProviderRunner.class)
public class CeActivityDtoTest {
+ private static final String STR_40_CHARS = "0123456789012345678901234567890123456789";
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
private CeActivityDto underTest = new CeActivityDto();
@Test
+ public void constructor_from_CeQueueDto_populates_fields() {
+ long now = new Random().nextLong();
+ CeQueueDto ceQueueDto = new CeQueueDto()
+ .setUuid(randomAlphanumeric(10))
+ .setTaskType(randomAlphanumeric(11))
+ .setComponentUuid(randomAlphanumeric(12))
+ .setMainComponentUuid(randomAlphanumeric(13))
+ .setSubmitterUuid(randomAlphanumeric(14))
+ .setWorkerUuid(randomAlphanumeric(15))
+ .setCreatedAt(now + 9_999)
+ .setStartedAt(now + 865);
+
+ CeActivityDto underTest = new CeActivityDto(ceQueueDto);
+
+ assertThat(underTest.getUuid()).isEqualTo(ceQueueDto.getUuid());
+ assertThat(underTest.getTaskType()).isEqualTo(ceQueueDto.getTaskType());
+ assertThat(underTest.getComponentUuid()).isEqualTo(ceQueueDto.getComponentUuid());
+ assertThat(underTest.getMainComponentUuid()).isEqualTo(ceQueueDto.getMainComponentUuid());
+ assertThat(underTest.getIsLastKey()).isEqualTo(ceQueueDto.getTaskType() + ceQueueDto.getComponentUuid());
+ assertThat(underTest.getIsLast()).isFalse();
+ assertThat(underTest.getMainIsLastKey()).isEqualTo(ceQueueDto.getTaskType() + ceQueueDto.getMainComponentUuid());
+ assertThat(underTest.getMainIsLast()).isFalse();
+ assertThat(underTest.getSubmitterUuid()).isEqualTo(ceQueueDto.getSubmitterUuid());
+ assertThat(underTest.getWorkerUuid()).isEqualTo(ceQueueDto.getWorkerUuid());
+ assertThat(underTest.getSubmittedAt()).isEqualTo(ceQueueDto.getCreatedAt());
+ assertThat(underTest.getStartedAt()).isEqualTo(ceQueueDto.getStartedAt());
+ assertThat(underTest.getStatus()).isNull();
+ }
+
+ @Test
+ public void setComponentUuid_accepts_null_empty_and_string_40_chars_or_less() {
+ underTest.setComponentUuid(null);
+ underTest.setComponentUuid("");
+ underTest.setComponentUuid("bar");
+ underTest.setComponentUuid(STR_40_CHARS);
+ }
+
+ @Test
+ public void setComponentUuid_throws_IAE_if_value_is_41_chars() {
+ String str_41_chars = STR_40_CHARS + "a";
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Value is too long for column CE_ACTIVITY.COMPONENT_UUID: " + str_41_chars);
+
+ underTest.setComponentUuid(str_41_chars);
+ }
+
+ @Test
+ public void setMainComponentUuid_accepts_null_empty_and_string_40_chars_or_less() {
+ underTest.setMainComponentUuid(null);
+ underTest.setMainComponentUuid("");
+ underTest.setMainComponentUuid("bar");
+ underTest.setMainComponentUuid(STR_40_CHARS);
+ }
+
+ @Test
+ public void setMainComponentUuid_throws_IAE_if_value_is_41_chars() {
+ String str_41_chars = STR_40_CHARS + "a";
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Value is too long for column CE_ACTIVITY.MAIN_COMPONENT_UUID: " + str_41_chars);
+
+ underTest.setMainComponentUuid(str_41_chars);
+ }
+
+ @Test
@UseDataProvider("stringsWithChar0")
public void setStacktrace_filters_out_char_zero(String withChar0, String expected) {
underTest.setErrorStacktrace(withChar0);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java
index eaab9cb25ef..23907c739ad 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java
@@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Random;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.junit.Rule;
@@ -54,8 +55,8 @@ public class CeQueueDaoTest {
private static final long INIT_TIME = 1_450_000_000_000L;
private static final String TASK_UUID_1 = "TASK_1";
private static final String TASK_UUID_2 = "TASK_2";
- private static final String COMPONENT_UUID_1 = "PROJECT_1";
- private static final String COMPONENT_UUID_2 = "PROJECT_2";
+ private static final String MAIN_COMPONENT_UUID_1 = "PROJECT_1";
+ private static final String MAIN_COMPONENT_UUID_2 = "PROJECT_2";
private static final String TASK_UUID_3 = "TASK_3";
private static final String SELECT_QUEUE_UUID_AND_STATUS_QUERY = "select uuid,status from ce_queue";
private static final String SUBMITTER_LOGIN = "submitter uuid";
@@ -79,7 +80,7 @@ public class CeQueueDaoTest {
long now = 1_334_333L;
CeQueueDto dto = new CeQueueDto()
.setTaskType(CeTaskTypes.REPORT)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(PENDING)
.setSubmitterUuid(SUBMITTER_LOGIN);
@@ -99,7 +100,7 @@ public class CeQueueDaoTest {
CeQueueDto saved = underTest.selectByUuid(db.getSession(), uuid).get();
assertThat(saved.getUuid()).isEqualTo(uuid);
assertThat(saved.getTaskType()).isEqualTo(CeTaskTypes.REPORT);
- assertThat(saved.getComponentUuid()).isEqualTo(COMPONENT_UUID_1);
+ assertThat(saved.getComponentUuid()).isEqualTo(MAIN_COMPONENT_UUID_1);
assertThat(saved.getStatus()).isEqualTo(PENDING);
assertThat(saved.getSubmitterUuid()).isEqualTo(SUBMITTER_LOGIN);
assertThat(saved.getWorkerUuid()).isNull();
@@ -110,7 +111,7 @@ public class CeQueueDaoTest {
CeQueueDto saved = underTest.selectByUuid(db.getSession(), uuid4).get();
assertThat(saved.getUuid()).isEqualTo(uuid4);
assertThat(saved.getTaskType()).isEqualTo(CeTaskTypes.REPORT);
- assertThat(saved.getComponentUuid()).isEqualTo(COMPONENT_UUID_1);
+ assertThat(saved.getComponentUuid()).isEqualTo(MAIN_COMPONENT_UUID_1);
assertThat(saved.getStatus()).isEqualTo(PENDING);
assertThat(saved.getSubmitterUuid()).isEqualTo(SUBMITTER_LOGIN);
assertThat(saved.getWorkerUuid()).isNull();
@@ -121,13 +122,14 @@ public class CeQueueDaoTest {
@Test
public void test_selectByUuid() {
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
+ CeQueueDto ceQueueDto = insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
assertThat(underTest.selectByUuid(db.getSession(), "TASK_UNKNOWN").isPresent()).isFalse();
CeQueueDto saved = underTest.selectByUuid(db.getSession(), TASK_UUID_1).get();
assertThat(saved.getUuid()).isEqualTo(TASK_UUID_1);
assertThat(saved.getTaskType()).isEqualTo(CeTaskTypes.REPORT);
- assertThat(saved.getComponentUuid()).isEqualTo(COMPONENT_UUID_1);
+ assertThat(saved.getMainComponentUuid()).isEqualTo(MAIN_COMPONENT_UUID_1);
+ assertThat(saved.getComponentUuid()).isEqualTo(ceQueueDto.getComponentUuid());
assertThat(saved.getStatus()).isEqualTo(PENDING);
assertThat(saved.getSubmitterUuid()).isEqualTo("henri");
assertThat(saved.getWorkerUuid()).isNull();
@@ -137,20 +139,20 @@ public class CeQueueDaoTest {
}
@Test
- public void test_selectByComponentUuid() {
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
- insertPending(TASK_UUID_2, COMPONENT_UUID_1);
+ public void test_selectByMainComponentUuid() {
+ insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
+ insertPending(TASK_UUID_2, MAIN_COMPONENT_UUID_1);
insertPending(TASK_UUID_3, "PROJECT_2");
- assertThat(underTest.selectByComponentUuid(db.getSession(), "UNKNOWN")).isEmpty();
- assertThat(underTest.selectByComponentUuid(db.getSession(), COMPONENT_UUID_1)).extracting("uuid").containsOnly(TASK_UUID_1, TASK_UUID_2);
- assertThat(underTest.selectByComponentUuid(db.getSession(), "PROJECT_2")).extracting("uuid").containsOnly(TASK_UUID_3);
+ assertThat(underTest.selectByMainComponentUuid(db.getSession(), "UNKNOWN")).isEmpty();
+ assertThat(underTest.selectByMainComponentUuid(db.getSession(), MAIN_COMPONENT_UUID_1)).extracting("uuid").containsOnly(TASK_UUID_1, TASK_UUID_2);
+ assertThat(underTest.selectByMainComponentUuid(db.getSession(), "PROJECT_2")).extracting("uuid").containsOnly(TASK_UUID_3);
}
@Test
public void test_selectAllInAscOrder() {
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
- insertPending(TASK_UUID_2, COMPONENT_UUID_1);
+ insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
+ insertPending(TASK_UUID_2, MAIN_COMPONENT_UUID_1);
insertPending(TASK_UUID_3, "PROJECT_2");
assertThat(underTest.selectAllInAscOrder(db.getSession())).extracting("uuid").containsOnly(TASK_UUID_1, TASK_UUID_2, TASK_UUID_3);
@@ -186,8 +188,8 @@ public class CeQueueDaoTest {
@Test
public void test_delete() {
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
- insertPending(TASK_UUID_2, COMPONENT_UUID_1);
+ insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
+ insertPending(TASK_UUID_2, MAIN_COMPONENT_UUID_1);
int deletedCount = underTest.deleteByUuid(db.getSession(), "UNKNOWN");
assertThat(deletedCount).isEqualTo(0);
@@ -204,8 +206,8 @@ public class CeQueueDaoTest {
@Test
public void test_delete_with_expected_status() {
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
- insertInProgress(TASK_UUID_2, COMPONENT_UUID_1);
+ insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
+ insertInProgress(TASK_UUID_2, MAIN_COMPONENT_UUID_1);
int deletedCount = underTest.deleteByUuid(db.getSession(), "UNKNOWN", null);
assertThat(deletedCount).isEqualTo(0);
@@ -340,15 +342,15 @@ public class CeQueueDaoTest {
assertThat(underTest.peek(db.getSession(), WORKER_UUID_1).isPresent()).isFalse();
// not pending, but in progress
- makeInProgress(WORKER_UUID_1, 2_232_222L, insertPending(TASK_UUID_1, COMPONENT_UUID_1));
+ makeInProgress(WORKER_UUID_1, 2_232_222L, insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1));
assertThat(underTest.peek(db.getSession(), WORKER_UUID_1).isPresent()).isFalse();
}
@Test
public void peek_oldest_pending() {
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
+ insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
system2.setNow(INIT_TIME + 3_000_000);
- insertPending(TASK_UUID_2, COMPONENT_UUID_2);
+ insertPending(TASK_UUID_2, MAIN_COMPONENT_UUID_2);
assertThat(db.countRowsOfTable("ce_queue")).isEqualTo(2);
verifyCeQueueStatuses(TASK_UUID_1, PENDING, TASK_UUID_2, PENDING);
@@ -374,15 +376,16 @@ public class CeQueueDaoTest {
}
@Test
- public void do_not_peek_multiple_tasks_on_same_project_at_the_same_time() {
+ public void do_not_peek_multiple_tasks_on_same_main_component_at_the_same_time() {
// two pending tasks on the same project
- insertPending(TASK_UUID_1, COMPONENT_UUID_1);
+ insertPending(TASK_UUID_1, MAIN_COMPONENT_UUID_1);
system2.setNow(INIT_TIME + 3_000_000);
- insertPending(TASK_UUID_2, COMPONENT_UUID_1);
+ insertPending(TASK_UUID_2, MAIN_COMPONENT_UUID_1);
Optional<CeQueueDto> peek = underTest.peek(db.getSession(), WORKER_UUID_1);
assertThat(peek).isPresent();
assertThat(peek.get().getUuid()).isEqualTo(TASK_UUID_1);
+ assertThat(peek.get().getMainComponentUuid()).isEqualTo(MAIN_COMPONENT_UUID_1);
assertThat(peek.get().getWorkerUuid()).isEqualTo(WORKER_UUID_1);
verifyCeQueueStatuses(TASK_UUID_1, IN_PROGRESS, TASK_UUID_2, PENDING);
@@ -401,42 +404,42 @@ public class CeQueueDaoTest {
public void select_by_query() {
// task status not in query
insertPending(newCeQueueDto(TASK_UUID_1)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
// too early
insertPending(newCeQueueDto(TASK_UUID_3)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(PENDING)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(90_000L));
// task type not in query
insertPending(newCeQueueDto("TASK_4")
- .setComponentUuid("PROJECT_2")
+ .setMainComponentUuid("PROJECT_2")
.setStatus(PENDING)
.setTaskType("ANOTHER_TYPE")
.setCreatedAt(100_000L));
// correct
insertPending(newCeQueueDto(TASK_UUID_2)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(PENDING)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
// correct submitted later
insertPending(newCeQueueDto("TASK_5")
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(PENDING)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(120_000L));
// select by component uuid, status, task type and minimum submitted at
CeTaskQuery query = new CeTaskQuery()
- .setComponentUuids(newArrayList(COMPONENT_UUID_1, "PROJECT_2"))
+ .setMainComponentUuids(newArrayList(MAIN_COMPONENT_UUID_1, "PROJECT_2"))
.setStatuses(singletonList(PENDING.name()))
.setType(CeTaskTypes.REPORT)
.setMinSubmittedAt(100_000L);
@@ -451,7 +454,7 @@ public class CeQueueDaoTest {
@Test
public void select_by_query_returns_empty_list_when_only_current() {
insertPending(newCeQueueDto(TASK_UUID_1)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
@@ -468,7 +471,7 @@ public class CeQueueDaoTest {
@Test
public void select_by_query_returns_empty_list_when_max_submitted_at() {
insertPending(newCeQueueDto(TASK_UUID_1)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
@@ -483,14 +486,14 @@ public class CeQueueDaoTest {
}
@Test
- public void select_by_query_returns_empty_list_when_empty_list_of_component_uuid() {
+ public void select_by_query_returns_empty_list_when_empty_list_of_main_component_uuid() {
insertPending(newCeQueueDto(TASK_UUID_1)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
- CeTaskQuery query = new CeTaskQuery().setComponentUuids(Collections.emptyList());
+ CeTaskQuery query = new CeTaskQuery().setMainComponentUuids(Collections.emptyList());
List<CeQueueDto> result = underTest.selectByQueryInDescOrder(db.getSession(), query, 1_000);
int total = underTest.countByQuery(db.getSession(), query);
@@ -500,57 +503,57 @@ public class CeQueueDaoTest {
}
@Test
- public void count_by_status_and_component_uuid() {
+ public void count_by_status_and_main_component_uuid() {
// task retrieved in the queue
insertPending(newCeQueueDto(TASK_UUID_1)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
// on component uuid 2, not returned
insertPending(newCeQueueDto(TASK_UUID_2)
- .setComponentUuid(COMPONENT_UUID_2)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_2)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
// pending status, not returned
insertPending(newCeQueueDto(TASK_UUID_3)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(PENDING)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
- assertThat(underTest.countByStatusAndComponentUuid(db.getSession(), IN_PROGRESS, COMPONENT_UUID_1)).isEqualTo(1);
+ assertThat(underTest.countByStatusAndMainComponentUuid(db.getSession(), IN_PROGRESS, MAIN_COMPONENT_UUID_1)).isEqualTo(1);
assertThat(underTest.countByStatus(db.getSession(), IN_PROGRESS)).isEqualTo(2);
}
@Test
- public void count_by_status_and_component_uuids() {
+ public void count_by_status_and_main_component_uuids() {
// task retrieved in the queue
insertPending(newCeQueueDto(TASK_UUID_1)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
// on component uuid 2, not returned
insertPending(newCeQueueDto(TASK_UUID_2)
- .setComponentUuid(COMPONENT_UUID_2)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_2)
.setStatus(IN_PROGRESS)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
// pending status, not returned
insertPending(newCeQueueDto(TASK_UUID_3)
- .setComponentUuid(COMPONENT_UUID_1)
+ .setMainComponentUuid(MAIN_COMPONENT_UUID_1)
.setStatus(PENDING)
.setTaskType(CeTaskTypes.REPORT)
.setCreatedAt(100_000L));
- assertThat(underTest.countByStatusAndComponentUuids(db.getSession(), IN_PROGRESS, ImmutableSet.of())).isEmpty();
- assertThat(underTest.countByStatusAndComponentUuids(db.getSession(), IN_PROGRESS, ImmutableSet.of("non existing component uuid"))).isEmpty();
- assertThat(underTest.countByStatusAndComponentUuids(db.getSession(), IN_PROGRESS, ImmutableSet.of(COMPONENT_UUID_1, COMPONENT_UUID_2)))
- .containsOnly(entry(COMPONENT_UUID_1, 1), entry(COMPONENT_UUID_2, 1));
- assertThat(underTest.countByStatusAndComponentUuids(db.getSession(), PENDING, ImmutableSet.of(COMPONENT_UUID_1, COMPONENT_UUID_2)))
- .containsOnly(entry(COMPONENT_UUID_1, 1));
+ assertThat(underTest.countByStatusAndMainComponentUuids(db.getSession(), IN_PROGRESS, ImmutableSet.of())).isEmpty();
+ assertThat(underTest.countByStatusAndMainComponentUuids(db.getSession(), IN_PROGRESS, ImmutableSet.of("non existing component uuid"))).isEmpty();
+ assertThat(underTest.countByStatusAndMainComponentUuids(db.getSession(), IN_PROGRESS, ImmutableSet.of(MAIN_COMPONENT_UUID_1, MAIN_COMPONENT_UUID_2)))
+ .containsOnly(entry(MAIN_COMPONENT_UUID_1, 1), entry(MAIN_COMPONENT_UUID_2, 1));
+ assertThat(underTest.countByStatusAndMainComponentUuids(db.getSession(), PENDING, ImmutableSet.of(MAIN_COMPONENT_UUID_1, MAIN_COMPONENT_UUID_2)))
+ .containsOnly(entry(MAIN_COMPONENT_UUID_1, 1));
assertThat(underTest.countByStatus(db.getSession(), IN_PROGRESS)).isEqualTo(2);
}
@@ -570,11 +573,13 @@ public class CeQueueDaoTest {
return dto;
}
- private CeQueueDto insertPending(String uuid, String componentUuid) {
+ private int pendingComponentUuidGenerator = new Random().nextInt(200);
+ private CeQueueDto insertPending(String uuid, String mainComponentUuid) {
CeQueueDto dto = new CeQueueDto();
dto.setUuid(uuid);
dto.setTaskType(CeTaskTypes.REPORT);
- dto.setComponentUuid(componentUuid);
+ dto.setMainComponentUuid(mainComponentUuid);
+ dto.setComponentUuid("uuid_" + pendingComponentUuidGenerator++);
dto.setStatus(PENDING);
dto.setSubmitterUuid("henri");
underTest.insert(db.getSession(), dto);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDtoTest.java
index b93c6c90fd7..10e57d12133 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDtoTest.java
@@ -47,12 +47,30 @@ public class CeQueueDtoTest {
String str_41_chars = STR_40_CHARS + "a";
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Value of component UUID is too long: " + str_41_chars);
+ expectedException.expectMessage("Value is too long for column CE_QUEUE.COMPONENT_UUID: " + str_41_chars);
underTest.setComponentUuid(str_41_chars);
}
@Test
+ public void setMainComponentUuid_accepts_null_empty_and_string_40_chars_or_less() {
+ underTest.setMainComponentUuid(null);
+ underTest.setMainComponentUuid("");
+ underTest.setMainComponentUuid("bar");
+ underTest.setMainComponentUuid(STR_40_CHARS);
+ }
+
+ @Test
+ public void setMainComponentUuid_throws_IAE_if_value_is_41_chars() {
+ String str_41_chars = STR_40_CHARS + "a";
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Value is too long for column CE_QUEUE.MAIN_COMPONENT_UUID: " + str_41_chars);
+
+ underTest.setMainComponentUuid(str_41_chars);
+ }
+
+ @Test
public void setTaskType_throws_NPE_if_argument_is_null() {
expectedException.expect(NullPointerException.class);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueTesting.java
index 16330122d55..a32c733358b 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueTesting.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueTesting.java
@@ -38,6 +38,7 @@ public class CeQueueTesting {
return new CeQueueDto()
.setUuid(uuid)
.setComponentUuid(randomAlphanumeric(40))
+ .setMainComponentUuid(randomAlphanumeric(39))
.setStatus(CeQueueDto.Status.PENDING)
.setTaskType(CeTaskTypes.REPORT)
.setSubmitterUuid(randomAlphanumeric(255))
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskQueryTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskQueryTest.java
index 8322735f85f..c95622292ef 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskQueryTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskQueryTest.java
@@ -33,22 +33,22 @@ public class CeTaskQueryTest {
@Test
public void no_filter_on_component_uuids_by_default() {
- assertThat(underTest.getComponentUuids()).isNull();
- assertThat(underTest.isShortCircuitedByComponentUuids()).isFalse();
+ assertThat(underTest.getMainComponentUuids()).isNull();
+ assertThat(underTest.isShortCircuitedByMainComponentUuids()).isFalse();
}
@Test
public void filter_on_component_uuid() {
- underTest.setComponentUuid("UUID1");
- assertThat(underTest.getComponentUuids()).containsOnly("UUID1");
- assertThat(underTest.isShortCircuitedByComponentUuids()).isFalse();
+ underTest.setMainComponentUuid("UUID1");
+ assertThat(underTest.getMainComponentUuids()).containsOnly("UUID1");
+ assertThat(underTest.isShortCircuitedByMainComponentUuids()).isFalse();
}
@Test
public void filter_on_multiple_component_uuids() {
- underTest.setComponentUuids(asList("UUID1", "UUID2"));
- assertThat(underTest.getComponentUuids()).containsOnly("UUID1", "UUID2");
- assertThat(underTest.isShortCircuitedByComponentUuids()).isFalse();
+ underTest.setMainComponentUuids(asList("UUID1", "UUID2"));
+ assertThat(underTest.getMainComponentUuids()).containsOnly("UUID1", "UUID2");
+ assertThat(underTest.isShortCircuitedByMainComponentUuids()).isFalse();
}
/**
@@ -57,9 +57,9 @@ public class CeTaskQueryTest {
*/
@Test
public void short_circuited_if_empty_component_uuid_filter() {
- underTest.setComponentUuids(Collections.emptyList());
- assertThat(underTest.getComponentUuids()).isEmpty();
- assertThat(underTest.isShortCircuitedByComponentUuids()).isTrue();
+ underTest.setMainComponentUuids(Collections.emptyList());
+ assertThat(underTest.getMainComponentUuids()).isEmpty();
+ assertThat(underTest.isShortCircuitedByMainComponentUuids()).isTrue();
}
/**
@@ -72,8 +72,8 @@ public class CeTaskQueryTest {
for (int i = 0; i < CeTaskQuery.MAX_COMPONENT_UUIDS + 2; i++) {
uuids.add(String.valueOf(i));
}
- underTest.setComponentUuids(uuids);
- assertThat(underTest.getComponentUuids()).hasSize(CeTaskQuery.MAX_COMPONENT_UUIDS + 2);
- assertThat(underTest.isShortCircuitedByComponentUuids()).isTrue();
+ underTest.setMainComponentUuids(uuids);
+ assertThat(underTest.getMainComponentUuids()).hasSize(CeTaskQuery.MAX_COMPONENT_UUIDS + 2);
+ assertThat(underTest.isShortCircuitedByMainComponentUuids()).isTrue();
}
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
index a6247601c0b..eb060efb539 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
@@ -63,6 +63,7 @@ import org.sonar.db.source.FileSourceDto;
import org.sonar.db.webhook.WebhookDeliveryLiteDto;
import org.sonar.db.webhook.WebhookDto;
+import static com.google.common.base.MoreObjects.firstNonNull;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
@@ -905,6 +906,7 @@ public class PurgeDaoTest {
queueDto.setUuid(Uuids.create());
queueDto.setTaskType(REPORT);
queueDto.setComponentUuid(component.uuid());
+ queueDto.setMainComponentUuid(firstNonNull(component.getMainBranchProjectUuid(), component.uuid()));
queueDto.setSubmitterUuid("submitter uuid");
queueDto.setCreatedAt(1_300_000_000_000L);
queueDto.setStatus(status);
@@ -929,6 +931,7 @@ public class PurgeDaoTest {
.setUuid(UuidFactoryFast.getInstance().create())
.setTaskType("foo")
.setComponentUuid(project.uuid())
+ .setMainComponentUuid(firstNonNull(project.getMainBranchProjectUuid(), project.uuid()))
.setStatus(Status.PENDING)
.setCreatedAt(1_2323_222L)
.setUpdatedAt(1_2323_222L);
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java
new file mode 100644
index 00000000000..f394c338378
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+
+@SupportsBlueGreen
+public class AddTmpColumnsToCeActivity extends AddTmpColumnsToCeTable {
+
+ public AddTmpColumnsToCeActivity(Database db) {
+ super(db, "ce_activity");
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java
new file mode 100644
index 00000000000..0cb136643b2
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+
+@SupportsBlueGreen
+public class AddTmpColumnsToCeQueue extends AddTmpColumnsToCeTable {
+
+ public AddTmpColumnsToCeQueue(Database db) {
+ super(db, "ce_queue");
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java
new file mode 100644
index 00000000000..3a2581897d0
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+abstract class AddTmpColumnsToCeTable extends DdlChange {
+ private static final VarcharColumnDef COLUMN_TMP_COMPONENT_UUID = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_component_uuid")
+ .setLimit(VarcharColumnDef.UUID_SIZE)
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_TMP_MAIN_COMPONENT_UUID = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_main_component_uuid")
+ .setLimit(VarcharColumnDef.UUID_SIZE)
+ .setIsNullable(true)
+ .build();
+ private final String tableName;
+
+ public AddTmpColumnsToCeTable(Database db, String tableName) {
+ super(db);
+ this.tableName = tableName;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), tableName)
+ .addColumn(COLUMN_TMP_MAIN_COMPONENT_UUID)
+ .addColumn(COLUMN_TMP_COMPONENT_UUID)
+ .build());
+
+ // create indexes
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(tableName)
+ .setName(tableName + "_tmp_cpnt_uuid")
+ .addColumn(COLUMN_TMP_COMPONENT_UUID)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(tableName)
+ .setName(tableName + "_tmp_main_cpnt_uuid")
+ .addColumn(COLUMN_TMP_MAIN_COMPONENT_UUID)
+ .setUnique(false)
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java
new file mode 100644
index 00000000000..63980918339
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+@SupportsBlueGreen
+public class AddTmpLastKeyColumnsToCeActivity extends DdlChange {
+ private static final String TABLE_NAME = "ce_activity";
+ private static final int TASK_TYPE_COLUMN_SIZE = 15;
+ private static final BooleanColumnDef COLUMN_TMP_IS_LAST = newBooleanColumnDefBuilder()
+ .setColumnName("tmp_is_last")
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_TMP_IS_LAST_KEY = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_is_last_key")
+ .setLimit(UUID_SIZE + TASK_TYPE_COLUMN_SIZE)
+ .setIsNullable(true)
+ .build();
+ private static final BooleanColumnDef COLUMN_TMP_MAIN_IS_LAST = newBooleanColumnDefBuilder()
+ .setColumnName("tmp_main_is_last")
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_TMP_MAIN_IS_LAST_KEY = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_main_is_last_key")
+ .setLimit(UUID_SIZE + TASK_TYPE_COLUMN_SIZE)
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_STATUS = newVarcharColumnDefBuilder()
+ .setColumnName("status")
+ .setLimit(15)
+ .setIsNullable(false)
+ .build();
+
+ public AddTmpLastKeyColumnsToCeActivity(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+ .addColumn(COLUMN_TMP_IS_LAST)
+ .addColumn(COLUMN_TMP_IS_LAST_KEY)
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST)
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST_KEY)
+ .build());
+
+ // create indexes
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_islast_key")
+ .addColumn(COLUMN_TMP_IS_LAST_KEY)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_islast")
+ .addColumn(COLUMN_TMP_IS_LAST)
+ .addColumn(COLUMN_STATUS)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_main_islast_key")
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST_KEY)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_main_islast")
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST)
+ .addColumn(COLUMN_STATUS)
+ .setUnique(false)
+ .build());
+
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
index 4d45e1d0765..82b34413d0b 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
@@ -32,6 +32,12 @@ public class DbVersion74 implements DbVersion {
.add(2302, "Populate IS_AD_HOC in RULES", PopulateIsAdHocOnRules.class)
.add(2303, "Set IS_EXTERNAL and IS_AD_HOC not nullable in RULES", SetIsExternalAndIsAdHocNotNullableInRules.class)
.add(2304, "Add ad hoc related columns in RULES_METADATA", AddAdHocColumnsInInRulesMetadata.class)
+ .add(2305, "Add CE_QUEUE.MAIN_COMPONENT_UUID 1/5", AddTmpColumnsToCeQueue.class)
+ .add(2306, "Add CE_ACTIVITY.MAIN_COMPONENT_UUID 1/5", AddTmpColumnsToCeActivity.class)
+ .add(2307, "Populate CE_QUEUE.MAIN_COMPONENT_UUID 2/5", PopulateTmpColumnsToCeQueue.class)
+ .add(2308, "Populate CE_ACTIVITY.MAIN_COMPONENT_UUID 2/5", PopulateTmpColumnsToCeActivity.class)
+ .add(2309, "Add CE_ACTIVITY.MAIN_LAST_KEY 1/6", AddTmpLastKeyColumnsToCeActivity.class)
+ .add(2310, "Populate CE_ACTIVITY.MAIN_LAST_KEY 2/6", PopulateTmpLastKeyColumnsToCeActivity.class)
;
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java
new file mode 100644
index 00000000000..bcf08d5981e
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+@SupportsBlueGreen
+public class PopulateTmpColumnsToCeActivity extends DataChange {
+ private final Configuration configuration;
+
+ public PopulateTmpColumnsToCeActivity(Database db, Configuration configuration) {
+ super(db);
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ if (configuration.getBoolean("sonar.sonarcloud.enabled").orElse(false)) {
+ // data migration will be done in background so that interruption of service
+ // is reduced during upgrade
+ return;
+ }
+
+ // activity of long and short branches
+ populateCeActivityTmpColumns(context, "Archived tasks of branches",
+ "select" +
+ " cea.uuid, p.uuid, cea.component_uuid" +
+ " from ce_activity cea" +
+ " inner join projects mp on mp.uuid = cea.component_uuid" +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cea.uuid and ctc1.kee = 'branchType'" +
+ " inner join ce_task_characteristics ctc2 on ctc2.task_uuid = cea.uuid and ctc2.kee = 'branch'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':BRANCH:', ctc2.text_value)" +
+ " where" +
+ " cea.component_uuid is not null" +
+ " and (cea.tmp_component_uuid is null or cea.tmp_main_component_uuid is null)");
+
+ // activity of PRs
+ populateCeActivityTmpColumns(context, "Archived tasks of PRs",
+ "select" +
+ " cea.uuid, p.uuid, cea.component_uuid" +
+ " from ce_activity cea" +
+ " inner join projects mp on mp.uuid = cea.component_uuid " +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cea.uuid and ctc1.kee = 'pullRequest'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':PULL_REQUEST:', ctc1.text_value)" +
+ " where" +
+ " cea.component_uuid is not null" +
+ " and (cea.tmp_component_uuid is null or cea.tmp_main_component_uuid is null)");
+
+ // all activities which tmp columns are not populated yet (will include main and deprecated branches)
+ // both tmp columns will be set to CE_ACTIVITY.COMPONENT_UUID
+ // do not join on PROJECTS to also catch orphans
+ populateCeActivityTmpColumns(context, "Archived tasks of main and deprecated branches",
+ "select" +
+ " cea.uuid, cea.component_uuid, cea.component_uuid" +
+ " from ce_activity cea" +
+ " where" +
+ " cea.component_uuid is not null" +
+ " and (cea.tmp_component_uuid is null or cea.tmp_main_component_uuid is null)");
+ }
+
+ private static void populateCeActivityTmpColumns(Context context, String rowPluralName, String sql) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(sql);
+ massUpdate.update("update ce_activity set tmp_component_uuid=?, tmp_main_component_uuid=? where uuid=?");
+ massUpdate.rowPluralName(rowPluralName);
+ massUpdate.execute(PopulateTmpColumnsToCeActivity::handleUpdate);
+ }
+
+ private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+ String componentUuuid = row.getString(2);
+ String mainComponentUuuid = row.getString(3);
+
+ update.setString(1, componentUuuid);
+ update.setString(2, mainComponentUuuid);
+ update.setString(3, uuid);
+
+ return true;
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java
new file mode 100644
index 00000000000..e19e274bfd0
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java
@@ -0,0 +1,135 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+@SupportsBlueGreen
+public class PopulateTmpColumnsToCeQueue extends DataChange {
+ public PopulateTmpColumnsToCeQueue(Database db) {
+ super(db);
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ // queued task of long and short branches which have already been analyzed at least once
+ populateCeQueueTmpColumns(context, "queued tasks of branches",
+ "select" +
+ " cq.uuid, p.uuid, cq.component_uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid" +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'branchType'" +
+ " inner join ce_task_characteristics ctc2 on ctc2.task_uuid = cq.uuid and ctc2.kee = 'branch'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':BRANCH:', ctc2.text_value)" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)");
+
+ // queued task of pull request which have already been analyzed at least once
+ populateCeQueueTmpColumns(context, "queued tasks of PRs",
+ " select" +
+ " cq.uuid, p.uuid, cq.component_uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid " +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'pullRequest'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':PULL_REQUEST:', ctc1.text_value)" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)");
+
+ // queued task of long and short branches which have never been analyzed must be deleted
+ deleteFromCeQueue(context, "queued tasks of never analyzed branches",
+ "select" +
+ " cq.uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid" +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'branchType'" +
+ " inner join ce_task_characteristics ctc2 on ctc2.task_uuid = cq.uuid and ctc2.kee = 'branch'" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)" +
+ " and not exists (select 1 from projects p where p.kee = concat(mp.kee, ':BRANCH:', ctc2.text_value))");
+
+ // queued of pull request which have never been analyzed must be deleted
+ deleteFromCeQueue(context, "queued tasks of never analyzed PRs",
+ "select" +
+ " cq.uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid " +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'pullRequest'" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)" +
+ " and not exists (select 1 from projects p where p.kee = concat(mp.kee, ':PULL_REQUEST:', ctc1.text_value))");
+
+ // all queue which tmp columns are not populated yet (will include main and deprecated branches)
+ // both tmp columns will be set to CE_QUEUE.COMPONENT_UUID
+ // do not join on PROJECTS to also catch orphans (there are many for branch and PRs due to SONAR-10642)
+ populateCeQueueTmpColumns(context, "queued tasks of main and deprecated branches",
+ "select" +
+ " cq.uuid, cq.component_uuid, cq.component_uuid" +
+ " from ce_queue cq" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)");
+ }
+
+ private static void populateCeQueueTmpColumns(Context context, String pluralName, String selectSQL) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(selectSQL);
+ massUpdate.update("update ce_queue set tmp_component_uuid=?, tmp_main_component_uuid=? where uuid=?");
+ massUpdate.rowPluralName(pluralName);
+ massUpdate.execute(PopulateTmpColumnsToCeQueue::handleUpdate);
+ }
+
+ private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+ String componentUuuid = row.getString(2);
+ String mainComponentUuuid = row.getString(3);
+
+ update.setString(1, componentUuuid);
+ update.setString(2, mainComponentUuuid);
+ update.setString(3, uuid);
+
+ return true;
+ }
+
+ private static void deleteFromCeQueue(Context context, String pluralName, String sql) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(sql);
+ massUpdate.update("delete from ce_queue where uuid = ?");
+ massUpdate.rowPluralName(pluralName);
+ massUpdate.execute(PopulateTmpColumnsToCeQueue::handleDelete);
+ }
+
+ private static boolean handleDelete(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+
+ update.setString(1, uuid);
+
+ return true;
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java
new file mode 100644
index 00000000000..81b9340a751
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+@SupportsBlueGreen
+public class PopulateTmpLastKeyColumnsToCeActivity extends DataChange {
+ private final Configuration configuration;
+
+ public PopulateTmpLastKeyColumnsToCeActivity(Database db, Configuration configuration) {
+ super(db);
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ if (configuration.getBoolean("sonar.sonarcloud.enabled").orElse(false)) {
+ // data migration will be done in background so that interruption of service
+ // is reduced during upgrade
+ return;
+ }
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select" +
+ " cea.uuid, cea.task_type, cea.component_uuid, cea.tmp_component_uuid, cea.tmp_main_component_uuid" +
+ " from ce_activity cea" +
+ " where" +
+ " cea.tmp_is_last is null" +
+ " or cea.tmp_is_last_key is null" +
+ " or cea.tmp_main_is_last is null" +
+ " or cea.tmp_main_is_last_key is null");
+ massUpdate.update("update ce_activity" +
+ " set" +
+ " tmp_is_last=?" +
+ " ,tmp_is_last_key=?" +
+ " ,tmp_main_is_last=?" +
+ " ,tmp_main_is_last_key=?" +
+ " where uuid=?");
+ massUpdate.rowPluralName("rows of ce_activity");
+ massUpdate.execute(PopulateTmpLastKeyColumnsToCeActivity::handleUpdate);
+ }
+
+ private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+ String taskType = row.getString(2);
+ String oldComponentUuid = row.getString(3);
+ String componentUuuid = row.getString(4);
+ String mainComponentUuuid = row.getString(5);
+
+ update.setBoolean(1, false);
+ update.setString(2, oldComponentUuid == null ? taskType : (taskType + componentUuuid));
+ update.setBoolean(3, false);
+ update.setString(4, oldComponentUuid == null ? taskType : (taskType + mainComponentUuuid));
+ update.setString(5, uuid);
+
+ return true;
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..f0f703699d3
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddTmpColumnsToCeActivityTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddTmpColumnsToCeActivityTest.class, "ce_activity.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddTmpColumnsToCeActivity underTest = new AddTmpColumnsToCeActivity(db.database());
+
+ @Test
+ public void columns_and_indexes_are_added_to_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_activity", "tmp_component_uuid", VARCHAR, 40, true);
+ db.assertColumnDefinition("ce_activity", "tmp_main_component_uuid", VARCHAR, 40, true);
+ db.assertIndex("ce_activity", "ce_activity_tmp_cpnt_uuid", "tmp_component_uuid");
+ db.assertIndex("ce_activity", "ce_activity_tmp_main_cpnt_uuid", "tmp_main_component_uuid");
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java
new file mode 100644
index 00000000000..97b062a8d9e
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddTmpColumnsToCeQueueTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddTmpColumnsToCeQueueTest.class, "ce_queue.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddTmpColumnsToCeQueue underTest = new AddTmpColumnsToCeQueue(db.database());
+
+ @Test
+ public void columns_and_indexes_are_added_to_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_queue", "tmp_component_uuid", VARCHAR, 40, true);
+ db.assertColumnDefinition("ce_queue", "tmp_main_component_uuid", VARCHAR, 40, true);
+ db.assertIndex("ce_queue", "ce_queue_tmp_cpnt_uuid", "tmp_component_uuid");
+ db.assertIndex("ce_queue", "ce_queue_tmp_main_cpnt_uuid", "tmp_main_component_uuid");
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..de71958cc25
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.BOOLEAN;
+import static java.sql.Types.VARCHAR;
+
+public class AddTmpLastKeyColumnsToCeActivityTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddTmpLastKeyColumnsToCeActivityTest.class, "ce_activity.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddTmpLastKeyColumnsToCeActivity underTest = new AddTmpLastKeyColumnsToCeActivity(db.database());
+
+ @Test
+ public void columns_and_indexes_are_added_to_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_activity", "tmp_is_last", BOOLEAN, null, true);
+ db.assertColumnDefinition("ce_activity", "tmp_is_last_key", VARCHAR, 55, true);
+ db.assertColumnDefinition("ce_activity", "tmp_main_is_last", BOOLEAN, null, true);
+ db.assertColumnDefinition("ce_activity", "tmp_main_is_last_key", VARCHAR, 55, true);
+ db.assertIndex("ce_activity", "ce_activity_t_islast_key", "tmp_is_last_key");
+ db.assertIndex("ce_activity", "ce_activity_t_main_islast", "tmp_main_is_last", "status");
+ db.assertIndex("ce_activity", "ce_activity_t_main_islast_key", "tmp_main_is_last_key");
+ db.assertIndex("ce_activity", "ce_activity_t_main_islast", "tmp_main_is_last", "status");
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
index 28dc704bf77..c0ba8282e1c 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
@@ -35,6 +35,6 @@ public class DbVersion74Test {
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 5);
+ verifyMigrationCount(underTest, 11);
}
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..298ae547b9d
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java
@@ -0,0 +1,233 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.db.CoreDbTester;
+
+import static java.util.Arrays.stream;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class PopulateTmpColumnsToCeActivityTest {
+ private static final Map<String, String> NO_CHARACTERISTICS = Collections.emptyMap();
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateTmpColumnsToCeActivityTest.class, "ce_activity.sql");
+
+ private MapSettings settings = new MapSettings();
+ private PopulateTmpColumnsToCeActivity underTest = new PopulateTmpColumnsToCeActivity(db.database(), settings.asConfig());
+
+ @Test
+ public void execute_has_no_effect_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(rowsInCeActivity()).isEmpty();
+ }
+
+ @Test
+ public void execute_has_no_effect_on_empty_table_on_sonarcloud() throws SQLException {
+ settings.setProperty("sonar.sonarcloud.enabled", true);
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity()).isEmpty();
+ }
+
+ @Test
+ @UseDataProvider("characteriticsOfMainBranchesAndPr")
+ public void execute_populates_tmp_columns_with_component_uuid_if_task_has_no_row_in_PROJECTS(Map<String, String> characteristics) throws SQLException {
+ Row[] notUpdatedRows = Stream.of(
+ // not updated because no component_uuid
+ new Row(newUuid(), null, null, null),
+ new Row(newUuid(), null, randomAlphabetic(2), null),
+ new Row(newUuid(), null, randomAlphabetic(3), randomAlphabetic(4)),
+ new Row(newUuid(), null, null, randomAlphabetic(5)),
+ // not updated because both target fields are already set (re-entrance)
+ new Row(newUuid(), randomAlphabetic(14), randomAlphabetic(6), randomAlphabetic(7)))
+ .toArray(Row[]::new);
+ Row[] updatedRows = {
+ new Row(newUuid(), randomAlphabetic(12), null, null),
+ new Row(newUuid(), randomAlphabetic(13), randomAlphabetic(5), null),
+ new Row(newUuid(), randomAlphabetic(14), null, randomAlphabetic(6)),
+ };
+ stream(notUpdatedRows).forEach(row -> insertCeActivity(row, characteristics));
+ stream(updatedRows).forEach(row -> insertCeActivity(row, characteristics));
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .hasSize(notUpdatedRows.length + updatedRows.length)
+ .contains(notUpdatedRows)
+ .contains(stream(updatedRows)
+ .map(row -> new Row(row.taskUuid, row.componentUuid, row.componentUuid, row.componentUuid))
+ .toArray(Row[]::new));
+ }
+
+ @DataProvider
+ public static Object[][] characteriticsOfMainBranchesAndPr() {
+ return new Object[][] {
+ {NO_CHARACTERISTICS},
+ {branchCharacteristics("LONG", randomAlphabetic(15))},
+ {branchCharacteristics("SHORT", randomAlphabetic(16))},
+ {branchCharacteristics(randomAlphabetic(17), randomAlphabetic(18))},
+ {prCharacteristics(randomAlphabetic(19))},
+ };
+ }
+
+ @Test
+ public void execute_populates_tmp_columns_with_component_uuid_for_existing_main_branch() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ insertProjects(mainComponentUuid, randomAlphabetic(3));
+ String taskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), NO_CHARACTERISTICS);
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .containsOnly(new Row(taskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_populates_branches_of_task_without_row_in_PROJECTS_with_COMPONENT_UUID_and_those_with_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String branchUuid = randomAlphabetic(4);
+ String branchType1 = randomAlphabetic(5);
+ String branchName1 = randomAlphabetic(6);
+ String branchType2 = randomAlphabetic(7);
+ String branchName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(branchUuid, mainComponentKey + ":BRANCH:" + branchName2);
+ String orphanTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType1, branchName1));
+ String regularTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType2, branchName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .containsOnly(
+ new Row(orphanTaskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid),
+ new Row(regularTaskUuid, mainComponentUuid, branchUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_populates_prs_of_task_without_row_in_PROJECTS_with_COMPONENT_UUID_and_those_with_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String prUuid = randomAlphabetic(4);
+ String prName1 = randomAlphabetic(6);
+ String prName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(prUuid, mainComponentKey + ":PULL_REQUEST:" + prName2);
+ String orphanTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName1));
+ String regularTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .containsOnly(
+ new Row(orphanTaskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid),
+ new Row(regularTaskUuid, mainComponentUuid, prUuid, mainComponentUuid));
+ }
+
+ private Stream<Row> rowsInCeActivity() {
+ return db.select("select" +
+ " uuid as \"UUID\", component_uuid as \"COMPONENT_UUID\", tmp_component_uuid as \"TMP_COMPONENT_UUID\", tmp_main_component_UUID as \"TMP_MAIN_COMPONENT_UUID\"" +
+ " from ce_activity")
+ .stream()
+ .map(row -> new Row(
+ (String) row.get("UUID"),
+ (String) row.get("COMPONENT_UUID"),
+ (String) row.get("TMP_COMPONENT_UUID"),
+ (String) row.get("TMP_MAIN_COMPONENT_UUID")));
+ }
+
+ private String insertCeActivity(Row row, Map<String, String> characteristics) {
+ String uuid = insertCeActivity(row.taskUuid, row.componentUuid, row.tmpComponentUuid, row.tmpMainComponentUuid);
+ characteristics.forEach((key, value) -> insertCeCharacteristic(uuid, key, value));
+ return uuid;
+ }
+
+ private String insertCeActivity(String uuid, @Nullable String componentUuid, @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ Random random = new Random();
+ db.executeInsert("ce_activity",
+ "UUID", uuid,
+ "TASK_TYPE", randomAlphabetic(6),
+ "COMPONENT_UUID", componentUuid,
+ "TMP_COMPONENT_UUID", tmpComponentUuid,
+ "TMP_MAIN_COMPONENT_UUID", tmpMainComponentUuid,
+ "STATUS", randomAlphabetic(7),
+ "IS_LAST", random.nextBoolean(),
+ "IS_LAST_KEY", random.nextBoolean(),
+ "EXECUTION_COUNT", random.nextInt(500),
+ "SUBMITTED_AT", (long) random.nextInt(500),
+ "CREATED_AT", (long) random.nextInt(500),
+ "UPDATED_AT", (long) random.nextInt(500));
+ return uuid;
+ }
+
+ private void insertCeCharacteristic(String taskUuid, String key, String value) {
+ db.executeInsert(
+ "ce_task_characteristics",
+ "UUID", newUuid(),
+ "TASK_UUID", taskUuid,
+ "KEE", key,
+ "TEXT_VALUE", value);
+ }
+
+ private void insertProjects(String uuid, String key) {
+ db.executeInsert(
+ "PROJECTS",
+ "UUID", uuid,
+ "KEE", key,
+ "ORGANIZATION_UUID", "org_" + uuid,
+ "ROOT_UUID", uuid + "_root",
+ "UUID_PATH", uuid + "_path",
+ "PROJECT_UUID", uuid + "_project",
+ "PRIVATE", new Random().nextBoolean());
+ }
+
+ private int uuidGenerator = new Random().nextInt(9000);
+
+ private String newUuid() {
+ return "uuid_" + uuidGenerator++;
+ }
+
+ private static Map<String, String> branchCharacteristics(String branchType, String branchName) {
+ return ImmutableMap.of("branchType", branchType, "branch", branchName);
+ }
+
+ private static Map<String, String> prCharacteristics(String prName) {
+ return ImmutableMap.of("pullRequest", prName);
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java
new file mode 100644
index 00000000000..866eaa49026
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java
@@ -0,0 +1,216 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.db.CoreDbTester;
+
+import static java.util.Arrays.stream;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class PopulateTmpColumnsToCeQueueTest {
+ private static final Map<String, String> NO_CHARACTERISTICS = Collections.emptyMap();
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateTmpColumnsToCeQueueTest.class, "ce_queue.sql");
+
+ private PopulateTmpColumnsToCeQueue underTest = new PopulateTmpColumnsToCeQueue(db.database());
+
+ @Test
+ public void no_action_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable("ce_queue")).isZero();
+ }
+
+ @Test
+ @UseDataProvider("characteriticsOfMainBranchesAndPr")
+ public void execute_populates_tmp_columns_with_component_uuid_if_task_has_no_row_in_PROJECTS(Map<String, String> characteristics) throws SQLException {
+ Row[] notUpdatedRows = Stream.of(
+ // not updated because no component_uuid
+ new Row(newUuid(), null, null, null),
+ new Row(newUuid(), null, randomAlphabetic(2), null),
+ new Row(newUuid(), null, randomAlphabetic(3), randomAlphabetic(4)),
+ new Row(newUuid(), null, null, randomAlphabetic(5)),
+ // not updated because both target fields are already set (re-entrance)
+ new Row(newUuid(), randomAlphabetic(14), randomAlphabetic(6), randomAlphabetic(7)))
+ .toArray(Row[]::new);
+ Row[] updatedRows = {
+ new Row(newUuid(), randomAlphabetic(12), null, null),
+ new Row(newUuid(), randomAlphabetic(13), randomAlphabetic(5), null),
+ new Row(newUuid(), randomAlphabetic(14), null, randomAlphabetic(6)),
+ };
+ stream(notUpdatedRows).forEach(row -> insertCeQueue(row, characteristics));
+ stream(updatedRows).forEach(row -> insertCeQueue(row, characteristics));
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .hasSize(notUpdatedRows.length + updatedRows.length)
+ .contains(notUpdatedRows)
+ .contains(stream(updatedRows)
+ .map(row -> new Row(row.taskUuid, row.componentUuid, row.componentUuid, row.componentUuid))
+ .toArray(Row[]::new));
+ }
+
+ @DataProvider
+ public static Object[][] characteriticsOfMainBranchesAndPr() {
+ return new Object[][] {
+ {NO_CHARACTERISTICS},
+ {branchCharacteristics("LONG", randomAlphabetic(15))},
+ {branchCharacteristics("SHORT", randomAlphabetic(16))},
+ {branchCharacteristics(randomAlphabetic(17), randomAlphabetic(18))},
+ {prCharacteristics(randomAlphabetic(19))},
+ };
+ }
+
+ @Test
+ public void execute_populates_tmp_columns_with_component_uuid_for_existing_main_branch() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ insertProjects(mainComponentUuid, randomAlphabetic(3));
+ String taskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), NO_CHARACTERISTICS);
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .containsOnly(new Row(taskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_tasks_of_branches_without_row_in_PROJECTS_and_populates_others_matching_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String branchUuid = randomAlphabetic(4);
+ String branchType1 = randomAlphabetic(5);
+ String branchName1 = randomAlphabetic(6);
+ String branchType2 = randomAlphabetic(7);
+ String branchName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(branchUuid, mainComponentKey + ":BRANCH:" + branchName2);
+ String deletedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType1, branchName1));
+ String updatedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType2, branchName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .containsOnly(new Row(updatedTaskUuid, mainComponentUuid, branchUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_tasks_of_prs_without_row_in_PROJECTS_and_populates_others_matching_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String prUuid = randomAlphabetic(4);
+ String prName1 = randomAlphabetic(6);
+ String prName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(prUuid, mainComponentKey + ":PULL_REQUEST:" + prName2);
+ String deletedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName1));
+ String updatedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .containsOnly(new Row(updatedTaskUuid, mainComponentUuid, prUuid, mainComponentUuid));
+ }
+
+ private Stream<Row> rowsInCeQueue() {
+ return db.select("select" +
+ " uuid as \"UUID\", component_uuid as \"COMPONENT_UUID\", tmp_component_uuid as \"TMP_COMPONENT_UUID\", tmp_main_component_UUID as \"TMP_MAIN_COMPONENT_UUID\"" +
+ " from ce_queue")
+ .stream()
+ .map(row -> new Row(
+ (String) row.get("UUID"),
+ (String) row.get("COMPONENT_UUID"),
+ (String) row.get("TMP_COMPONENT_UUID"),
+ (String) row.get("TMP_MAIN_COMPONENT_UUID")));
+ }
+
+ private String insertCeQueue(Row row, Map<String, String> characteristics) {
+ String uuid = insertCeQueue(row.taskUuid, row.componentUuid, row.tmpComponentUuid, row.tmpMainComponentUuid);
+ characteristics.forEach((key, value) -> insertCeCharacteristic(uuid, key, value));
+ return uuid;
+ }
+
+ private String insertCeQueue(String uuid, @Nullable String componentUuid, @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ Random random = new Random();
+ db.executeInsert("ce_queue",
+ "UUID", uuid,
+ "TASK_TYPE", randomAlphabetic(6),
+ "COMPONENT_UUID", componentUuid,
+ "TMP_COMPONENT_UUID", tmpComponentUuid,
+ "TMP_MAIN_COMPONENT_UUID", tmpMainComponentUuid,
+ "STATUS", randomAlphabetic(7),
+ "EXECUTION_COUNT", random.nextInt(500),
+ "CREATED_AT", (long) random.nextInt(500),
+ "UPDATED_AT", (long) random.nextInt(500));
+ return uuid;
+ }
+
+ private void insertCeCharacteristic(String taskUuid, String key, String value) {
+ db.executeInsert(
+ "ce_task_characteristics",
+ "UUID", newUuid(),
+ "TASK_UUID", taskUuid,
+ "KEE", key,
+ "TEXT_VALUE", value);
+ }
+
+ private void insertProjects(String uuid, String key) {
+ db.executeInsert(
+ "PROJECTS",
+ "UUID", uuid,
+ "KEE", key,
+ "ORGANIZATION_UUID", "org_" + uuid,
+ "ROOT_UUID", uuid + "_root",
+ "UUID_PATH", uuid + "_path",
+ "PROJECT_UUID", uuid + "_project",
+ "PRIVATE", new Random().nextBoolean());
+ }
+
+ private int uuidGenerator = new Random().nextInt(9000);
+
+ private String newUuid() {
+ return "uuid_" + uuidGenerator++;
+ }
+
+ private static Map<String, String> branchCharacteristics(String branchType, String branchName) {
+ return ImmutableMap.of("branchType", branchType, "branch", branchName);
+ }
+
+ private static Map<String, String> prCharacteristics(String prName) {
+ return ImmutableMap.of("pullRequest", prName);
+ }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..ae4bd591e82
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Random;
+import javax.annotation.Nullable;
+import org.assertj.core.api.AbstractIterableAssert;
+import org.assertj.core.api.ObjectAssert;
+import org.assertj.core.groups.Tuple;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.CoreDbTester;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class PopulateTmpLastKeyColumnsToCeActivityTest {
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateTmpLastKeyColumnsToCeActivityTest.class, "ce_activity.sql");
+
+ private MapSettings settings = new MapSettings();
+ private PopulateTmpLastKeyColumnsToCeActivity underTest = new PopulateTmpLastKeyColumnsToCeActivity(db.database(), settings.asConfig());
+
+ @Test
+ public void execute_has_no_effect_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable("ce_activity")).isZero();
+ }
+
+ @Test
+ public void execute_populate_tmp_last_key_columns_from_type_and_component_uuid_columns() throws SQLException {
+ String type = randomAlphabetic(6);
+ String oldComponentUuid = randomAlphabetic(7);
+ String tmpComponentUuid = randomAlphabetic(8);
+ String tmpMainComponentUuid = randomAlphabetic(9);
+
+ String taskWithComponentUuid = insertCeActivity(type, oldComponentUuid, tmpComponentUuid, tmpMainComponentUuid);
+ String taskWithInconsistentComponentUuid = insertCeActivity(type, null, tmpComponentUuid, tmpMainComponentUuid);
+ String taskNoComponentUuid = insertCeActivity(type, null, null, null);
+
+ underTest.execute();
+
+ assertThatTmpLastKeyAndMainLastKeyOf(taskWithComponentUuid).containsOnly(tuple(type + tmpComponentUuid, type + tmpMainComponentUuid));
+ assertThatTmpLastKeyAndMainLastKeyOf(taskWithInconsistentComponentUuid).containsOnly(tuple(type, type));
+ assertThatTmpLastKeyAndMainLastKeyOf(taskNoComponentUuid).containsOnly(tuple(type, type));
+
+ assertThatTmpIsLastAndMainIsLastOf(taskWithComponentUuid).containsOnly(tuple(false, false));
+ assertThatTmpIsLastAndMainIsLastOf(taskWithInconsistentComponentUuid).containsOnly(tuple(false, false));
+ assertThatTmpIsLastAndMainIsLastOf(taskNoComponentUuid).containsOnly(tuple(false, false));
+ }
+ @Test
+ public void execute_is_reentrant() throws SQLException {
+ execute_populate_tmp_last_key_columns_from_type_and_component_uuid_columns();
+
+ underTest.execute();
+ }
+
+ private String insertCeActivity(String type,
+ @Nullable String oldComponentUuid,
+ @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ checkArgument((tmpComponentUuid == null) == (tmpMainComponentUuid == null));
+
+ String uuid = UuidFactoryFast.getInstance().create();
+
+ Random random = new Random();
+ db.executeInsert(
+ "ce_activity",
+ "UUID", uuid,
+ "TASK_TYPE", type,
+ "COMPONENT_UUID", oldComponentUuid,
+ "TMP_COMPONENT_UUID", tmpComponentUuid,
+ "TMP_MAIN_COMPONENT_UUID", tmpMainComponentUuid,
+ "STATUS", randomAlphabetic(5),
+ "IS_LAST", random.nextBoolean(),
+ "IS_LAST_KEY", randomAlphabetic(12),
+ "EXECUTION_COUNT", random.nextInt(10),
+ "SUBMITTED_AT", (long) random.nextInt(5_999),
+ "CREATED_AT", (long) random.nextInt(5_999),
+ "UPDATED_AT", (long) random.nextInt(5_999));
+
+ return uuid;
+ }
+
+ private AbstractIterableAssert<?, List<? extends Tuple>, Tuple, ObjectAssert<Tuple>> assertThatTmpLastKeyAndMainLastKeyOf(String uuid) {
+ return assertThat(db.select("select tmp_is_last_key as \"LAST_KEY\", tmp_main_is_last_key as \"MAIN_LAST_KEY\" from ce_activity where uuid='" + uuid + "'"))
+ .extracting(t -> (String) t.get("LAST_KEY"), t -> (String) t.get("MAIN_LAST_KEY"));
+ }
+
+ private AbstractIterableAssert<?, List<? extends Tuple>, Tuple, ObjectAssert<Tuple>> assertThatTmpIsLastAndMainIsLastOf(String uuid) {
+ return assertThat(db.select("select tmp_is_last as \"LAST\", tmp_main_is_last as \"MAIN_LAST\" from ce_activity where uuid='" + uuid + "'"))
+ .extracting(t -> (Boolean) t.get("LAST"), t -> (Boolean) t.get("MAIN_LAST"));
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java
new file mode 100644
index 00000000000..5ba77192079
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v74;
+
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+final class Row {
+ final String taskUuid;
+ final String componentUuid;
+ final String tmpComponentUuid;
+ final String tmpMainComponentUuid;
+
+ Row(String taskUuid, @Nullable String componentUuid, @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ this.taskUuid = taskUuid;
+ this.componentUuid = componentUuid;
+ this.tmpComponentUuid = tmpComponentUuid;
+ this.tmpMainComponentUuid = tmpMainComponentUuid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Row row = (Row) o;
+ return Objects.equals(taskUuid, row.taskUuid) &&
+ Objects.equals(componentUuid, row.componentUuid) &&
+ Objects.equals(tmpComponentUuid, row.tmpComponentUuid) &&
+ Objects.equals(tmpMainComponentUuid, row.tmpMainComponentUuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(taskUuid, componentUuid, tmpComponentUuid, tmpMainComponentUuid);
+ }
+
+ @Override
+ public String toString() {
+ return "Row{" +
+ "uuid='" + taskUuid + '\'' +
+ ", componentUuid='" + componentUuid + '\'' +
+ ", tmpComponentUuid='" + tmpComponentUuid + '\'' +
+ ", tmpMainComponentUuid='" + tmpMainComponentUuid + '\'' +
+ '}';
+ }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..540c11a8dd5
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,26 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql
new file mode 100644
index 00000000000..b1282536589
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql
@@ -0,0 +1,16 @@
+CREATE TABLE "CE_QUEUE" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
+CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..fb0d7303aae
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,30 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..68242f666e7
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,87 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+
+CREATE TABLE "CE_TASK_CHARACTERISTICS" (
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(50) NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+
+ CONSTRAINT "PK_CE_TASK_CHARACTERISTICS" PRIMARY KEY ("UUID")
+);
+CREATE INDEX "CE_TASK_CHARACTERISTICS_TASK_UUID" ON "CE_TASK_CHARACTERISTICS" ("TASK_UUID");
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(1500) NOT NULL,
+ "ROOT_UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(1500),
+ "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_COMPONENT_UUID" VARCHAR(50),
+ "LONG_NAME" VARCHAR(2000),
+ "DEVELOPER_UUID" VARCHAR(50),
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT,
+ "B_CHANGED" BOOLEAN,
+ "B_COPY_COMPONENT_UUID" VARCHAR(50),
+ "B_DESCRIPTION" VARCHAR(2000),
+ "B_ENABLED" BOOLEAN,
+ "B_UUID_PATH" VARCHAR(1500),
+ "B_LANGUAGE" VARCHAR(20),
+ "B_LONG_NAME" VARCHAR(500),
+ "B_MODULE_UUID" VARCHAR(50),
+ "B_MODULE_UUID_PATH" VARCHAR(1500),
+ "B_NAME" VARCHAR(500),
+ "B_PATH" VARCHAR(2000),
+ "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql
new file mode 100644
index 00000000000..96cd2eab03c
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql
@@ -0,0 +1,77 @@
+CREATE TABLE "CE_QUEUE" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
+CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_TMP_COMPONENT_UUID" ON "CE_QUEUE" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_TMP_MAIN_CMPT_UUID" ON "CE_QUEUE" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
+
+CREATE TABLE "CE_TASK_CHARACTERISTICS" (
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(50) NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+
+ CONSTRAINT "PK_CE_TASK_CHARACTERISTICS" PRIMARY KEY ("UUID")
+);
+CREATE INDEX "CE_TASK_CHARACTERISTICS_TASK_UUID" ON "CE_TASK_CHARACTERISTICS" ("TASK_UUID");
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(1500) NOT NULL,
+ "ROOT_UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(1500),
+ "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_COMPONENT_UUID" VARCHAR(50),
+ "LONG_NAME" VARCHAR(2000),
+ "DEVELOPER_UUID" VARCHAR(50),
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT,
+ "B_CHANGED" BOOLEAN,
+ "B_COPY_COMPONENT_UUID" VARCHAR(50),
+ "B_DESCRIPTION" VARCHAR(2000),
+ "B_ENABLED" BOOLEAN,
+ "B_UUID_PATH" VARCHAR(1500),
+ "B_LANGUAGE" VARCHAR(20),
+ "B_LONG_NAME" VARCHAR(500),
+ "B_MODULE_UUID" VARCHAR(50),
+ "B_MODULE_UUID_PATH" VARCHAR(1500),
+ "B_NAME" VARCHAR(500),
+ "B_PATH" VARCHAR(2000),
+ "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..b4e110b67e7
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,38 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "TMP_IS_LAST" BOOLEAN,
+ "TMP_IS_LAST_KEY" VARCHAR(55),
+ "TMP_MAIN_IS_LAST" BOOLEAN,
+ "TMP_MAIN_IS_LAST_KEY" VARCHAR(55),
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CPNT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CPNT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_T_ISLAST_KEY" ON "CE_ACTIVITY" ("TMP_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_T_ISLAST" ON "CE_ACTIVITY" ("TMP_IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_T_MAIN_ISLAST_KEY" ON "CE_ACTIVITY" ("TMP_MAIN_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_T_MAIN_ISLAST" ON "CE_ACTIVITY" ("TMP_MAIN_IS_LAST", "STATUS");
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/CeModule.java b/server/sonar-server/src/main/java/org/sonar/server/ce/CeModule.java
index 66ee97b1465..f5b0e3b5791 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/CeModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/CeModule.java
@@ -24,6 +24,7 @@ import org.sonar.ce.task.log.CeTaskLogging;
import org.sonar.ce.task.projectanalysis.taskprocessor.ReportTaskProcessor;
import org.sonar.core.platform.Module;
import org.sonar.server.ce.http.CeHttpClientImpl;
+import org.sonar.server.ce.queue.BranchSupport;
import org.sonar.server.ce.queue.ReportSubmitter;
public class CeModule extends Module {
@@ -34,6 +35,7 @@ public class CeModule extends Module {
// Queue
CeQueueImpl.class,
+ BranchSupport.class,
ReportSubmitter.class,
// Core tasks processors
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupport.java b/server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupport.java
new file mode 100644
index 00000000000..647f3e43a91
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupport.java
@@ -0,0 +1,169 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.queue;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ServerSide;
+import org.sonar.core.component.ComponentKeys;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Branch code for {@link ReportSubmitter}.
+ * <p>
+ * Does not support branches (except deprecated branch feature provided by "sonar.branch") unless an implementation of
+ * {@link BranchSupportDelegate} is available.
+ */
+@ServerSide
+public class BranchSupport {
+ @CheckForNull
+ private final BranchSupportDelegate delegate;
+
+ /**
+ * Constructor called by Pico when no implementation of {@link BranchSupportDelegate} is available.
+ */
+ public BranchSupport() {
+ this(null);
+ }
+
+ public BranchSupport(@Nullable BranchSupportDelegate delegate) {
+ this.delegate = delegate;
+ }
+
+ ComponentKey createComponentKey(String projectKey, @Nullable String deprecatedBranch, Map<String, String> characteristics) {
+ if (characteristics.isEmpty()) {
+ return new ComponentKeyImpl(projectKey, deprecatedBranch, ComponentKeys.createKey(projectKey, deprecatedBranch));
+ } else {
+ checkState(delegate != null, "Current edition does not support branch feature");
+ }
+
+ checkArgument(deprecatedBranch == null, "Deprecated branch feature can't be used at the same time as new branch support");
+ return delegate.createComponentKey(projectKey, characteristics);
+ }
+
+ ComponentDto createBranchComponent(DbSession dbSession, ComponentKey componentKey, OrganizationDto organization, ComponentDto mainComponentDto) {
+ checkState(delegate != null, "Current edition does not support branch feature");
+
+ return delegate.createBranchComponent(dbSession, componentKey, organization, mainComponentDto);
+ }
+
+ public interface ComponentKey {
+ String getKey();
+
+ String getDbKey();
+
+ Optional<String> getDeprecatedBranchName();
+
+ Optional<String> getBranchName();
+
+ Optional<String> getPullRequestKey();
+
+ boolean isMainBranch();
+
+ boolean isDeprecatedBranch();
+
+ /**
+ * @return the {@link ComponentKey} of the main branch for this component.
+ * If this component is the main branch (ie. {@link #isMainBranch()} returns true), this method returns
+ * {@code this}.
+ */
+ ComponentKey getMainBranchComponentKey();
+ }
+
+ private static final class ComponentKeyImpl implements ComponentKey {
+ private final String key;
+ private final String dbKey;
+ @CheckForNull
+ private final String deprecatedBranchName;
+
+ public ComponentKeyImpl(String key, @Nullable String deprecatedBranchName, String dbKey) {
+ this.key = key;
+ this.deprecatedBranchName = deprecatedBranchName;
+ this.dbKey = dbKey;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public String getDbKey() {
+ return dbKey;
+ }
+
+ @Override
+ public Optional<String> getDeprecatedBranchName() {
+ return Optional.ofNullable(deprecatedBranchName);
+ }
+
+ @Override
+ public Optional<String> getBranchName() {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<String> getPullRequestKey() {
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean isMainBranch() {
+ return key.equals(dbKey);
+ }
+
+ @Override
+ public boolean isDeprecatedBranch() {
+ return deprecatedBranchName != null;
+ }
+
+ @Override
+ public ComponentKey getMainBranchComponentKey() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ComponentKeyImpl that = (ComponentKeyImpl) o;
+ return Objects.equals(key, that.key) &&
+ Objects.equals(dbKey, that.dbKey) &&
+ Objects.equals(deprecatedBranchName, that.deprecatedBranchName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, dbKey, deprecatedBranchName);
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupportDelegate.java b/server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupportDelegate.java
new file mode 100644
index 00000000000..e2a68de7daf
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/queue/BranchSupportDelegate.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.queue;
+
+import java.util.Map;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeTaskCharacteristicDto;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.ce.queue.BranchSupport.ComponentKey;
+
+@ServerSide
+public interface BranchSupportDelegate {
+ /**
+ * Creates a {@link ComponentKey} for the specified projectKey and the specified {@code characteristics}.
+ *
+ * @throws IllegalArgumentException if {@code characteristics} is empty
+ * @throws IllegalArgumentException if does not contain a supported value for {@link CeTaskCharacteristicDto#BRANCH_TYPE_KEY BRANCH_TYPE_KEY}
+ * @throws IllegalArgumentException if does not contain a value for expected
+ * {@link CeTaskCharacteristicDto#BRANCH_KEY BRANCH_KEY} or {@link CeTaskCharacteristicDto#PULL_REQUEST PULL_REQUEST}
+ * given the value of {@link CeTaskCharacteristicDto#BRANCH_TYPE_KEY BRANCH_TYPE_KEY}
+ * @throws IllegalArgumentException if incorrectly contains a value in
+ * {@link CeTaskCharacteristicDto#BRANCH_KEY BRANCH_KEY} or {@link CeTaskCharacteristicDto#PULL_REQUEST PULL_REQUEST}
+ * given the value of {@link CeTaskCharacteristicDto#BRANCH_TYPE_KEY BRANCH_TYPE_KEY}
+ */
+ ComponentKey createComponentKey(String projectKey, Map<String, String> characteristics);
+
+ /**
+ * Creates the ComponentDto for the branch described in {@code componentKey} which belongs to the specified
+ * {@code mainComponentDto} in the specified {@code organization}.
+ *
+ * @throws IllegalArgumentException if arguments are inconsistent (such as {@code mainComponentDto} not having the same
+ * key as {@code componentKey.getKey()}, ...)
+ */
+ ComponentDto createBranchComponent(DbSession dbSession, ComponentKey componentKey,
+ OrganizationDto organization, ComponentDto mainComponentDto);
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java b/server/sonar-server/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java
index 69414944486..0c3716e860a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java
@@ -31,7 +31,6 @@ import org.sonar.api.server.ServerSide;
import org.sonar.ce.queue.CeQueue;
import org.sonar.ce.queue.CeTaskSubmit;
import org.sonar.ce.task.CeTask;
-import org.sonar.core.component.ComponentKeys;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.ce.CeTaskTypes;
@@ -60,31 +59,33 @@ public class ReportSubmitter {
private final ComponentUpdater componentUpdater;
private final PermissionTemplateService permissionTemplateService;
private final DbClient dbClient;
+ private final BranchSupport branchSupport;
public ReportSubmitter(CeQueue queue, UserSession userSession, ComponentUpdater componentUpdater,
- PermissionTemplateService permissionTemplateService, DbClient dbClient) {
+ PermissionTemplateService permissionTemplateService, DbClient dbClient, BranchSupport branchSupport) {
this.queue = queue;
this.userSession = userSession;
this.componentUpdater = componentUpdater;
this.permissionTemplateService = permissionTemplateService;
this.dbClient = dbClient;
+ this.branchSupport = branchSupport;
}
/**
* @throws NotFoundException if the organization with the specified key does not exist
* @throws IllegalArgumentException if the organization with the specified key is not the organization of the specified project (when it already exists in DB)
*/
- public CeTask submit(String organizationKey, String projectKey, @Nullable String deprecatedBranch, @Nullable String projectName, Map<String, String> characteristics,
- InputStream reportInput) {
+ public CeTask submit(String organizationKey, String projectKey, @Nullable String deprecatedBranch, @Nullable String projectName,
+ Map<String, String> characteristics, InputStream reportInput) {
try (DbSession dbSession = dbClient.openSession(false)) {
OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey);
- String effectiveProjectKey = ComponentKeys.createKey(projectKey, deprecatedBranch);
- Optional<ComponentDto> component = dbClient.componentDao().selectByKey(dbSession, effectiveProjectKey);
- validateProject(dbSession, component, projectKey);
- ensureOrganizationIsConsistent(component, organizationDto);
- ComponentDto project = component.orElseGet(() -> createProject(dbSession, organizationDto, projectKey, deprecatedBranch, projectName));
- checkScanPermission(project);
- return submitReport(dbSession, reportInput, project, characteristics);
+ BranchSupport.ComponentKey componentKey = branchSupport.createComponentKey(projectKey, deprecatedBranch, characteristics);
+ Optional<ComponentDto> existingComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getDbKey());
+ validateProject(dbSession, existingComponent, projectKey);
+ ensureOrganizationIsConsistent(existingComponent, organizationDto);
+ ComponentDto component = existingComponent.orElseGet(() -> createComponent(dbSession, organizationDto, componentKey, projectName));
+ checkScanPermission(component);
+ return submitReport(dbSession, reportInput, component, characteristics);
}
}
@@ -135,13 +136,32 @@ public class ReportSubmitter {
}
}
- private ComponentDto createProject(DbSession dbSession, OrganizationDto organization, String projectKey, @Nullable String deprecatedBranch, @Nullable String projectName) {
+ private ComponentDto createComponent(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey, @Nullable String projectName) {
+ if (componentKey.isMainBranch() || componentKey.isDeprecatedBranch()) {
+ ComponentDto project = createProject(dbSession, organization, componentKey, projectName);
+ componentUpdater.commitAndIndex(dbSession, project);
+ return project;
+ }
+
+ Optional<ComponentDto> existingMainComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getKey());
+ ComponentDto mainComponentDto = existingMainComponent
+ .orElseGet(() -> createProject(dbSession, organization, componentKey.getMainBranchComponentKey(), projectName));
+ ComponentDto branchComponent = branchSupport.createBranchComponent(dbSession, componentKey, organization, mainComponentDto);
+ if (existingMainComponent.isPresent()) {
+ dbSession.commit();
+ } else {
+ componentUpdater.commitAndIndex(dbSession, mainComponentDto);
+ }
+ return branchComponent;
+ }
+
+ private ComponentDto createProject(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey,
+ @Nullable String projectName) {
userSession.checkPermission(OrganizationPermission.PROVISION_PROJECTS, organization);
Integer userId = userSession.getUserId();
- String effectiveProjectKey = ComponentKeys.createEffectiveKey(projectKey, deprecatedBranch);
boolean wouldCurrentUserHaveScanPermission = permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(
- dbSession, organization.getUuid(), userId, effectiveProjectKey, Qualifiers.PROJECT);
+ dbSession, organization.getUuid(), userId, componentKey.getDbKey(), Qualifiers.PROJECT);
if (!wouldCurrentUserHaveScanPermission) {
throw insufficientPrivilegesException();
}
@@ -150,13 +170,13 @@ public class ReportSubmitter {
NewComponent newProject = newComponentBuilder()
.setOrganizationUuid(organization.getUuid())
- .setKey(projectKey)
- .setName(defaultIfBlank(projectName, projectKey))
- .setDeprecatedBranch(deprecatedBranch)
+ .setKey(componentKey.getKey())
+ .setName(defaultIfBlank(projectName, componentKey.getKey()))
+ .setDeprecatedBranch(componentKey.getDeprecatedBranchName().orElse(null))
.setQualifier(Qualifiers.PROJECT)
.setPrivate(newProjectPrivate)
.build();
- return componentUpdater.create(dbSession, newProject, userId);
+ return componentUpdater.createWithoutCommit(dbSession, newProject, userId);
}
private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto project, Map<String, String> characteristics) {
@@ -167,9 +187,10 @@ public class ReportSubmitter {
dbSession.commit();
submit.setType(CeTaskTypes.REPORT);
- submit.setComponentUuid(project.uuid());
+ submit.setComponent(CeTaskSubmit.Component.fromDto(project));
submit.setSubmitterUuid(userSession.getUuid());
submit.setCharacteristics(characteristics);
return queue.submit(submit.build());
}
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
index 35ecc68b825..c80223efdf7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
@@ -237,9 +237,9 @@ public class ActivityAction implements CeWsAction {
String componentQuery = request.getQ();
if (component != null) {
- query.setComponentUuid(component.uuid());
+ query.setMainComponentUuid(component.uuid());
} else if (componentQuery != null) {
- query.setComponentUuids(loadComponents(dbSession, componentQuery).stream()
+ query.setMainComponentUuids(loadComponents(dbSession, componentQuery).stream()
.map(ComponentDto::uuid)
.collect(toList()));
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java
index 647ba71f29d..3cf6000d877 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java
@@ -83,9 +83,9 @@ public class ActivityStatusAction implements CeWsAction {
Optional<ComponentDto> component = searchComponent(dbSession, request);
String componentUuid = component.isPresent() ? component.get().uuid() : null;
checkPermissions(component);
- int pendingCount = dbClient.ceQueueDao().countByStatusAndComponentUuid(dbSession, CeQueueDto.Status.PENDING, componentUuid);
- int inProgressCount = dbClient.ceQueueDao().countByStatusAndComponentUuid(dbSession, CeQueueDto.Status.IN_PROGRESS, componentUuid);
- int failingCount = dbClient.ceActivityDao().countLastByStatusAndComponentUuid(dbSession, CeActivityDto.Status.FAILED, componentUuid);
+ int pendingCount = dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, CeQueueDto.Status.PENDING, componentUuid);
+ int inProgressCount = dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, CeQueueDto.Status.IN_PROGRESS, componentUuid);
+ int failingCount = dbClient.ceActivityDao().countLastByStatusAndMainComponentUuid(dbSession, CeActivityDto.Status.FAILED, componentUuid);
return ActivityStatusWsResponse.newBuilder()
.setPending(pendingCount)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ComponentAction.java
index 5d39614fc9f..8374b8ff42d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ComponentAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ComponentAction.java
@@ -88,9 +88,9 @@ public class ComponentAction implements CeWsAction {
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto component = loadComponent(dbSession, wsRequest);
userSession.checkComponentPermission(UserRole.USER, component);
- List<CeQueueDto> queueDtos = dbClient.ceQueueDao().selectByComponentUuid(dbSession, component.uuid());
+ List<CeQueueDto> queueDtos = dbClient.ceQueueDao().selectByMainComponentUuid(dbSession, component.uuid());
CeTaskQuery activityQuery = new CeTaskQuery()
- .setComponentUuid(component.uuid())
+ .setMainComponentUuid(component.uuid())
.setOnlyCurrents(true);
List<CeActivityDto> activityDtos = dbClient.ceActivityDao().selectByQuery(dbSession, activityQuery, forPage(1).andSize(1));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/SubmitAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/SubmitAction.java
index e37e0aec33a..e606c1c8a4c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/SubmitAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/SubmitAction.java
@@ -121,7 +121,7 @@ public class SubmitAction implements CeWsAction {
CeTask task = reportSubmitter.submit(organizationKey, projectKey, deprecatedBranch, projectName, characteristics, report);
Ce.SubmitResponse submitResponse = Ce.SubmitResponse.newBuilder()
.setTaskId(task.getUuid())
- .setProjectId(task.getComponentUuid())
+ .setProjectId(task.getComponent().get().getUuid())
.build();
WsUtils.writeProtobuf(submitResponse, wsRequest, wsResponse);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index 9d6eee10437..5b127d92749 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -128,6 +128,7 @@ import org.sonar.server.platform.ws.DbMigrationStatusAction;
import org.sonar.server.platform.ws.HealthActionModule;
import org.sonar.server.platform.ws.L10nWs;
import org.sonar.server.platform.ws.LogsAction;
+import org.sonar.server.platform.ws.MigrateDataAction;
import org.sonar.server.platform.ws.MigrateDbAction;
import org.sonar.server.platform.ws.PingAction;
import org.sonar.server.platform.ws.RestartAction;
@@ -512,6 +513,7 @@ public class PlatformLevel4 extends PlatformLevel {
UpgradesAction.class,
StatusAction.class,
MigrateDbAction.class,
+ MigrateDataAction.class,
LogsAction.class,
ChangeLogLevelAction.class,
DbMigrationStatusAction.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDataAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDataAction.java
new file mode 100644
index 00000000000..0c4dde8885f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDataAction.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.ws;
+
+import org.sonar.api.config.Configuration;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.server.platform.db.migration.version.v74.PopulateTmpColumnsToCeActivity;
+import org.sonar.server.platform.db.migration.version.v74.PopulateTmpColumnsToCeQueue;
+import org.sonar.server.platform.db.migration.version.v74.PopulateTmpLastKeyColumnsToCeActivity;
+import org.sonar.server.user.UserSession;
+
+public class MigrateDataAction implements SystemWsAction {
+ private static final Logger LOG = Loggers.get(MigrateDataAction.class);
+
+ private final UserSession userSession;
+ private final DbClient dbClient;
+
+ public MigrateDataAction(UserSession userSession, DbClient dbClient) {
+ this.userSession = userSession;
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ controller.createAction("migrate_data")
+ .setPost(true)
+ .setHandler(this);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.isSystemAdministrator();
+
+ Configuration emptyConfiguration = new MapSettings().asConfig();
+ new PopulateTmpColumnsToCeQueue(dbClient.getDatabase()).execute();
+ new PopulateTmpColumnsToCeActivity(dbClient.getDatabase(), emptyConfiguration).execute();
+ new PopulateTmpLastKeyColumnsToCeActivity(dbClient.getDatabase(), emptyConfiguration).execute();
+ LOG.info("done");
+
+ response.noContent();
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
index bcbca4c5751..8474aef2e05 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
@@ -126,7 +126,8 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
}
private boolean noPendingTask(DbSession dbSession, ComponentDto rootComponent) {
- return dbClient.ceQueueDao().selectByComponentUuid(dbSession, rootComponent.uuid()).isEmpty();
+ // FIXME this is probably broken in case a branch is passed to the WS
+ return dbClient.ceQueueDao().selectByMainComponentUuid(dbSession, rootComponent.uuid()).isEmpty();
}
private void updatePermissionsToPrivate(DbSession dbSession, ComponentDto component) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
index 2a92147ee59..96bd9974b2f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
@@ -39,7 +39,6 @@ import static org.apache.commons.lang.StringUtils.defaultString;
public abstract class AbstractUserSession implements UserSession {
private static final String INSUFFICIENT_PRIVILEGES_MESSAGE = "Insufficient privileges";
- private static final ForbiddenException INSUFFICIENT_PRIVILEGES_EXCEPTION = new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
private static final String AUTHENTICATION_IS_REQUIRED_MESSAGE = "Authentication is required";
protected static Identity computeIdentity(UserDto userDto) {
@@ -191,7 +190,7 @@ public abstract class AbstractUserSession implements UserSession {
}
public static ForbiddenException insufficientPrivilegesException() {
- return INSUFFICIENT_PRIVILEGES_EXCEPTION;
+ return new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
}
@Override
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchReportSubmitterTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchReportSubmitterTest.java
new file mode 100644
index 00000000000..07f1d616ad0
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchReportSubmitterTest.java
@@ -0,0 +1,301 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.queue;
+
+import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Random;
+import java.util.function.BiConsumer;
+import java.util.stream.IntStream;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.queue.CeQueue;
+import org.sonar.ce.queue.CeQueueImpl;
+import org.sonar.ce.queue.CeTaskSubmit;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskTypes;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.component.ComponentUpdater;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.favorite.FavoriteUpdater;
+import org.sonar.server.permission.PermissionTemplateService;
+import org.sonar.server.tester.UserSessionRule;
+
+import static java.util.Collections.emptyMap;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
+import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+import static org.sonar.db.component.ComponentTesting.newBranchDto;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
+import static org.sonar.db.permission.OrganizationPermission.SCAN;
+
+/**
+ * Tests of {@link ReportSubmitter} when branch support is installed.
+ */
+@RunWith(DataProviderRunner.class)
+public class BranchReportSubmitterTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private CeQueue queue = mock(CeQueueImpl.class);
+ private ComponentUpdater componentUpdater = mock(ComponentUpdater.class);
+ private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
+ private FavoriteUpdater favoriteUpdater = mock(FavoriteUpdater.class);
+ private BranchSupportDelegate branchSupportDelegate = mock(BranchSupportDelegate.class);
+ private BranchSupport branchSupport = spy(new BranchSupport(branchSupportDelegate));
+
+ private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient(), branchSupport);
+
+ @Test
+ public void submit_does_not_use_delegate_if_characteristics_are_empty() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project);
+ mockSuccessfulPrepareSubmitCall();
+ InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8);
+
+ underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), emptyMap(), reportInput);
+
+ verifyZeroInteractions(branchSupportDelegate);
+ }
+
+ @Test
+ public void submit_a_report_on_existing_branch() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project);
+ Map<String, String> randomCharacteristics = randomNonEmptyMap();
+ BranchSupport.ComponentKey componentKey = createComponentKeyOfBranch(branch);
+ when(branchSupportDelegate.createComponentKey(project.getDbKey(), randomCharacteristics))
+ .thenReturn(componentKey);
+ InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8);
+ String taskUuid = mockSuccessfulPrepareSubmitCall();
+
+ underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), randomCharacteristics, reportInput);
+
+ verifyZeroInteractions(permissionTemplateService);
+ verifyZeroInteractions(favoriteUpdater);
+ verify(branchSupport, times(0)).createBranchComponent(any(), any(), any(), any());
+ verify(branchSupportDelegate).createComponentKey(project.getDbKey(), randomCharacteristics);
+ verify(branchSupportDelegate, times(0)).createBranchComponent(any(), any(), any(), any());
+ verifyNoMoreInteractions(branchSupportDelegate);
+ verifyQueueSubmit(project, branch, user, randomCharacteristics, taskUuid);
+ }
+
+ @Test
+ public void submit_a_report_on_missing_branch_but_existing_project() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto nonExistingProject = db.components().insertPrivateProject(organization);
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, nonExistingProject);
+ Map<String, String> randomCharacteristics = randomNonEmptyMap();
+ ComponentDto createdBranch = createButDoNotInsertBranch(nonExistingProject);
+ BranchSupport.ComponentKey componentKey = createComponentKeyOfBranch(createdBranch);
+ when(branchSupportDelegate.createComponentKey(nonExistingProject.getDbKey(), randomCharacteristics))
+ .thenReturn(componentKey);
+ when(branchSupportDelegate.createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject)))
+ .thenReturn(createdBranch);
+ InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8);
+ String taskUuid = mockSuccessfulPrepareSubmitCall();
+
+ underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), null, nonExistingProject.name(), randomCharacteristics, reportInput);
+
+ verifyZeroInteractions(permissionTemplateService);
+ verifyZeroInteractions(favoriteUpdater);
+ verify(branchSupport).createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject));
+ verify(branchSupportDelegate).createComponentKey(nonExistingProject.getDbKey(), randomCharacteristics);
+ verify(branchSupportDelegate).createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject));
+ verifyNoMoreInteractions(branchSupportDelegate);
+ verify(componentUpdater, times(0)).commitAndIndex(any(), any());
+ verifyQueueSubmit(nonExistingProject, createdBranch, user, randomCharacteristics, taskUuid);
+ }
+
+ @Test
+ public void submit_report_on_missing_branch_of_missing_project_provisions_project_when_org_PROVISION_PROJECT_perm() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto nonExistingProject = newPrivateProjectDto(organization);
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user)
+ .addPermission(PROVISION_PROJECTS, organization)
+ .addPermission(SCAN, organization);
+
+ Map<String, String> randomCharacteristics = randomNonEmptyMap();
+ ComponentDto createdBranch = createButDoNotInsertBranch(nonExistingProject);
+ BranchSupport.ComponentKey componentKey = createComponentKeyOfBranch(createdBranch);
+ when(branchSupportDelegate.createComponentKey(nonExistingProject.getDbKey(), randomCharacteristics))
+ .thenReturn(componentKey);
+ when(componentUpdater.createWithoutCommit(any(), any(), eq(user.getId()))).thenReturn(nonExistingProject);
+ when(branchSupportDelegate.createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject)))
+ .thenReturn(createdBranch);
+ when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(organization.getUuid()), any(),
+ eq(nonExistingProject.getKey()), eq(Qualifiers.PROJECT)))
+ .thenReturn(true);
+ String taskUuid = mockSuccessfulPrepareSubmitCall();
+ InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8);
+
+ underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), null, nonExistingProject.name(), randomCharacteristics, reportInput);
+
+ verify(branchSupport).createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject));
+ verify(branchSupportDelegate).createComponentKey(nonExistingProject.getDbKey(), randomCharacteristics);
+ verify(branchSupportDelegate).createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject));
+ verifyNoMoreInteractions(branchSupportDelegate);
+ verifyQueueSubmit(nonExistingProject, createdBranch, user, randomCharacteristics, taskUuid);
+ verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(nonExistingProject));
+ }
+
+ @Test
+ public void submit_fails_if_branch_support_delegate_createComponentKey_throws_an_exception() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project);
+ Map<String, String> randomCharacteristics = randomNonEmptyMap();
+ InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8);
+ RuntimeException expected = new RuntimeException("Faking an exception thrown by branchSupportDelegate");
+ when(branchSupportDelegate.createComponentKey(any(), any())).thenThrow(expected);
+
+ try {
+ underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), randomCharacteristics, reportInput);
+ fail("exception should have been thrown");
+ } catch (Exception e) {
+ assertThat(e).isSameAs(expected);
+ }
+ }
+
+ @DataProvider
+ public static Object[][] permissionsAllowingProjectProvisioning() {
+ BiConsumer<ComponentDto, UserSessionRule> noProjectPerm = (cpt, userSession) -> {
+ };
+ BiConsumer<OrganizationDto, UserSessionRule> noOrgPerm = (cpt, userSession) -> {
+ };
+ BiConsumer<ComponentDto, UserSessionRule> provisionOnProject = (cpt, userSession) -> userSession.addProjectPermission(PROVISIONING, cpt);
+ BiConsumer<OrganizationDto, UserSessionRule> provisionOnOrganization = (cpt, userSession) -> userSession.addPermission(PROVISION_PROJECTS, cpt);
+ return new Object[][] {
+ {provisionOnProject, noOrgPerm},
+ {noProjectPerm, provisionOnOrganization},
+ {provisionOnProject, provisionOnOrganization}
+ };
+ }
+
+ @Test
+ public void submit_report_on_missing_branch_of_missing_project_fails_with_ForbiddenException_if_only_scan_permission() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto nonExistingProject = newPrivateProjectDto(organization);
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, nonExistingProject);
+ Map<String, String> randomCharacteristics = randomNonEmptyMap();
+ ComponentDto createdBranch = createButDoNotInsertBranch(nonExistingProject);
+ BranchSupport.ComponentKey componentKey = createComponentKeyOfBranch(createdBranch);
+ when(branchSupportDelegate.createComponentKey(nonExistingProject.getDbKey(), randomCharacteristics))
+ .thenReturn(componentKey);
+ when(branchSupportDelegate.createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject)))
+ .thenReturn(createdBranch);
+ InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8);
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), null, nonExistingProject.name(), randomCharacteristics, reportInput);
+ }
+
+ private static ComponentDto createButDoNotInsertBranch(ComponentDto project) {
+ BranchType randomBranchType = BranchType.values()[new Random().nextInt(BranchType.values().length)];
+ BranchDto branchDto = newBranchDto(project.projectUuid(), randomBranchType);
+ return ComponentTesting.newProjectBranch(project, branchDto);
+ }
+
+ private String mockSuccessfulPrepareSubmitCall() {
+ String taskUuid = RandomStringUtils.randomAlphabetic(12);
+ when(queue.prepareSubmit()).thenReturn(new CeTaskSubmit.Builder(taskUuid));
+ return taskUuid;
+ }
+
+ private void verifyQueueSubmit(ComponentDto project, ComponentDto branch, UserDto user, Map<String, String> characteristics, String taskUuid) {
+ verify(queue).submit(argThat(submit -> submit.getType().equals(CeTaskTypes.REPORT)
+ && submit.getComponent().filter(cpt -> cpt.getUuid().equals(branch.uuid()) && cpt.getMainComponentUuid().equals(project.uuid())).isPresent()
+ && submit.getSubmitterUuid().equals(user.getUuid())
+ && submit.getCharacteristics().equals(characteristics)
+ && submit.getUuid().equals(taskUuid)));
+ }
+
+ private static BranchSupport.ComponentKey createComponentKeyOfBranch(ComponentDto branch) {
+ BranchSupport.ComponentKey mainComponentKey = mockComponentKey(branch.getKey(), branch.getKey());
+ when(mainComponentKey.getMainBranchComponentKey()).thenReturn(mainComponentKey);
+
+ BranchSupport.ComponentKey componentKey = mockComponentKey(branch.getKey(), branch.getDbKey());
+ when(componentKey.getMainBranchComponentKey()).thenReturn(mainComponentKey);
+
+ return componentKey;
+ }
+
+ private static BranchSupport.ComponentKey mockComponentKey(String key, String dbKey) {
+ BranchSupport.ComponentKey componentKey = mock(BranchSupport.ComponentKey.class);
+ when(componentKey.isDeprecatedBranch()).thenReturn(false);
+ when(componentKey.isMainBranch()).thenReturn(key.equals(dbKey));
+ when(componentKey.getKey()).thenReturn(key);
+ when(componentKey.getDbKey()).thenReturn(dbKey);
+ return componentKey;
+ }
+
+ private static ImmutableMap<String, String> randomNonEmptyMap() {
+ return IntStream.range(0, 1 + new Random().nextInt(5))
+ .boxed()
+ .collect(uniqueIndex(i -> "key_" + i, i -> "val_" + i));
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchSupportTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchSupportTest.java
new file mode 100644
index 00000000000..6c941f6571d
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/queue/BranchSupportTest.java
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.queue;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.IntStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.ce.queue.BranchSupport.ComponentKey;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+
+@RunWith(DataProviderRunner.class)
+public class BranchSupportTest {
+ private static final Map<String, String> NO_CHARACTERISTICS = Collections.emptyMap();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private BranchSupportDelegate branchSupportDelegate = mock(BranchSupportDelegate.class);
+ private BranchSupport underTestNoBranch = new BranchSupport();
+ private BranchSupport underTestWithBranch = new BranchSupport(branchSupportDelegate);
+
+ @Test
+ public void createComponentKey_of_main_branch() {
+ String projectKey = randomAlphanumeric(12);
+
+ ComponentKey componentKey = underTestNoBranch.createComponentKey(projectKey, null, NO_CHARACTERISTICS);
+
+ assertThat(componentKey)
+ .isEqualTo(underTestWithBranch.createComponentKey(projectKey, null, NO_CHARACTERISTICS));
+ assertThat(componentKey.getKey()).isEqualTo(projectKey);
+ assertThat(componentKey.getDbKey()).isEqualTo(projectKey);
+ assertThat(componentKey.isMainBranch()).isTrue();
+ assertThat(componentKey.isDeprecatedBranch()).isFalse();
+ assertThat(componentKey.getMainBranchComponentKey()).isSameAs(componentKey);
+ assertThat(componentKey.getBranchName()).isEmpty();
+ assertThat(componentKey.getPullRequestKey()).isEmpty();
+ }
+
+ @Test
+ public void createComponentKey_of_deprecated_branch() {
+ String projectKey = randomAlphanumeric(12);
+ String deprecatedBranchName = randomAlphanumeric(12);
+
+ ComponentKey componentKey = underTestNoBranch.createComponentKey(projectKey, deprecatedBranchName, NO_CHARACTERISTICS);
+
+ assertThat(componentKey)
+ .isEqualTo(underTestWithBranch.createComponentKey(projectKey, deprecatedBranchName, NO_CHARACTERISTICS));
+ assertThat(componentKey.getKey()).isEqualTo(projectKey);
+ assertThat(componentKey.getDbKey()).isEqualTo(projectKey + ":" + deprecatedBranchName);
+ assertThat(componentKey.isMainBranch()).isFalse();
+ assertThat(componentKey.isDeprecatedBranch()).isTrue();
+ assertThat(componentKey.getMainBranchComponentKey()).isSameAs(componentKey);
+ assertThat(componentKey.getBranchName()).isEmpty();
+ assertThat(componentKey.getPullRequestKey()).isEmpty();
+ }
+
+ @Test
+ @UseDataProvider("nullOrNonEmpty")
+ public void createComponentKey_with_ISE_if_characteristics_is_not_empty_and_delegate_is_null(String deprecatedBranchName) {
+ String projectKey = randomAlphanumeric(12);
+ Map<String, String> nonEmptyMap = newRandomNonEmptyMap();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Current edition does not support branch feature");
+
+ underTestNoBranch.createComponentKey(projectKey, deprecatedBranchName, nonEmptyMap);
+ }
+
+ @Test
+ public void createComponentKey_fails_with_IAE_if_characteristics_is_not_empty_and_deprecatedBranchName_is_non_null() {
+ String projectKey = randomAlphanumeric(12);
+ String deprecatedBranchName = randomAlphanumeric(13);
+ Map<String, String> nonEmptyMap = newRandomNonEmptyMap();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Deprecated branch feature can't be used at the same time as new branch support");
+
+ underTestWithBranch.createComponentKey(projectKey, deprecatedBranchName, nonEmptyMap);
+ }
+
+ @Test
+ public void createComponentKey_delegates_to_delegate_if_characteristics_is_not_empty() {
+ String projectKey = randomAlphanumeric(12);
+ Map<String, String> nonEmptyMap = newRandomNonEmptyMap();
+ ComponentKey expected = mock(ComponentKey.class);
+ when(branchSupportDelegate.createComponentKey(projectKey, nonEmptyMap)).thenReturn(expected);
+
+ ComponentKey componentKey = underTestWithBranch.createComponentKey(projectKey, null, nonEmptyMap);
+
+ assertThat(componentKey).isSameAs(expected);
+ }
+
+ @Test
+ public void createBranchComponent_fails_with_ISE_if_delegate_is_null() {
+ DbSession dbSession = mock(DbSession.class);
+ ComponentKey componentKey = mock(ComponentKey.class);
+ OrganizationDto organization = new OrganizationDto();
+ ComponentDto mainComponentDto = new ComponentDto();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Current edition does not support branch feature");
+
+ underTestNoBranch.createBranchComponent(dbSession, componentKey, organization, mainComponentDto);
+ }
+
+ @Test
+ public void createBranchComponent_delegates_to_delegate() {
+ DbSession dbSession = mock(DbSession.class);
+ ComponentKey componentKey = mock(ComponentKey.class);
+ OrganizationDto organization = new OrganizationDto();
+ ComponentDto mainComponentDto = new ComponentDto();
+ ComponentDto expected = new ComponentDto();
+ when(branchSupportDelegate.createBranchComponent(dbSession, componentKey, organization, mainComponentDto))
+ .thenReturn(expected);
+
+ ComponentDto dto = underTestWithBranch.createBranchComponent(dbSession, componentKey, organization, mainComponentDto);
+
+ assertThat(dto).isSameAs(expected);
+ }
+
+ @DataProvider
+ public static Object[][] nullOrNonEmpty() {
+ return new Object[][] {
+ {null},
+ {randomAlphabetic(5)},
+ };
+ }
+
+ private static Map<String, String> newRandomNonEmptyMap() {
+ return IntStream.range(0, 1 + new Random().nextInt(10)).boxed().collect(uniqueIndex(i -> "key_" + i, i -> "val_" + i));
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java
index 6ad3033a68b..38196fcf8a3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java
@@ -19,15 +19,16 @@
*/
package org.sonar.server.ce.queue;
-import com.google.common.collect.ImmutableMap;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
+import java.util.Random;
+import java.util.stream.IntStream;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.mockito.ArgumentCaptor;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.utils.System2;
import org.sonar.ce.queue.CeQueue;
@@ -36,7 +37,6 @@ import org.sonar.ce.queue.CeTaskSubmit;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
-import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
@@ -55,8 +55,8 @@ import org.sonar.server.tester.UserSessionRule;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyMap;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.entry;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
@@ -67,6 +67,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
@@ -92,8 +93,9 @@ public class ReportSubmitterTest {
private ComponentUpdater componentUpdater = mock(ComponentUpdater.class);
private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
private FavoriteUpdater favoriteUpdater = mock(FavoriteUpdater.class);
+ private BranchSupport ossEditionBranchSupport = new BranchSupport();
- private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient());
+ private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient(), ossEditionBranchSupport);
@Before
public void setUp() throws Exception {
@@ -102,25 +104,45 @@ public class ReportSubmitterTest {
}
@Test
- public void submit_stores_report() {
+ public void submit_with_characteristics_fails_with_ISE_when_no_branch_support_delegate() {
userSession
.addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
.addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
- mockSuccessfulPrepareSubmitCall();
ComponentDto project = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
+ mockSuccessfulPrepareSubmitCall();
when(componentUpdater.create(any(), any(), any())).thenReturn(project);
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY),
eq(Qualifiers.PROJECT)))
+ .thenReturn(true);
+ Map<String, String> nonEmptyCharacteristics = IntStream.range(0, 1 + new Random().nextInt(5))
+ .boxed()
+ .collect(uniqueIndex(i -> randomAlphabetic(i + 10), i -> randomAlphabetic(i + 20)));
+ InputStream reportInput = IOUtils.toInputStream("{binary}", UTF_8);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Current edition does not support branch feature");
+
+ underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, nonEmptyCharacteristics, reportInput);
+ }
+
+ @Test
+ public void submit_stores_report() {
+ userSession
+ .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
+ .addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
+
+ ComponentDto project = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
+ mockSuccessfulPrepareSubmitCall();
+ when(componentUpdater.createWithoutCommit(any(), any(), any())).thenReturn(project);
+ when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY),
+ eq(Qualifiers.PROJECT)))
.thenReturn(true);
- Map<String, String> taskCharacteristics = ImmutableMap.of(CeTaskCharacteristicDto.PULL_REQUEST, "123");
- underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, taskCharacteristics, IOUtils.toInputStream("{binary}", UTF_8));
+ underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));
- ArgumentCaptor<CeTaskSubmit> submittedTask = ArgumentCaptor.forClass(CeTaskSubmit.class);
- verify(queue).submit(submittedTask.capture());
- assertThat(submittedTask.getValue().getCharacteristics())
- .containsExactly(entry("pullRequest", "123"));
+ verifyReportIsPersisted(TASK_UUID);
+ verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(project));
}
@Test
@@ -128,7 +150,6 @@ public class ReportSubmitterTest {
ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project);
-
mockSuccessfulPrepareSubmitCall();
underTest.submit(defaultOrganizationKey, project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8));
@@ -137,7 +158,7 @@ public class ReportSubmitterTest {
verifyZeroInteractions(permissionTemplateService);
verifyZeroInteractions(favoriteUpdater);
verify(queue).submit(argThat(submit -> submit.getType().equals(CeTaskTypes.REPORT)
- && submit.getComponentUuid().equals(project.uuid())
+ && submit.getComponent().filter(cpt -> cpt.getUuid().equals(project.uuid()) && cpt.getMainComponentUuid().equals(project.uuid())).isPresent()
&& submit.getSubmitterUuid().equals(user.getUuid())
&& submit.getUuid().equals(TASK_UUID)));
}
@@ -151,7 +172,7 @@ public class ReportSubmitterTest {
mockSuccessfulPrepareSubmitCall();
ComponentDto createdProject = newPrivateProjectDto(organization, PROJECT_UUID).setDbKey(PROJECT_KEY);
- when(componentUpdater.create(any(), any(), isNull())).thenReturn(createdProject);
+ when(componentUpdater.createWithoutCommit(any(), any(), isNull())).thenReturn(createdProject);
when(
permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(organization.getUuid()), any(), eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
.thenReturn(true);
@@ -160,7 +181,10 @@ public class ReportSubmitterTest {
underTest.submit(organization.getKey(), PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
verifyReportIsPersisted(TASK_UUID);
- verify(queue).submit(argThat(submit -> submit.getType().equals(CeTaskTypes.REPORT) && submit.getComponentUuid().equals(PROJECT_UUID) && submit.getUuid().equals(TASK_UUID)));
+ verify(queue).submit(argThat(submit -> submit.getType().equals(CeTaskTypes.REPORT)
+ && submit.getComponent().filter(cpt -> cpt.getUuid().equals(PROJECT_UUID) && cpt.getMainComponentUuid().equals(PROJECT_UUID)).isPresent()
+ && submit.getUuid().equals(TASK_UUID)));
+ verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(createdProject));
}
@Test
@@ -169,17 +193,18 @@ public class ReportSubmitterTest {
.addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
.addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
- mockSuccessfulPrepareSubmitCall();
ComponentDto createdProject = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
- when(componentUpdater.create(any(), any(), isNull())).thenReturn(createdProject);
+ when(componentUpdater.createWithoutCommit(any(), any(), isNull())).thenReturn(createdProject);
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(),
eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
.thenReturn(true);
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(), eq(defaultOrganizationUuid), any())).thenReturn(false);
+ mockSuccessfulPrepareSubmitCall();
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
verifyZeroInteractions(favoriteUpdater);
+ verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(createdProject));
}
@Test
@@ -188,9 +213,9 @@ public class ReportSubmitterTest {
.addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
.addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
- mockSuccessfulPrepareSubmitCall();
ComponentDto project = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
- when(componentUpdater.create(any(), any(), any())).thenReturn(project);
+ mockSuccessfulPrepareSubmitCall();
+ when(componentUpdater.createWithoutCommit(any(), any(), any())).thenReturn(project);
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(),
eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
.thenReturn(true);
@@ -198,6 +223,7 @@ public class ReportSubmitterTest {
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
verify(queue).submit(any(CeTaskSubmit.class));
+ verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(project));
}
@Test
@@ -205,7 +231,6 @@ public class ReportSubmitterTest {
OrganizationDto org = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(org);
userSession.addPermission(SCAN, org);
-
mockSuccessfulPrepareSubmitCall();
underTest.submit(org.getKey(), project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
@@ -217,7 +242,6 @@ public class ReportSubmitterTest {
public void submit_a_report_on_existing_project_with_project_scan_permission() {
ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
userSession.addProjectPermission(SCAN_EXECUTION, project);
-
mockSuccessfulPrepareSubmitCall();
underTest.submit(defaultOrganizationKey, project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
@@ -230,15 +254,16 @@ public class ReportSubmitterTest {
*/
@Test
public void project_branch_must_not_benefit_from_the_scan_permission_on_main_project() {
+ String branchName = "branchFoo";
+
ComponentDto mainProject = db.components().insertPrivateProject();
userSession.addProjectPermission(GlobalPermissions.SCAN_EXECUTION, mainProject);
// user does not have the "scan" permission on the branch, so it can't scan it
- String branchName = "branchFoo";
ComponentDto branchProject = db.components().insertPrivateProject(p -> p.setDbKey(mainProject.getDbKey() + ":" + branchName));
expectedException.expect(ForbiddenException.class);
- underTest.submit(defaultOrganizationKey, mainProject.getDbKey(), branchName, PROJECT_NAME,emptyMap(), IOUtils.toInputStream("{binary}"));
+ underTest.submit(defaultOrganizationKey, mainProject.getDbKey(), branchName, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
}
@Test
@@ -298,8 +323,8 @@ public class ReportSubmitterTest {
@Test
public void fail_with_forbidden_exception_on_new_project_when_only_project_scan_permission() {
- userSession.addProjectPermission(SCAN_EXECUTION, ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID));
-
+ ComponentDto component = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID);
+ userSession.addProjectPermission(SCAN_EXECUTION, component);
mockSuccessfulPrepareSubmitCall();
when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(new ComponentDto().setUuid(PROJECT_UUID).setDbKey(PROJECT_KEY));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
index 6a584456b73..846a358ca91 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
@@ -507,7 +507,7 @@ public class ActivityActionTest {
private CeQueueDto insertQueue(String taskUuid, @Nullable ComponentDto project, CeQueueDto.Status status) {
CeQueueDto queueDto = new CeQueueDto();
queueDto.setTaskType(CeTaskTypes.REPORT);
- queueDto.setComponentUuid(project == null ? null : project.uuid());
+ queueDto.setComponent(project);
queueDto.setUuid(taskUuid);
queueDto.setStatus(status);
db.getDbClient().ceQueueDao().insert(db.getSession(), queueDto);
@@ -522,7 +522,7 @@ public class ActivityActionTest {
private CeActivityDto insertActivity(String taskUuid, ComponentDto project, Status status, @Nullable SnapshotDto analysis) {
CeQueueDto queueDto = new CeQueueDto();
queueDto.setTaskType(CeTaskTypes.REPORT);
- queueDto.setComponentUuid(project.uuid());
+ queueDto.setComponent(project);
queueDto.setUuid(taskUuid);
queueDto.setCreatedAt(EXECUTED_AT);
CeActivityDto activityDto = new CeActivityDto(queueDto);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java
index 2fc170c9867..9e7e0a5833f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java
@@ -45,9 +45,9 @@ import org.sonarqube.ws.Ce;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.ce.CeQueueTesting.newCeQueueDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.test.JsonAssert.assertJson;
import static org.sonar.server.ce.ws.CeWsParameters.DEPRECATED_PARAM_COMPONENT_KEY;
import static org.sonar.server.ce.ws.CeWsParameters.PARAM_COMPONENT_ID;
+import static org.sonar.test.JsonAssert.assertJson;
public class ActivityStatusActionTest {
@@ -84,22 +84,23 @@ public class ActivityStatusActionTest {
String anotherProjectUuid = "another-project-uuid";
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto project = newPrivateProjectDto(organizationDto, projectUuid);
+ ComponentDto anotherProject = newPrivateProjectDto(organizationDto, anotherProjectUuid);
db.components().insertComponent(project);
db.components().insertComponent(newPrivateProjectDto(organizationDto, anotherProjectUuid));
userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
// pending tasks returned
- insertInQueue(CeQueueDto.Status.PENDING, projectUuid);
- insertInQueue(CeQueueDto.Status.PENDING, projectUuid);
+ insertInQueue(CeQueueDto.Status.PENDING, project);
+ insertInQueue(CeQueueDto.Status.PENDING, project);
// other tasks not returned
- insertInQueue(CeQueueDto.Status.IN_PROGRESS, projectUuid);
- insertInQueue(CeQueueDto.Status.PENDING, anotherProjectUuid);
+ insertInQueue(CeQueueDto.Status.IN_PROGRESS, project);
+ insertInQueue(CeQueueDto.Status.PENDING, anotherProject);
insertInQueue(CeQueueDto.Status.PENDING, null);
// only one last activity for a given project
- insertActivity(CeActivityDto.Status.SUCCESS, projectUuid);
- insertActivity(CeActivityDto.Status.CANCELED, projectUuid);
- insertActivity(CeActivityDto.Status.FAILED, projectUuid);
- insertActivity(CeActivityDto.Status.FAILED, projectUuid);
- insertActivity(CeActivityDto.Status.FAILED, anotherProjectUuid);
+ insertActivity(CeActivityDto.Status.SUCCESS, project);
+ insertActivity(CeActivityDto.Status.CANCELED, project);
+ insertActivity(CeActivityDto.Status.FAILED, project);
+ insertActivity(CeActivityDto.Status.FAILED, project);
+ insertActivity(CeActivityDto.Status.FAILED, anotherProject);
Ce.ActivityStatusWsResponse result = call(projectUuid);
@@ -159,18 +160,18 @@ public class ActivityStatusActionTest {
callByComponentKey(project.getDbKey());
}
- private void insertInQueue(CeQueueDto.Status status, @Nullable String componentUuid) {
+ private void insertInQueue(CeQueueDto.Status status, @Nullable ComponentDto componentDto) {
dbClient.ceQueueDao().insert(dbSession, newCeQueueDto(Uuids.createFast())
.setStatus(status)
- .setComponentUuid(componentUuid));
+ .setComponent(componentDto));
db.commit();
}
- private void insertActivity(CeActivityDto.Status status, @Nullable String componentUuid) {
- dbClient.ceActivityDao().insert(dbSession, new CeActivityDto(
- newCeQueueDto(Uuids.createFast())
- .setComponentUuid(componentUuid))
- .setStatus(status));
+ private void insertActivity(CeActivityDto.Status status, @Nullable ComponentDto dto) {
+ CeQueueDto ceQueueDto = newCeQueueDto(Uuids.createFast());
+ ceQueueDto.setComponent(dto);
+ dbClient.ceActivityDao().insert(dbSession, new CeActivityDto(ceQueueDto)
+ .setStatus(status));
db.commit();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java
index 2236a47cc6a..784f0c60fcf 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java
@@ -62,7 +62,7 @@ public class CancelActionTest {
public void cancel_pending_task_on_project() {
logInAsSystemAdministrator();
ComponentDto project = db.components().insertPrivateProject();
- CeQueueDto queue = createTaskSubmit(project.uuid());
+ CeQueueDto queue = createTaskSubmit(project);
tester.newRequest()
.setParam("id", queue.getUuid())
@@ -87,7 +87,7 @@ public class CancelActionTest {
public void cancel_pending_task_when_system_administer() {
logInAsSystemAdministrator();
ComponentDto project = db.components().insertPrivateProject();
- CeQueueDto queue = createTaskSubmit(project.uuid());
+ CeQueueDto queue = createTaskSubmit(project);
tester.newRequest()
.setParam("id", queue.getUuid())
@@ -100,7 +100,7 @@ public class CancelActionTest {
public void cancel_pending_task_when_project_administer() {
ComponentDto project = db.components().insertPrivateProject();
userSession.addProjectPermission(UserRole.ADMIN, project);
- CeQueueDto queue = createTaskSubmit(project.uuid());
+ CeQueueDto queue = createTaskSubmit(project);
tester.newRequest()
.setParam("id", queue.getUuid())
@@ -132,7 +132,7 @@ public class CancelActionTest {
public void throw_ForbiddenException_if_not_enough_permission_when_canceling_task_on_project() {
userSession.logIn().setNonSystemAdministrator();
ComponentDto project = db.components().insertPrivateProject();
- CeQueueDto queue = createTaskSubmit(project.uuid());
+ CeQueueDto queue = createTaskSubmit(project);
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
@@ -158,7 +158,7 @@ public class CancelActionTest {
@Test
public void throw_ForbiddenException_if_not_enough_permission_when_canceling_task_when_project_does_not_exist() {
userSession.logIn().setNonSystemAdministrator();
- CeQueueDto queue = createTaskSubmit("UNKNOWN");
+ CeQueueDto queue = createTaskSubmit(nonExistentComponentDot());
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
@@ -168,16 +168,22 @@ public class CancelActionTest {
.execute();
}
+ private static ComponentDto nonExistentComponentDot() {
+ return new ComponentDto().setUuid("does_not_exist").setProjectUuid("unknown");
+ }
+
private void logInAsSystemAdministrator() {
userSession.logIn().setSystemAdministrator();
}
- private CeQueueDto createTaskSubmit(@Nullable String componentUuid) {
+ private CeQueueDto createTaskSubmit(@Nullable ComponentDto componentDto) {
CeTaskSubmit.Builder submission = queue.prepareSubmit()
.setType(CeTaskTypes.REPORT)
- .setComponentUuid(componentUuid)
.setSubmitterUuid(null)
.setCharacteristics(emptyMap());
+ if (componentDto != null) {
+ submission.setComponent(CeTaskSubmit.Component.fromDto(componentDto));
+ }
CeTask task = queue.submit(submission.build());
return db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), task.getUuid()).get();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
index d1b8d61af24..ff927dc762d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ComponentActionTest.java
@@ -281,7 +281,7 @@ public class ComponentActionTest {
private CeQueueDto insertQueue(String taskUuid, ComponentDto component, CeQueueDto.Status status) {
CeQueueDto queueDto = new CeQueueDto();
queueDto.setTaskType(CeTaskTypes.REPORT);
- queueDto.setComponentUuid(component.uuid());
+ queueDto.setComponent(component);
queueDto.setUuid(taskUuid);
queueDto.setStatus(status);
db.getDbClient().ceQueueDao().insert(db.getSession(), queueDto);
@@ -296,7 +296,7 @@ public class ComponentActionTest {
private CeActivityDto insertActivity(String taskUuid, ComponentDto component, CeActivityDto.Status status, @Nullable SnapshotDto analysis) {
CeQueueDto queueDto = new CeQueueDto();
queueDto.setTaskType(CeTaskTypes.REPORT);
- queueDto.setComponentUuid(component.uuid());
+ queueDto.setComponent(component);
queueDto.setUuid(taskUuid);
CeActivityDto activityDto = new CeActivityDto(queueDto);
activityDto.setStatus(status);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/SubmitActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/SubmitActionTest.java
index 59813b50e21..5755411dee6 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/SubmitActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/SubmitActionTest.java
@@ -51,11 +51,14 @@ import static org.mockito.Mockito.when;
public class SubmitActionTest {
+ private static final String PROJECT_UUID = "PROJECT_1";
+ private static final CeTask.Component COMPONENT = new CeTask.Component(PROJECT_UUID, "KEY_1", "NAME_1");
private static final CeTask A_CE_TASK = new CeTask.Builder()
.setOrganizationUuid("org1")
.setUuid("TASK_1")
.setType(CeTaskTypes.REPORT)
- .setComponentUuid("PROJECT_1")
+ .setComponent(COMPONENT)
+ .setMainComponent(COMPONENT)
.setSubmitterUuid("robert")
.build();
@@ -89,7 +92,7 @@ public class SubmitActionTest {
anyMap(), any());
assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1");
- assertThat(submitResponse.getProjectId()).isEqualTo("PROJECT_1");
+ assertThat(submitResponse.getProjectId()).isEqualTo(PROJECT_UUID);
}
@Test
@@ -132,7 +135,7 @@ public class SubmitActionTest {
anyMap(), any());
assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1");
- assertThat(submitResponse.getProjectId()).isEqualTo("PROJECT_1");
+ assertThat(submitResponse.getProjectId()).isEqualTo(PROJECT_UUID);
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java
index 8ad0902bf8f..3f6296abe4c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java
@@ -266,7 +266,7 @@ public class UpdateVisibilityActionTest {
}
@Test
- public void execute_throws_BadRequestException_if_specified_component_has_in_progress_tasks() {
+ public void execute_throws_BadRequestException_if_main_component_of_specified_component_has_in_progress_tasks() {
ComponentDto project = randomPublicOrPrivateProject();
IntStream.range(0, 1 + Math.abs(random.nextInt(5)))
.forEach(i -> insertInProgressTask(project));
@@ -734,7 +734,7 @@ public class UpdateVisibilityActionTest {
private void insertCeQueueDto(ComponentDto project, CeQueueDto.Status status) {
dbClient.ceQueueDao().insert(dbTester.getSession(), new CeQueueDto()
.setUuid("pending" + counter++)
- .setComponentUuid(project.uuid())
+ .setComponent(project)
.setTaskType("foo")
.setStatus(status));
dbTester.commit();