From: Sébastien Lesaint Date: Wed, 26 Sep 2018 06:58:59 +0000 (+0200) Subject: SONAR-11310 add temporary columns to CE tables X-Git-Tag: 7.5~402 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2c540713f9289d8cfd14a65f3b4c3c33a4696e20;p=sonarqube.git SONAR-11310 add temporary columns to CE tables - add main_component_uuid temporary columns to CE_QUEUE - add main_last_key and main_component_uuid columns to CE_ACTIVITY - back to initial paradigm in Compute Engine: even for branches/PRs, the row in table PROJECTS a task belongs to is created in api/ce/submit - add main component concept to CeTask - improved consistency check when processing a report task to account for row in PROJECTS now being the one of the branche/PR - stronger validation of characteristics passed to api/ce/submit - add api/system/migrate_data for SonarCloud online data migration --- 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 { *

* 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 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 submit(CeTaskSubmit submission, SubmitOption... options) { + public Optional submit(CeTaskSubmit submission, SubmitOption... options) { return submit(submission, toSet(options)); } - private java.util.Optional submit(CeTaskSubmit submission, EnumSet submitOptions) { + private Optional submit(CeTaskSubmit submission, EnumSet 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 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 loadComponentDtos(DbSession dbSession, CeQueueDto taskDto) { + Set 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 massSubmit(Collection submissions, SubmitOption... options) { if (submissions.isEmpty()) { @@ -109,11 +129,11 @@ public class CeQueueImpl implements CeQueue { } try (DbSession dbSession = dbClient.openSession(false)) { - List taskDto = submissions.stream() + List taskDtos = submissions.stream() .filter(filterBySubmitOptions(options, submissions, dbSession)) .map(submission -> addToQueueInDb(dbSession, submission)) .collect(Collectors.toList()); - List tasks = loadTasks(dbSession, taskDto); + List tasks = loadTasks(dbSession, taskDtos); dbSession.commit(); return tasks; } @@ -122,31 +142,34 @@ public class CeQueueImpl implements CeQueue { private Predicate filterBySubmitOptions(SubmitOption[] options, Collection submissions, DbSession dbSession) { EnumSet submitOptions = toSet(options); - if (submitOptions.contains(UNIQUE_QUEUE_PER_COMPONENT)) { - Set componentUuids = submissions.stream() - .map(CeTaskSubmit::getComponentUuid) - .filter(Objects::nonNull) + if (submitOptions.contains(UNIQUE_QUEUE_PER_MAIN_COMPONENT)) { + Set 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 { - private final Map queuedItemsByComponentUuid; + private final Map queuedItemsByMainComponentUuid; - private NoPendingTaskFilter(DbSession dbSession, Set componentUuids) { - queuedItemsByComponentUuid = dbClient.ceQueueDao().countByStatusAndComponentUuids(dbSession, PENDING, componentUuids); + private NoPendingTaskFilter(DbSession dbSession, Set 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 loadTasks(DbSession dbSession, List dtos) { // load components, if defined Set componentUuids = dtos.stream() - .map(CeQueueDto::getComponentUuid) + .flatMap(dto -> Stream.of(dto.getComponentUuid(), dto.getMainComponentUuid())) .filter(Objects::nonNull) .collect(Collectors.toSet()); Map componentsByUuid = dbClient.componentDao() @@ -194,13 +219,15 @@ public class CeQueueImpl implements CeQueue { List 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 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 propValue = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.COMPUTE_ENGINE_PAUSE); + Optional 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 characteristics, @Nullable ComponentDto component) { + CeTask convertToTask(CeQueueDto taskDto, Map 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 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 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 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 task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT); + Optional 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 task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT); + Optional 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 task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT); + Optional 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 task = underTest.submit(taskSubmit, UNIQUE_QUEUE_PER_COMPONENT); + Optional 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 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 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 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 tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT); + List 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 tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT); + List 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 tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT); + List 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 tasks = underTest.massSubmit(of(taskSubmit), UNIQUE_QUEUE_PER_COMPONENT); + List 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 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 tasks = underTest.massSubmit(of(taskSubmit1, taskSubmit2, taskSubmit3, taskSubmit4, taskSubmit5), UNIQUE_QUEUE_PER_COMPONENT); + List 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 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 = 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 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 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 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 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 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 = 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 = 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 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; @@ -165,10 +166,44 @@ public class LoadReportAnalysisMetadataHolderStepTest { assertThat(analysisMetadataHolder.isCrossProjectDuplicationEnabled()).isEqualTo(false); } + @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 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 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 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 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 getComponent() { + return Optional.ofNullable(component); } - @CheckForNull - public String getComponentName() { - return componentName; + public Optional 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 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 getKey() { + return Optional.ofNullable(key); + } + + public Optional 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(); @@ -91,15 +96,40 @@ public class CeTaskTest { underTest.build(); } + @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,25 +138,11 @@ 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") 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 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 componentsByUuid = loadComponentDtos(dbSession, taskDto); Map 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}. + *

+ * 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}. + *

+ * 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 selectByComponentUuid(@Param("componentUuid") String componentUuid); - List selectByQuery(@Param("query") CeTaskQuery query, @Param("pagination") Pagination pagination); List 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 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 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 selectByComponentUuid(DbSession session, String componentUuid) { - return mapper(session).selectByComponentUuid(componentUuid); + public List selectByMainComponentUuid(DbSession session, String projectUuid) { + return mapper(session).selectByMainComponentUuid(projectUuid); } public Optional 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 countByStatusAndComponentUuids(DbSession dbSession, CeQueueDto.Status status, Set componentUuids) { - if (componentUuids.isEmpty()) { + public Map countByStatusAndMainComponentUuids(DbSession dbSession, CeQueueDto.Status status, Set projectUuids) { + if (projectUuids.isEmpty()) { return emptyMap(); } ImmutableMap.Builder builder = ImmutableMap.builder(); executeLargeUpdates( - componentUuids, + projectUuids, uuids -> { - List i = mapper(dbSession).countByStatusAndComponentUuids(status, componentUuids); - i.forEach(o -> builder.put(o.getComponentUuid(), o.getTotal())); + List 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}. + *

+ * 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}. + *

+ * 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 selectByComponentUuid(@Param("componentUuid") String componentUuid); + List selectByMainComponentUuid(@Param("mainComponentUuid") String mainComponentUuid); List 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 countByStatusAndComponentUuids(@Param("status") CeQueueDto.Status status, @Param("componentUuids") Set componentUuids); + List countByStatusAndMainComponentUuids(@Param("status") CeQueueDto.Status status, @Param("mainComponentUuids") Set 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 componentUuids; + private ArrayList mainComponentUuids; private ArrayList statuses; private String type; private Long minSubmittedAt; private Long maxExecutedAt; @CheckForNull - public List getComponentUuids() { - return componentUuids; + public List getMainComponentUuids() { + return mainComponentUuids; } - public CeTaskQuery setComponentUuids(@Nullable List l) { - this.componentUuids = l == null ? null : newArrayList(l); + public CeTaskQuery setMainComponentUuids(@Nullable List 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 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 - and ca.is_last=${_true} + and ca.tmp_main_is_last=${_true} - - and ca.component_uuid in - + + and ca.tmp_main_component_uuid in + #{cUuid,jdbcType=VARCHAR} @@ -118,26 +121,32 @@ ca.created_at < #{beforeDate,jdbcType=BIGINT} - select count(1) from ce_activity where - status=#{status,jdbcType=VARCHAR} - and is_last=${_true} - - and component_uuid=#{componentUuid,jdbcType=VARCHAR} + tmp_main_is_last=${_true} + and status=#{status,jdbcType=VARCHAR} + + and tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR} 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 @@ ) - - update ce_activity - set is_last=${_false}, - updated_at=#{updatedAt,jdbcType=BIGINT} - where is_last=${_true} and is_last_key=#{isLastKey,jdbcType=VARCHAR} + + 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}) 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 @@ 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 count(1) from ce_queue where status=#{status,jdbcType=VARCHAR} - - and component_uuid=#{componentUuid,jdbcType=VARCHAR} + + and tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR} - 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 - - #{cUuid,jdbcType=VARCHAR} + and tmp_main_component_uuid in + + #{mainComponentUuid,jdbcType=VARCHAR} - group by component_uuid + group by tmp_main_component_uuid - select from ce_queue cq where - cq.component_uuid=#{componentUuid,jdbcType=VARCHAR} + cq.tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR} @@ -105,10 +106,10 @@ from ce_queue cq - - and cq.component_uuid in - - #{cUuid,jdbcType=VARCHAR} + + and cq.tmp_main_component_uuid in + + #{mainComponentUuid,jdbcType=VARCHAR} @@ -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 <> 'PENDING' ) @@ -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 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 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 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 from ce_activity where component_uuid=#{projectUuid,jdbcType=VARCHAR} + delete from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR} 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 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 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 from ce_queue where component_uuid=#{projectUuid,jdbcType=VARCHAR} + delete from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR} 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 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 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 dtos = underTest.selectByQuery(db.getSession(), new CeTaskQuery().setComponentUuid("PROJECT_1"), forPage(1).andSize(100)); + List 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 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,8 +33,79 @@ 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) { 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 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 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,11 +47,29 @@ 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 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 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 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 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 branchCharacteristics(String branchType, String branchName) { + return ImmutableMap.of("branchType", branchType, "branch", branchName); + } + + private static Map 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 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 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 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 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 branchCharacteristics(String branchType, String branchName) { + return ImmutableMap.of("branchType", branchType, "branch", branchName); + } + + private static Map 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, Tuple, ObjectAssert> 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, Tuple, ObjectAssert> 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}. + *

+ * 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 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 getDeprecatedBranchName(); + + Optional getBranchName(); + + Optional 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 getDeprecatedBranchName() { + return Optional.ofNullable(deprecatedBranchName); + } + + @Override + public Optional getBranchName() { + return Optional.empty(); + } + + @Override + public Optional 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 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 characteristics, - InputStream reportInput) { + public CeTask submit(String organizationKey, String projectKey, @Nullable String deprecatedBranch, @Nullable String projectName, + Map characteristics, InputStream reportInput) { try (DbSession dbSession = dbClient.openSession(false)) { OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey); - String effectiveProjectKey = ComponentKeys.createKey(projectKey, deprecatedBranch); - Optional 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 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 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 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 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 queueDtos = dbClient.ceQueueDao().selectByComponentUuid(dbSession, component.uuid()); + List queueDtos = dbClient.ceQueueDao().selectByMainComponentUuid(dbSession, component.uuid()); CeTaskQuery activityQuery = new CeTaskQuery() - .setComponentUuid(component.uuid()) + .setMainComponentUuid(component.uuid()) .setOnlyCurrents(true); List 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 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 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 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 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 noProjectPerm = (cpt, userSession) -> { + }; + BiConsumer noOrgPerm = (cpt, userSession) -> { + }; + BiConsumer provisionOnProject = (cpt, userSession) -> userSession.addProjectPermission(PROVISIONING, cpt); + BiConsumer 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 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 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 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 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 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 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 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 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 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 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 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();