Skip to content

Commit

Permalink
added create new project from project backup
Browse files Browse the repository at this point in the history
  • Loading branch information
soimugeo committed Dec 11, 2024
1 parent b170b71 commit 9fcc1d5
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import edu.stanford.protege.webprotege.hierarchy.NamedHierarchyManager;
import edu.stanford.protege.webprotege.hierarchy.NamedHierarchyManagerImpl;
import edu.stanford.protege.webprotege.hierarchy.NamedHierarchyRepository;
import edu.stanford.protege.webprotege.icd.projects.*;
import edu.stanford.protege.webprotege.index.*;
import edu.stanford.protege.webprotege.inject.*;
import edu.stanford.protege.webprotege.inject.project.ProjectDirectoryFactory;
Expand Down Expand Up @@ -565,6 +566,11 @@ CommandExecutor<CreateInitialRevisionHistoryRequest, CreateInitialRevisionHistor
return new CommandExecutorImpl<>(CreateInitialRevisionHistoryResponse.class);
}

@Bean
CommandExecutor<PrepareBackupFilesForUseRequest, PrepareBackupFilesForUseResponse> executorForPrepareBackupFilesForUse() {
return new CommandExecutorImpl<>(PrepareBackupFilesForUseResponse.class);
}

@Bean
MinioClient minioClient(MinioProperties properties) {
return MinioClient.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package edu.stanford.protege.webprotege.icd.projects;

import com.fasterxml.jackson.annotation.*;
import edu.stanford.protege.webprotege.common.Request;
import edu.stanford.protege.webprotege.csv.DocumentId;

import static edu.stanford.protege.webprotege.icd.projects.PrepareBackupFilesForUseRequest.CHANNEL;

@JsonTypeName(CHANNEL)
public record PrepareBackupFilesForUseRequest(
@JsonProperty("fileSubmissionId") DocumentId fileSubmissionId
) implements Request<PrepareBackupFilesForUseResponse> {

public static final String CHANNEL = "icatx.versioning.PrepareBinaryFileBackupForUse";

@Override
public String getChannel() {
return CHANNEL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package edu.stanford.protege.webprotege.icd.projects;

import com.fasterxml.jackson.annotation.*;
import edu.stanford.protege.webprotege.common.BlobLocation;
import edu.stanford.protege.webprotege.dispatch.Result;

import static edu.stanford.protege.webprotege.icd.projects.PrepareBackupFilesForUseRequest.CHANNEL;

@JsonTypeName(CHANNEL)
public record PrepareBackupFilesForUseResponse(
@JsonProperty("binaryFileLocation") BlobLocation binaryFileLocation
) implements Result {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package edu.stanford.protege.webprotege.project;

import com.fasterxml.jackson.annotation.*;
import com.google.common.base.Objects;
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotege.dispatch.Action;

import static com.google.common.base.MoreObjects.toStringHelper;

@JsonTypeName(CreateNewProjectFromProjectBackupAction.CHANNEL)
public record CreateNewProjectFromProjectBackupAction(
@JsonProperty("newProjectId") ProjectId newProjectId,
@JsonProperty("newProjectSettings") NewProjectSettings newProjectSettings
) implements Action<CreateNewProjectFromProjectBackupResult> {

public static final String CHANNEL = "webprotege.projects.CreateNewProjectFromProjectBackup";

@Override
public String getChannel() {
return CHANNEL;
}

@Override
public int hashCode() {
return Objects.hashCode(newProjectId, newProjectSettings);
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CreateNewProjectFromProjectBackupAction)) {
return false;
}
CreateNewProjectFromProjectBackupAction other = (CreateNewProjectFromProjectBackupAction) obj;
return this.newProjectId.equals(other.newProjectId) && this.newProjectSettings.equals(other.newProjectSettings);
}

@Override
public String toString() {
return toStringHelper("CreateNewProjectFromProjectBackupAction")
.addValue(newProjectId)
.addValue(newProjectSettings)
.toString();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package edu.stanford.protege.webprotege.project;

import edu.stanford.protege.webprotege.authorization.*;
import edu.stanford.protege.webprotege.ipc.*;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import java.util.*;

import static edu.stanford.protege.webprotege.access.BuiltInAction.*;


@WebProtegeHandler
public class CreateNewProjectFromProjectBackupCommandHandler implements AuthorizedCommandHandler<CreateNewProjectFromProjectBackupAction, CreateNewProjectFromProjectBackupResult> {


private final CreateProjectSagaManager createProjectSagaManager;


public CreateNewProjectFromProjectBackupCommandHandler(CreateProjectSagaManager createProjectSagaManager) {
this.createProjectSagaManager = createProjectSagaManager;
}

@Nonnull
@Override
public String getChannelName() {
return CreateNewProjectFromProjectBackupAction.CHANNEL;
}

@Override
public Class<CreateNewProjectFromProjectBackupAction> getRequestClass() {
return CreateNewProjectFromProjectBackupAction.class;
}

@Override
public Mono<CreateNewProjectFromProjectBackupResult> handleRequest(CreateNewProjectFromProjectBackupAction request,
ExecutionContext executionContext) {
var result = createProjectSagaManager.executeFromBackup(request.newProjectSettings(), executionContext);
return Mono.fromFuture(result);
}

@Nonnull
@Override
public Resource getTargetResource(CreateNewProjectFromProjectBackupAction request) {
return ApplicationResource.get();
}

@Nonnull
@Override
public Collection<ActionId> getRequiredCapabilities() {
return List.of(CREATE_EMPTY_PROJECT.getActionId(), UPLOAD_PROJECT.getActionId());
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package edu.stanford.protege.webprotege.project;

import com.fasterxml.jackson.annotation.*;
import com.google.common.base.Objects;
import edu.stanford.protege.webprotege.dispatch.Result;

import static com.google.common.base.MoreObjects.toStringHelper;
import static edu.stanford.protege.webprotege.icd.projects.PrepareBackupFilesForUseRequest.CHANNEL;

@JsonTypeName(CHANNEL)
public record CreateNewProjectFromProjectBackupResult(
@JsonProperty("projectDetails") ProjectDetails projectDetails
) implements Result {

public static CreateNewProjectFromProjectBackupResult create(ProjectDetails projectDetails) {
return new CreateNewProjectFromProjectBackupResult(projectDetails);
}


@Override
public int hashCode() {
return Objects.hashCode(projectDetails);
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CreateNewProjectFromProjectBackupResult)) {
return false;
}
CreateNewProjectFromProjectBackupResult other = (CreateNewProjectFromProjectBackupResult) obj;
return this.projectDetails.equals(other.projectDetails);
}


@Override
public String toString() {
return toStringHelper("CreateNewProjectFromProjectBackupResult")
.addValue(projectDetails)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import edu.stanford.protege.webprotege.common.BlobLocation;
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotege.icd.projects.*;
import edu.stanford.protege.webprotege.ipc.CommandExecutor;
import edu.stanford.protege.webprotege.ipc.ExecutionContext;
import edu.stanford.protege.webprotege.ontology.ProcessUploadedOntologiesRequest;
Expand All @@ -16,8 +17,7 @@
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.*;

/**
* Matthew Horridge
Expand All @@ -34,6 +34,7 @@ public class CreateProjectSagaManager {
private final CommandExecutor<ProcessUploadedOntologiesRequest, ProcessUploadedOntologiesResponse> processOntologiesExecutor;

private final CommandExecutor<CreateInitialRevisionHistoryRequest, CreateInitialRevisionHistoryResponse> createInitialRevisionHistoryExecutor;
private final CommandExecutor<PrepareBackupFilesForUseRequest, PrepareBackupFilesForUseResponse> prepareBinaryFileBackupForUseExecutor;

private final MinioFileDownloader fileDownloader;

Expand All @@ -45,12 +46,14 @@ public class CreateProjectSagaManager {
public CreateProjectSagaManager(ProjectDetailsManager projectDetailsManager,
CommandExecutor<ProcessUploadedOntologiesRequest, ProcessUploadedOntologiesResponse> processOntologiesExecutor,
CommandExecutor<CreateInitialRevisionHistoryRequest, CreateInitialRevisionHistoryResponse> createInitialRevisionHistoryExecutor,
CommandExecutor<PrepareBackupFilesForUseRequest, PrepareBackupFilesForUseResponse> prepareBinaryFileBackupForUseExecutor,
MinioFileDownloader fileDownloader,
RevisionHistoryReplacer revisionHistoryReplacer,
ProjectPermissionsInitializer projectPermissionsInitializer) {
this.projectDetailsManager = projectDetailsManager;
this.processOntologiesExecutor = processOntologiesExecutor;
this.createInitialRevisionHistoryExecutor = createInitialRevisionHistoryExecutor;
this.prepareBinaryFileBackupForUseExecutor = prepareBinaryFileBackupForUseExecutor;
this.revisionHistoryReplacer = revisionHistoryReplacer;
this.fileDownloader = fileDownloader;
this.projectPermissionsInitializer = projectPermissionsInitializer;
Expand All @@ -66,6 +69,8 @@ public CompletableFuture<CreateNewProjectResult> execute(NewProjectSettings newP
}
}



private CompletableFuture<CreateNewProjectResult> createEmptyProject(SagaState sagaState) {
logger.info("Creating an empty project: {}", sagaState.newProjectSettings);
return registerProject(sagaState).thenCompose(this::initializeProjectPermissions)
Expand Down Expand Up @@ -105,6 +110,41 @@ private CompletableFuture<CreateNewProjectResult> createProjectFromSources(SagaS
});
}

public CompletableFuture<CreateNewProjectFromProjectBackupResult> executeFromBackup(NewProjectSettings newProjectSettings, ExecutionContext executionContext) {
if (newProjectSettings.hasSourceDocument()) {
return createProjectFromBackupFile(new SagaStateWithSources(ProjectId.generate(), newProjectSettings, executionContext));
}
return null;
}

private CompletableFuture<CreateNewProjectFromProjectBackupResult> createProjectFromBackupFile(SagaStateWithSources sagaState) {
logger.info("Creating an empty project: {}", sagaState.getNewProjectSettings());
return prepareBackupFilesForRestore(sagaState)
.thenCompose(this::downloadRevisionHistory)
.thenCompose(this::copyRevisionHistoryToProject)
.thenCompose(this::registerProject)
.thenCompose(this::initializeProjectPermissions)
.thenCompose(this::retrieveProjectDetails)
.handle((r, e) -> {
if (e == null) {
return new CreateNewProjectFromProjectBackupResult(r.getProjectDetails());
}
else {
// Should be a CompletionException
logger.error("Error creating project", e);
throw new ProjectCreationException(sagaState.getProjectId(),
"Project creation failed",
e.getCause());
}
});
}

private CompletableFuture<SagaStateWithSources> prepareBackupFilesForRestore(SagaStateWithSources sagaState) {
var createHistoryRequest = sagaState.createPrepareBackupFilesForUseRequest();
return prepareBinaryFileBackupForUseExecutor.execute(createHistoryRequest, sagaState.getExecutionContext())
.thenApply(sagaState::handleBackupFilesReady);
}

private CompletableFuture<SagaState> retrieveProjectDetails(SagaState sagaState) {
return CompletableFuture.supplyAsync(() -> projectDetailsManager.getProjectDetails(sagaState.getProjectId()))
.thenApply(sagaState::setProjectDetails);
Expand Down Expand Up @@ -230,6 +270,15 @@ public SagaStateWithSources handleProcessUploadedOntologiesResponse(ProcessUploa
return this;
}

public SagaStateWithSources handleBackupFilesReady(PrepareBackupFilesForUseResponse response) {
this.revisionHistoryLocation = response.binaryFileLocation();
return this;
}

public PrepareBackupFilesForUseRequest createPrepareBackupFilesForUseRequest() {
return new PrepareBackupFilesForUseRequest(getNewProjectSettings().sourceDocument());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotege.common.UserId;
import edu.stanford.protege.webprotege.csv.DocumentId;
import edu.stanford.protege.webprotege.icd.projects.*;
import edu.stanford.protege.webprotege.ipc.CommandExecutor;
import edu.stanford.protege.webprotege.ipc.ExecutionContext;
import edu.stanford.protege.webprotege.ontology.ProcessUploadedOntologiesRequest;
Expand Down Expand Up @@ -43,6 +44,9 @@ class CreateProjectSagaManagerTest {
@Mock
private CommandExecutor<CreateInitialRevisionHistoryRequest, CreateInitialRevisionHistoryResponse> createInitialRevisionHistoryExecutor;

@Mock
private CommandExecutor<PrepareBackupFilesForUseRequest, PrepareBackupFilesForUseResponse> prepareBinaryFileBackupForUseExecutor;

@Mock
private MinioFileDownloader fileDownloader;

Expand All @@ -69,6 +73,7 @@ void setUp() {
manager = new CreateProjectSagaManager(projectDetailsManager,
processOntologiesExecutor,
createInitialRevisionHistoryExecutor,
prepareBinaryFileBackupForUseExecutor,
fileDownloader,
revisionHistoryReplacer,
projectPermissionsInitializer);
Expand Down

0 comments on commit 9fcc1d5

Please sign in to comment.