From 8e3bd7f3d8382878c0cc46f9ce3fce2c43e7af0b Mon Sep 17 00:00:00 2001 From: John Niang Date: Mon, 14 Aug 2023 19:38:11 +0800 Subject: [PATCH] Support getting backup root path in plugin (#4422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind feature /area core /milestone 2.9.x #### What this PR does / why we need it: We already support backup and restore feature in Halo 2.8.0, but we cannot obtain backup files through regular channels in the plugin. For example, we want to upload backup files to OSS in the plugin. This PR is aimed at solving this problem. #### Does this PR introduce a user-facing change? ```release-note 支持在插件中获取备份文件根目录。 ``` --- .../run/halo/app/infra/BackupRootGetter.java | 14 ++++++++ .../app/infra/DefaultBackupRootGetter.java | 20 +++++++++++ .../migration/impl/MigrationServiceImpl.java | 8 +++-- .../SharedApplicationContextHolder.java | 3 ++ .../infra/DefaultBackupRootGetterTest.java | 33 +++++++++++++++++++ .../impl/MigrationServiceImplTest.java | 33 ++++++++++++------- 6 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 api/src/main/java/run/halo/app/infra/BackupRootGetter.java create mode 100644 application/src/main/java/run/halo/app/infra/DefaultBackupRootGetter.java create mode 100644 application/src/test/java/run/halo/app/infra/DefaultBackupRootGetterTest.java diff --git a/api/src/main/java/run/halo/app/infra/BackupRootGetter.java b/api/src/main/java/run/halo/app/infra/BackupRootGetter.java new file mode 100644 index 000000000..ecf3c86ea --- /dev/null +++ b/api/src/main/java/run/halo/app/infra/BackupRootGetter.java @@ -0,0 +1,14 @@ +package run.halo.app.infra; + +import java.nio.file.Path; +import java.util.function.Supplier; + +/** + * Utility of getting backup root path. + * + * @author johnniang + * @since 2.9.0 + */ +public interface BackupRootGetter extends Supplier { + +} diff --git a/application/src/main/java/run/halo/app/infra/DefaultBackupRootGetter.java b/application/src/main/java/run/halo/app/infra/DefaultBackupRootGetter.java new file mode 100644 index 000000000..976467aa3 --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/DefaultBackupRootGetter.java @@ -0,0 +1,20 @@ +package run.halo.app.infra; + +import java.nio.file.Path; +import org.springframework.stereotype.Component; +import run.halo.app.infra.properties.HaloProperties; + +@Component +public class DefaultBackupRootGetter implements BackupRootGetter { + + private final HaloProperties haloProperties; + + public DefaultBackupRootGetter(HaloProperties haloProperties) { + this.haloProperties = haloProperties; + } + + @Override + public Path get() { + return haloProperties.getWorkDir().resolve("backups"); + } +} diff --git a/application/src/main/java/run/halo/app/migration/impl/MigrationServiceImpl.java b/application/src/main/java/run/halo/app/migration/impl/MigrationServiceImpl.java index 40d81fed6..27bb87214 100644 --- a/application/src/main/java/run/halo/app/migration/impl/MigrationServiceImpl.java +++ b/application/src/main/java/run/halo/app/migration/impl/MigrationServiceImpl.java @@ -34,6 +34,7 @@ import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; import run.halo.app.extension.store.ExtensionStore; import run.halo.app.extension.store.ExtensionStoreRepository; +import run.halo.app.infra.BackupRootGetter; import run.halo.app.infra.exception.NotFoundException; import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.utils.FileUtils; @@ -48,6 +49,8 @@ public class MigrationServiceImpl implements MigrationService { private final HaloProperties haloProperties; + private final BackupRootGetter backupRoot; + private final ObjectMapper objectMapper; private final Set excludes = Set.of( @@ -69,9 +72,10 @@ public class MigrationServiceImpl implements MigrationService { private final Scheduler scheduler = Schedulers.boundedElastic(); public MigrationServiceImpl(ExtensionStoreRepository repository, - HaloProperties haloProperties) { + HaloProperties haloProperties, BackupRootGetter backupRoot) { this.repository = repository; this.haloProperties = haloProperties; + this.backupRoot = backupRoot; this.objectMapper = JsonMapper.builder() .defaultPrettyPrinter(new MinimalPrettyPrinter()) .build(); @@ -90,7 +94,7 @@ public class MigrationServiceImpl implements MigrationService { } Path getBackupsRoot() { - return haloProperties.getWorkDir().resolve("backups"); + return backupRoot.get(); } @Override diff --git a/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java b/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java index 3559c4140..96b3a2e4a 100644 --- a/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java +++ b/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java @@ -8,6 +8,7 @@ import run.halo.app.core.extension.service.AttachmentService; import run.halo.app.extension.DefaultSchemeManager; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.BackupRootGetter; import run.halo.app.infra.ExternalUrlSupplier; /** @@ -70,6 +71,8 @@ public class SharedApplicationContextHolder { rootApplicationContext.getBean(ServerSecurityContextRepository.class)); beanFactory.registerSingleton("attachmentService", rootApplicationContext.getBean(AttachmentService.class)); + beanFactory.registerSingleton("backupRootGetter", + rootApplicationContext.getBean(BackupRootGetter.class)); // TODO add more shared instance here return sharedApplicationContext; diff --git a/application/src/test/java/run/halo/app/infra/DefaultBackupRootGetterTest.java b/application/src/test/java/run/halo/app/infra/DefaultBackupRootGetterTest.java new file mode 100644 index 000000000..bfba9f438 --- /dev/null +++ b/application/src/test/java/run/halo/app/infra/DefaultBackupRootGetterTest.java @@ -0,0 +1,33 @@ +package run.halo.app.infra; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import run.halo.app.infra.properties.HaloProperties; + +@ExtendWith(MockitoExtension.class) +class DefaultBackupRootGetterTest { + + @Mock + HaloProperties haloProperties; + + @InjectMocks + DefaultBackupRootGetter backupRootGetter; + + @Test + void shouldGetBackupRootFromWorkDir() { + when(haloProperties.getWorkDir()).thenReturn(Path.of("workdir")); + var backupRoot = this.backupRootGetter.get(); + assertEquals(Path.of("workdir", "backups"), backupRoot); + verify(haloProperties).getWorkDir(); + } + + +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/migration/impl/MigrationServiceImplTest.java b/application/src/test/java/run/halo/app/migration/impl/MigrationServiceImplTest.java index a4a1bc129..1bbac7df3 100644 --- a/application/src/test/java/run/halo/app/migration/impl/MigrationServiceImplTest.java +++ b/application/src/test/java/run/halo/app/migration/impl/MigrationServiceImplTest.java @@ -4,7 +4,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,6 +31,7 @@ import reactor.test.StepVerifier; import run.halo.app.extension.Metadata; import run.halo.app.extension.store.ExtensionStore; import run.halo.app.extension.store.ExtensionStoreRepository; +import run.halo.app.infra.BackupRootGetter; import run.halo.app.infra.exception.NotFoundException; import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.utils.FileUtils; @@ -45,6 +46,9 @@ class MigrationServiceImplTest { @Mock HaloProperties haloProperties; + @Mock + BackupRootGetter backupRoot; + @InjectMocks MigrationServiceImpl migrationService; @@ -53,21 +57,23 @@ class MigrationServiceImplTest { @Test void backupTest() throws IOException { - var startTimestamp = Instant.now(); - var backup = createRunningBackup("fake-backup", startTimestamp); Files.writeString(tempDir.resolve("fake-file"), "halo", StandardOpenOption.CREATE_NEW); var extensionStores = List.of( createExtensionStore("fake-extension-store", "fake-data") ); when(repository.findAll()).thenReturn(Flux.fromIterable(extensionStores)); when(haloProperties.getWorkDir()).thenReturn(tempDir); + when(backupRoot.get()).thenReturn(tempDir.resolve("backups")); + var startTimestamp = Instant.now(); + var backup = createRunningBackup("fake-backup", startTimestamp); StepVerifier.create(migrationService.backup(backup)) .verifyComplete(); verify(repository).findAll(); // 1. backup workdir // 2. package backup - verify(haloProperties, times(2)).getWorkDir(); + verify(haloProperties).getWorkDir(); + verify(backupRoot).get(); var status = backup.getStatus(); var datetimePart = migrationService.getDateTimeFormatter().format(startTimestamp); @@ -134,26 +140,26 @@ class MigrationServiceImplTest { @Test void cleanupBackupTest() throws IOException { - var backup = createSucceededBackup("fake-backup", "backup.zip"); - var backupFile = tempDir.resolve("workdir").resolve("backups").resolve("backup.zip"); Files.createDirectories(backupFile.getParent()); Files.createFile(backupFile); - when(haloProperties.getWorkDir()).thenReturn(tempDir.resolve("workdir")); + when(backupRoot.get()).thenReturn(tempDir.resolve("workdir").resolve("backups")); + var backup = createSucceededBackup("fake-backup", "backup.zip"); StepVerifier.create(migrationService.cleanup(backup)) .verifyComplete(); - verify(haloProperties).getWorkDir(); + verify(haloProperties, never()).getWorkDir(); + verify(backupRoot).get(); assertTrue(Files.notExists(backupFile)); } @Test void downloadBackupTest() throws IOException { - var backup = createSucceededBackup("fake-backup", "backup.zip"); var backupFile = tempDir.resolve("workdir").resolve("backups").resolve("backup.zip"); Files.createDirectories(backupFile.getParent()); Files.writeString(backupFile, "this is a backup file.", StandardOpenOption.CREATE_NEW); - when(haloProperties.getWorkDir()).thenReturn(tempDir.resolve("workdir")); + when(backupRoot.get()).thenReturn(tempDir.resolve("workdir").resolve("backups")); + var backup = createSucceededBackup("fake-backup", "backup.zip"); StepVerifier.create(migrationService.download(backup)) .assertNext(resource -> { @@ -166,16 +172,21 @@ class MigrationServiceImplTest { } }) .verifyComplete(); + + verify(haloProperties, never()).getWorkDir(); + verify(backupRoot).get(); } @Test void downloadBackupWhichDoesNotExist() { var backup = createSucceededBackup("fake-backup", "backup.zip"); - when(haloProperties.getWorkDir()).thenReturn(tempDir.resolve("workdir")); + when(backupRoot.get()).thenReturn(tempDir.resolve("workdir").resolve("backups")); StepVerifier.create(migrationService.download(backup)) .expectError(NotFoundException.class) .verify(); + verify(haloProperties, never()).getWorkDir(); + verify(backupRoot).get(); } Backup createSucceededBackup(String name, String filename) {