aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorSteve Marion <steve.marion@sonarsource.com>2024-05-06 18:03:36 +0200
committersonartech <sonartech@sonarsource.com>2024-05-08 20:02:44 +0000
commitcb1f5878464822e4e0caf660c9689c255f8cdb66 (patch)
treef4ebcb430c79e218bcc29e5aafbc3ecb03d80ca3 /server
parentaedbebebed4b2048458a8a7b104f74de23cd2f22 (diff)
downloadsonarqube-cb1f5878464822e4e0caf660c9689c255f8cdb66.tar.gz
sonarqube-cb1f5878464822e4e0caf660c9689c255f8cdb66.zip
SONAR-22141 implement simple linear ETA algorithm for db-migration.
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationState.java19
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImpl.java38
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MutableDatabaseMigrationState.java7
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImplTest.java55
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java4
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java18
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsController.java14
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/DateString.java37
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/CommonWebConfig.java1
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsControllerTest.java33
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/DbMigrationJsonWriter.java7
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/DbMigrationStatusActionTest.java28
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/MigrateDbActionTest.java28
13 files changed, 137 insertions, 152 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationState.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationState.java
index ff2f310e6b5..fc7bb265c9f 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationState.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationState.java
@@ -19,8 +19,9 @@
*/
package org.sonar.server.platform.db.migration;
+import java.time.Instant;
import java.util.Date;
-import javax.annotation.CheckForNull;
+import java.util.Optional;
public interface DatabaseMigrationState {
@@ -62,21 +63,19 @@ public interface DatabaseMigrationState {
/**
* The time and day the last migration was started.
* <p>
- * If no migration was ever started, the returned date is {@code null}.
+ * If no migration was ever started, the returned date is empty.
* </p>
*
- * @return a {@link Date} or {@code null}
+ * @return a {@link Date} if present
*/
- @CheckForNull
- Date getStartedAt();
+ Optional<Instant> getStartedAt();
/**
* The error of the last migration if it failed.
*
- * @return a {@link Throwable} or {@code null}
+ * @return a {@link Throwable} if present.
*/
- @CheckForNull
- Throwable getError();
+ Optional<Throwable> getError();
/**
* The amount of migrations already completed.
@@ -89,8 +88,8 @@ public interface DatabaseMigrationState {
int getTotalMigrations();
/**
- * The expected finish timestamp of the migration.
+ * The expected finish timestamp of the migration if known.
*/
- Date getExpectedFinishDate();
+ Optional<Instant> getExpectedFinishDate(Instant now);
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImpl.java
index 5951e58871b..0cd28f0c813 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImpl.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImpl.java
@@ -19,8 +19,9 @@
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
-import javax.annotation.CheckForNull;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
import javax.annotation.Nullable;
/**
@@ -29,13 +30,11 @@ import javax.annotation.Nullable;
public class DatabaseMigrationStateImpl implements MutableDatabaseMigrationState {
private Status status = Status.NONE;
@Nullable
- private Date startedAt = null;
+ private Instant startedAt = null;
@Nullable
private Throwable error = null;
private int completedMigrations = 0;
private int totalMigrations = 0;
- @Nullable
- private Date completionExpectedAt = null;
@Override
public Status getStatus() {
@@ -48,26 +47,23 @@ public class DatabaseMigrationStateImpl implements MutableDatabaseMigrationState
}
@Override
- @CheckForNull
- public Date getStartedAt() {
- return startedAt;
+ public Optional<Instant> getStartedAt() {
+ return Optional.ofNullable(startedAt);
}
@Override
- public void setStartedAt(@Nullable Date startedAt) {
+ public void setStartedAt(Instant startedAt) {
this.startedAt = startedAt;
}
@Override
- @CheckForNull
- public Throwable getError() {
- return error;
+ public Optional<Throwable> getError() {
+ return Optional.ofNullable(error);
}
@Override
public void incrementCompletedMigrations() {
completedMigrations++;
- updateExpectedFinishDate();
}
@Override
@@ -91,14 +87,12 @@ public class DatabaseMigrationStateImpl implements MutableDatabaseMigrationState
}
@Override
- public Date getExpectedFinishDate() {
- return completionExpectedAt;
- }
-
- private void updateExpectedFinishDate() {
- // Here the logic is to calculate the expected finish date based on the current time and the number of migrations completed and total
- // migrations
- this.completionExpectedAt = new Date();
+ public Optional<Instant> getExpectedFinishDate(Instant now) {
+ if (startedAt == null || totalMigrations == 0 || completedMigrations == 0) {
+ return Optional.empty();
+ }
+ Duration elapsed = Duration.between(startedAt, now);
+ double progress = (double) completedMigrations / totalMigrations;
+ return Optional.of(startedAt.plusMillis((long) (elapsed.toMillis() / progress)));
}
-
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MutableDatabaseMigrationState.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MutableDatabaseMigrationState.java
index fb1d64ce71f..2185f168284 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MutableDatabaseMigrationState.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MutableDatabaseMigrationState.java
@@ -19,15 +19,14 @@
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
-import javax.annotation.Nullable;
+import java.time.Instant;
public interface MutableDatabaseMigrationState extends DatabaseMigrationState {
void setStatus(Status status);
- void setStartedAt(@Nullable Date startedAt);
+ void setStartedAt(Instant startedAt);
- void setError(@Nullable Throwable error);
+ void setError(Throwable error);
void incrementCompletedMigrations();
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImplTest.java
index 1c2813b4c3a..3abaab81772 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImplTest.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationStateImplTest.java
@@ -19,13 +19,16 @@
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.within;
class DatabaseMigrationStateImplTest {
- private DatabaseMigrationStateImpl underTest = new DatabaseMigrationStateImpl();
+ private final DatabaseMigrationStateImpl underTest = new DatabaseMigrationStateImpl();
@Test
void getStatus_whenComponentIsCreated_shouldReturnNONE() {
@@ -42,21 +45,21 @@ class DatabaseMigrationStateImplTest {
}
@Test
- void getStartedAt_whenComponentIsCreated_shouldReturnNull() {
- assertThat(underTest.getStartedAt()).isNull();
+ void getStartedAt_whenComponentIsCreated_shouldNotBePresent() {
+ assertThat(underTest.getStartedAt()).isEmpty();
}
@Test
void getStartedAt_shouldReturnArgumentOfSetStartedAt() {
- Date expected = new Date();
+ Instant expected = Instant.now();
underTest.setStartedAt(expected);
- assertThat(underTest.getStartedAt()).isSameAs(expected);
+ assertThat(underTest.getStartedAt()).get().isSameAs(expected);
}
@Test
- void getError_whenComponentIsCreated_shouldReturnNull() {
- assertThat(underTest.getError()).isNull();
+ void getError_whenComponentIsCreated_shouldNotBePresent() {
+ assertThat(underTest.getError()).isEmpty();
}
@Test
@@ -64,7 +67,7 @@ class DatabaseMigrationStateImplTest {
RuntimeException expected = new RuntimeException();
underTest.setError(expected);
- assertThat(underTest.getError()).isSameAs(expected);
+ assertThat(underTest.getError()).get().isSameAs(expected);
}
@Test
@@ -84,13 +87,37 @@ class DatabaseMigrationStateImplTest {
}
@Test
- void incrementCompletedMigrations_shouldUpdateExpectedFinishDate() {
- Date startDate = new Date();
+ void when_noStartedMigration_expectedFinishDateShouldBeAbsent() {
+ Instant startDate = Instant.now();
+ Instant later = startDate.plus(1, ChronoUnit.MINUTES);
+ underTest.setTotalMigrations(2);
+
+ assertThat(underTest.getExpectedFinishDate(later)).isEmpty();
+ }
+
+ @Test
+ void when_noStepCompleted_expectedFinishDateShouldBeAbsent() {
+ Instant startDate = Instant.now();
+ Instant later = startDate.plus(1, ChronoUnit.MINUTES);
+
+ underTest.setStartedAt(startDate);
+ underTest.setTotalMigrations(2);
+
+ assertThat(underTest.getExpectedFinishDate(later)).isEmpty();
+ }
+
+ @Test
+ void when_StepCompleted_expectedFinishDateShouldBePresent() {
+ Instant startDate = Instant.now();
+ Instant later = startDate.plus(1, ChronoUnit.MINUTES);
+ Instant expectedEnd = startDate.plus(2, ChronoUnit.MINUTES);
+
+ underTest.setStartedAt(startDate);
+ underTest.setTotalMigrations(2);
underTest.incrementCompletedMigrations();
- // At the moment the expected finish date gets update with the timestamp of the last migration completed
- assertThat(underTest.getExpectedFinishDate()).isAfterOrEqualTo(startDate);
+ assertThat(underTest.getExpectedFinishDate(later)).get(InstanceOfAssertFactories.INSTANT)
+ .isCloseTo(expectedEnd, within(1, ChronoUnit.SECONDS));
}
-
}
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java
index 9ddd8346604..71ae6b5130e 100644
--- a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java
@@ -19,7 +19,7 @@
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
+import java.time.Instant;
import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,7 +73,7 @@ public class DatabaseMigrationImpl implements DatabaseMigration {
private void doDatabaseMigration() {
migrationState.setStatus(Status.RUNNING);
- migrationState.setStartedAt(new Date());
+ migrationState.setStartedAt(Instant.now());
migrationState.setError(null);
Profiler profiler = Profiler.create(LOGGER);
try {
diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java
index 0cab425a9ca..5e7d7b21233 100644
--- a/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java
@@ -19,7 +19,7 @@
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
+import java.time.Instant;
import org.junit.Test;
import org.mockito.InOrder;
import org.sonar.server.platform.Platform;
@@ -68,7 +68,7 @@ public class DatabaseMigrationImplTest {
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
- assertThat(migrationState.getError()).isNull();
+ assertThat(migrationState.getError()).isEmpty();
assertThat(migrationState.getStartedAt()).isNotNull();
}
@@ -79,7 +79,7 @@ public class DatabaseMigrationImplTest {
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
- assertThat(migrationState.getError()).isSameAs(AN_ERROR);
+ assertThat(migrationState.getError()).get().isSameAs(AN_ERROR);
assertThat(migrationState.getStartedAt()).isNotNull();
}
@@ -90,17 +90,19 @@ public class DatabaseMigrationImplTest {
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
- assertThat(migrationState.getError()).isSameAs(AN_ERROR);
- Date firstStartDate = migrationState.getStartedAt();
- assertThat(firstStartDate).isNotNull();
+ assertThat(migrationState.getError()).get().isSameAs(AN_ERROR);
+ assertThat(migrationState.getStartedAt()).isPresent();
+ Instant firstStartDate = migrationState.getStartedAt().get();
mockMigrationDoesNothing();
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
- assertThat(migrationState.getError()).isNull();
- assertThat(migrationState.getStartedAt()).isNotSameAs(firstStartDate);
+ assertThat(migrationState.getError()).isEmpty();
+ assertThat(migrationState.getStartedAt())
+ .get()
+ .isNotSameAs(firstStartDate);
}
private void mockMigrationThrowsError() {
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsController.java
index 0bc47b5d54e..7747e8bdf90 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsController.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsController.java
@@ -20,9 +20,10 @@
package org.sonar.server.v2.api.system.controller;
import io.swagger.v3.oas.annotations.Operation;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Optional;
-import java.util.TimeZone;
import javax.annotation.Nullable;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
@@ -44,13 +45,10 @@ public class DatabaseMigrationsController {
private final DatabaseMigrationState databaseMigrationState;
private final Database database;
- private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-
public DatabaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState, Database database) {
this.databaseVersion = databaseVersion;
this.databaseMigrationState = databaseMigrationState;
this.database = database;
- simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@Operation(summary = "Gets the status of ongoing database migrations, if any", description = "Return the detailed status of ongoing database migrations" +
@@ -86,9 +84,9 @@ public class DatabaseMigrationsController {
this(state.getStatus().toString(),
state.getCompletedMigrations(),
state.getTotalMigrations(),
- state.getStartedAt() != null ? simpleDateFormat.format(state.getStartedAt()) : null,
- state.getError() != null ? state.getError().getMessage() : state.getStatus().getMessage(),
- state.getExpectedFinishDate() != null ? simpleDateFormat.format(state.getExpectedFinishDate()) : null);
+ state.getStartedAt().map(d -> d.atZone(ZoneOffset.UTC)).map(DateTimeFormatter.ISO_DATE_TIME::format).orElse(null),
+ state.getError().map(Throwable::getMessage).orElse(state.getStatus().getMessage()),
+ state.getExpectedFinishDate(Instant.now()).map(d -> d.atZone(ZoneOffset.UTC)).map(DateTimeFormatter.ISO_DATE_TIME::format).orElse(null));
}
public DatabaseMigrationsResponse(DatabaseMigrationState.Status status) {
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/DateString.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/DateString.java
deleted file mode 100644
index 1f7cbd6c483..00000000000
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/DateString.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.v2.common;
-
-import java.util.Date;
-import javax.annotation.Nullable;
-
-public class DateString {
-
- private DateString() {
- // intentionally empty
- }
-
- public static String from(@Nullable Date date) {
- if (date == null) {
- return null;
- }
- return date.toString();
- }
-}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/CommonWebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/CommonWebConfig.java
index 72204a5d3aa..3ba5ee6eb43 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/CommonWebConfig.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/CommonWebConfig.java
@@ -74,5 +74,4 @@ public class CommonWebConfig implements WebMvcConfigurer {
public BeanFactoryPostProcessor beanFactoryPostProcessor1(SpringDocConfigProperties springDocConfigProperties) {
return beanFactory -> springDocConfigProperties.setDefaultProducesMediaType(MediaType.APPLICATION_JSON_VALUE);
}
-
}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsControllerTest.java
index dec18c62cb9..ee5eeb7b260 100644
--- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsControllerTest.java
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DatabaseMigrationsControllerTest.java
@@ -19,20 +19,22 @@
*/
package org.sonar.server.v2.api.system.controller;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Optional;
-import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
+import org.sonar.server.platform.db.migration.DatabaseMigrationStateImpl;
import org.sonar.server.platform.db.migration.version.DatabaseVersion;
import org.sonar.server.v2.api.ControllerTester;
import org.springframework.test.web.servlet.MockMvc;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.FAILED;
@@ -45,11 +47,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
class DatabaseMigrationsControllerTest {
- private static final Date SOME_DATE = new Date();
- private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-
+ private static final Instant SOME_DATE = Instant.now();
+ private static final String SOME_DATE_STRING = DateTimeFormatter.ISO_DATE_TIME.format(SOME_DATE.atZone(ZoneOffset.UTC));
private final DatabaseVersion databaseVersion = mock();
- private final DatabaseMigrationState migrationState = mock();
+ private final DatabaseMigrationStateImpl migrationState = mock();
private final Dialect dialect = mock(Dialect.class);
private final Database database = mock();
private final MockMvc mockMvc = ControllerTester.getMockMvc(new DatabaseMigrationsController(databaseVersion, migrationState, database));
@@ -101,16 +102,14 @@ class DatabaseMigrationsControllerTest {
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getExpectedFinishDate()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getExpectedFinishDate(any())).thenReturn(Optional.of(SOME_DATE));
when(migrationState.getCompletedMigrations()).thenReturn(1);
when(migrationState.getTotalMigrations()).thenReturn(10);
-
- DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_RUNNING\",\"completedSteps\":1,\"totalSteps\":10," +
- "\"message\":\"Database migration is running.\",\"expectedFinishTimestamp\":\""+DATE_FORMAT.format(SOME_DATE)+"\"}"));
+ "\"message\":\"Database migration is running.\",\"expectedFinishTimestamp\":\"" + SOME_DATE_STRING + "\"}"));
}
@Test
@@ -118,7 +117,7 @@ class DatabaseMigrationsControllerTest {
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(DatabaseMigrationState.Status.FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_FAILED\",\"message\":\"Migration failed: %s.<br/> Please check logs.\"}"));
@@ -129,7 +128,7 @@ class DatabaseMigrationsControllerTest {
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(DatabaseMigrationState.Status.SUCCEEDED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_SUCCEEDED\",\"message\":\"Migration succeeded.\"}"));
@@ -140,7 +139,7 @@ class DatabaseMigrationsControllerTest {
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(NONE);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_REQUIRED\",\"message\":\"Database migration is required. DB migration " +
@@ -152,8 +151,8 @@ class DatabaseMigrationsControllerTest {
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.FRESH_INSTALL);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(new UnsupportedOperationException("error message"));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.of(new UnsupportedOperationException("error message")));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_FAILED\",\"message\":\"error message\"}"));
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/DbMigrationJsonWriter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/DbMigrationJsonWriter.java
index 29bbe25079c..75380100545 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/DbMigrationJsonWriter.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/DbMigrationJsonWriter.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.platform.ws;
+import java.util.Date;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
@@ -40,13 +41,13 @@ public class DbMigrationJsonWriter {
json.beginObject()
.prop(FIELD_STATE, databaseMigrationState.getStatus().toString())
.prop(FIELD_MESSAGE, writeMessageIncludingError(databaseMigrationState))
- .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt())
+ .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt().map(Date::from).orElse(null))
.endObject();
}
private static String writeMessageIncludingError(DatabaseMigrationState state) {
if (state.getStatus() == FAILED) {
- Throwable error = state.getError();
+ Throwable error = state.getError().orElse(null);
return String.format(state.getStatus().getMessage(), error != null ? error.getMessage() : "No failure error");
} else {
return state.getStatus().getMessage();
@@ -64,7 +65,7 @@ public class DbMigrationJsonWriter {
json.beginObject()
.prop(FIELD_STATE, RUNNING.toString())
.prop(FIELD_MESSAGE, RUNNING.getMessage())
- .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt())
+ .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt().map(Date::from).orElse(null))
.endObject();
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/DbMigrationStatusActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/DbMigrationStatusActionTest.java
index 63b9bd4ccf7..c2afd80b44e 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/DbMigrationStatusActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/DbMigrationStatusActionTest.java
@@ -23,14 +23,15 @@ import com.google.common.collect.ImmutableList;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Arrays;
-import java.util.Date;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.sonar.api.utils.DateUtils;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
@@ -56,9 +57,10 @@ import static org.sonar.test.JsonAssert.assertJson;
@RunWith(DataProviderRunner.class)
public class DbMigrationStatusActionTest {
- private static final Date SOME_DATE = new Date();
+ private static final Instant SOME_DATE = Instant.now();
private static final String SOME_THROWABLE_MSG = "blablabla pop !";
private static final String DEFAULT_ERROR_MSG = "No failure error";
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
private DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
private Database database = mock(Database.class);
@@ -77,7 +79,7 @@ public class DbMigrationStatusActionTest {
public void verify_example() {
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(DateUtils.parseDateTime("2015-02-23T18:54:23+0100"));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(DATE_TIME_FORMATTER.parse("2015-02-23T18:54:23+0100", Instant::from)));
TestResponse response = tester.newRequest().execute();
@@ -143,7 +145,7 @@ public class DbMigrationStatusActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
@@ -156,8 +158,8 @@ public class DbMigrationStatusActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(new UnsupportedOperationException(SOME_THROWABLE_MSG));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.of(new UnsupportedOperationException(SOME_THROWABLE_MSG)));
TestResponse response = tester.newRequest().execute();
@@ -170,8 +172,8 @@ public class DbMigrationStatusActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(null); // no failure throwable caught
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.empty()); // no failure throwable caught
TestResponse response = tester.newRequest().execute();
@@ -184,7 +186,7 @@ public class DbMigrationStatusActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(SUCCEEDED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
@@ -197,7 +199,7 @@ public class DbMigrationStatusActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(NONE);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
@@ -223,11 +225,11 @@ public class DbMigrationStatusActionTest {
"}";
}
- private static String expectedResponse(String status, String msg, Date date) {
+ private static String expectedResponse(String status, String msg, Instant date) {
return "{" +
"\"state\":\"" + status + "\"," +
"\"message\":\"" + msg + "\"," +
- "\"startedAt\":\"" + DateUtils.formatDateTime(date) + "\"" +
+ "\"startedAt\":\"" + DATE_TIME_FORMATTER.format(date.atZone(ZoneOffset.systemDefault())) + "\"" +
"}";
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/MigrateDbActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/MigrateDbActionTest.java
index 7e6d2cb7ee8..8513ff7ad88 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/MigrateDbActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/MigrateDbActionTest.java
@@ -23,14 +23,15 @@ import com.google.common.collect.ImmutableList;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Arrays;
-import java.util.Date;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.sonar.api.utils.DateUtils;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;
import org.sonar.server.platform.db.migration.DatabaseMigration;
@@ -57,7 +58,7 @@ import static org.sonar.test.JsonAssert.assertJson;
@RunWith(DataProviderRunner.class)
public class MigrateDbActionTest {
- private static final Date SOME_DATE = new Date();
+ private static final Instant SOME_DATE = Instant.now();
private static final String SOME_THROWABLE_MSG = "blablabla pop !";
private static final String DEFAULT_ERROR_MSG = "No failure error";
@@ -71,6 +72,7 @@ public class MigrateDbActionTest {
private static final String MESSAGE_STATUS_NONE = "Database is up-to-date, no migration needed.";
private static final String MESSAGE_STATUS_RUNNING = "Database migration is running.";
private static final String MESSAGE_STATUS_SUCCEEDED = "Migration succeeded.";
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
private DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
private Database database = mock(Database.class);
@@ -100,7 +102,7 @@ public class MigrateDbActionTest {
public void verify_example() {
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(DateUtils.parseDateTime("2015-02-23T18:54:23+0100"));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(DATE_TIME_FORMATTER.parse("2015-02-23T18:54:23+0100", Instant::from)));
TestResponse response = tester.newRequest().execute();
@@ -154,7 +156,7 @@ public class MigrateDbActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
@@ -167,8 +169,8 @@ public class MigrateDbActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(new UnsupportedOperationException(SOME_THROWABLE_MSG));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.of(new UnsupportedOperationException(SOME_THROWABLE_MSG)));
TestResponse response = tester.newRequest().execute();
@@ -181,8 +183,8 @@ public class MigrateDbActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(null); // no failure throwable caught
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.empty()); // no failure throwable caught
TestResponse response = tester.newRequest().execute();
@@ -195,7 +197,7 @@ public class MigrateDbActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(SUCCEEDED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
@@ -208,7 +210,7 @@ public class MigrateDbActionTest {
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(NONE);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
@@ -235,11 +237,11 @@ public class MigrateDbActionTest {
"}";
}
- private static String expectedResponse(String status, String msg, Date date) {
+ private static String expectedResponse(String status, String msg, Instant date) {
return "{" +
"\"state\":\"" + status + "\"," +
"\"message\":\"" + msg + "\"," +
- "\"startedAt\":\"" + DateUtils.formatDateTime(date) + "\"" +
+ "\"startedAt\":\"" + DATE_TIME_FORMATTER.format(date.atZone(ZoneOffset.UTC)) + "\"" +
"}";
}
}