mirror of https://github.com/halo-dev/halo
Support getting backup root path in plugin (#4422)
#### 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 支持在插件中获取备份文件根目录。 ```pull/4431/head
parent
905e867eb3
commit
8e3bd7f3d8
|
@ -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<Path> {
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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<String> 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue