Skip to content

Commit 9f21aa4

Browse files
sven1103KochTobi
andauthored
Finalise service API (#1141)
* Refactor project management API * Refactor project management API * wip Co-authored-by: Sven F. <[email protected]> * make it compile and break things * remove duplicate * rename to fixme * Fix deletion and creation for optional project properties * Add java docs * Sort methods * Checkin current progress * Add more methods * Sort methods * Clean up service implementation * Fix broken implementations * Cleanup * Implement experimental variable query * Add Cacheable request * Add implementation * Address change requests * Add missing list copy Co-authored-by: Sven F. <[email protected]> --------- Co-authored-by: KochTobi <[email protected]> Co-authored-by: Sven F. <[email protected]>
1 parent 3ea116a commit 9f21aa4

File tree

8 files changed

+1655
-1293
lines changed

8 files changed

+1655
-1293
lines changed

project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectService.java

Lines changed: 1380 additions & 1062 deletions
Large diffs are not rendered by default.

project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectServiceImpl.java

Lines changed: 135 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.nio.ByteBuffer;
1111
import java.util.ArrayList;
1212
import java.util.Collection;
13-
import java.util.HashSet;
1413
import java.util.List;
1514
import java.util.Objects;
1615
import java.util.Set;
@@ -31,7 +30,6 @@
3130
import life.qbic.projectmanagement.application.api.template.TemplateService;
3231
import life.qbic.projectmanagement.application.authorization.ReactiveSecurityContextUtils;
3332
import life.qbic.projectmanagement.application.experiment.ExperimentInformationService;
34-
import life.qbic.projectmanagement.application.experiment.ExperimentInformationService.ExperimentalVariableAddition;
3533
import life.qbic.projectmanagement.application.measurement.validation.MeasurementValidationService;
3634
import life.qbic.projectmanagement.application.ontology.OntologyClass;
3735
import life.qbic.projectmanagement.application.ontology.SpeciesLookupService;
@@ -46,8 +44,10 @@
4644
import life.qbic.projectmanagement.domain.model.project.ProjectId;
4745
import life.qbic.projectmanagement.domain.model.sample.Sample;
4846
import life.qbic.projectmanagement.domain.model.sample.SampleId;
47+
import life.qbic.projectmanagement.domain.repository.ProjectRepository.ProjectNotFoundException;
4948
import org.springframework.beans.factory.annotation.Autowired;
5049
import org.springframework.lang.NonNull;
50+
import org.springframework.security.access.AccessDeniedException;
5151
import org.springframework.security.core.context.SecurityContext;
5252
import org.springframework.security.core.context.SecurityContextHolder;
5353
import org.springframework.stereotype.Service;
@@ -129,7 +129,6 @@ private static OntologyTerm convertToApi(OntologyClass term) {
129129
term.getOntologyAbbreviation());
130130
}
131131

132-
133132
@Override
134133
public Mono<ExperimentCreationResponse> create(ExperimentCreationRequest request) {
135134
throw new RuntimeException("Not implemented");
@@ -208,6 +207,12 @@ private Mono<ProjectUpdateResponse> update(ProjectId projectId, String requestId
208207
});
209208
}
210209

210+
@Override
211+
public Mono<ProjectInformation> getProject(String projectId) {
212+
// TODO implement
213+
throw new RuntimeException("Not yet implemented");
214+
}
215+
211216
@Override
212217
public Mono<ProjectCreationResponse> create(ProjectCreationRequest request)
213218
throws UnknownRequestException, RequestFailedException, AccessDeniedException {
@@ -468,7 +473,6 @@ private Mono<ValidationResponse> validateRequest(ValidationRequest request) {
468473
};
469474
}
470475

471-
472476
/**
473477
* Ensures that the security context is applied in the correct order and written when it is
474478
* required.
@@ -555,18 +559,12 @@ private Mono<ValidationResponse> validateSampleMetadata(
555559
public Mono<ExperimentUpdateResponse> update(
556560
ExperimentUpdateRequest request) {
557561
Mono<ExperimentUpdateResponse> response = switch (request.body()) {
558-
559562
case ExperimentDescription experimentDescription ->
560563
updateExperimentDescription(request.projectId(), request.experimentId(),
561564
experimentDescription);
562-
563-
case ExperimentalGroups experimentalGroups -> unknownRequest();
564565
case ConfoundingVariableAdditions confoundingVariableAdditions -> unknownRequest();
565566
case ConfoundingVariableDeletions confoundingVariableDeletions -> unknownRequest();
566567
case ConfoundingVariableUpdates confoundingVariableUpdates -> unknownRequest();
567-
case ExperimentalVariableAdditions experimentalVariableAdditions ->
568-
addExperimentalVariables(request.projectId(), experimentalVariableAdditions,
569-
ExperimentId.parse(request.experimentId()), request.requestId());
570568
};
571569

572570
SecurityContext securityContext = SecurityContextHolder.getContext();
@@ -577,54 +575,138 @@ public Mono<ExperimentUpdateResponse> update(
577575

578576
@Override
579577
public Mono<ProjectDeletionResponse> delete(ProjectDeletionRequest request) {
580-
Mono<ProjectDeletionResponse> responseMono = switch (request.body()) {
581-
case ProjectResponsibleDeletion target ->
582-
delete(request.projectId(), request.requestId(), target);
583-
case FundingDeletion target -> delete(request.projectId(), request.requestId(), target);
584-
};
585-
SecurityContext securityContext = SecurityContextHolder.getContext();
586-
return applySecurityContext(responseMono)
587-
.contextWrite(reactiveSecurity(securityContext))
588-
.retryWhen(defaultRetryStrategy())
589-
.subscribeOn(scheduler);
578+
// TODO implement
579+
throw new RuntimeException("Not yet implemented");
590580
}
591581

592582
@Override
593-
public Mono<ExperimentDeletionResponse> delete(ExperimentDeletionRequest request) {
594-
Mono<ExperimentDeletionResponse> response = switch (request.body()) {
595-
case ExperimentalVariableDeletions experimentalVariableDeletions ->
596-
deleteExperimentalVariables(request, experimentalVariableDeletions);
597-
};
598-
SecurityContext securityContext = SecurityContextHolder.getContext();
599-
return applySecurityContext(response)
600-
.contextWrite(reactiveSecurity(securityContext))
601-
.retryWhen(defaultRetryStrategy())
602-
.subscribeOn(scheduler);
583+
public Mono<FundingInformationCreationResponse> create(
584+
FundingInformationCreationRequest request) {
585+
586+
var call = Mono.fromCallable(() -> {
587+
projectService.setFunding(ProjectId.parse(request.projectId()),
588+
request.information().grant(), request.information().grantId());
589+
return new FundingInformationCreationResponse(request.requestId(), request.information(),
590+
request.projectId());
591+
});
592+
593+
return applySecurityContext(call)
594+
.subscribeOn(VirtualThreadScheduler.getScheduler())
595+
.contextWrite(reactiveSecurity(SecurityContextHolder.getContext()))
596+
.doOnError(e -> log.error("Could not create funding information", e))
597+
.onErrorMap(ProjectNotFoundException.class,
598+
e -> new RequestFailedException("Project was not found"))
599+
.retryWhen(defaultRetryStrategy());
603600
}
604601

605-
private Mono<ExperimentDeletionResponse> deleteExperimentalVariables(
606-
ExperimentDeletionRequest request,
607-
ExperimentalVariableDeletions experimentalVariableDeletions) {
608-
return Mono.fromSupplier(() -> {
609-
ExperimentId experimentId = ExperimentId.parse(request.experimentId());
610-
ProjectId projectId = ProjectId.parse(request.projectId());
611-
Experiment experiment = experimentInformationService.find(
612-
request.projectId(),
613-
experimentId)
614-
.orElseThrow(() -> new ExperimentNotFoundException("No experiment was found."));
615-
616-
var removedVariables = new ArrayList<ExperimentalVariable>();
617-
for (ExperimentalVariable experimentalVariable : experimentalVariableDeletions.experimentalVariables()) {
618-
var wasRemoved = experimentInformationService.deleteExperimentalVariable(projectId,
619-
experimentId, experimentalVariable.name());
620-
if (wasRemoved) {
621-
removedVariables.add(experimentalVariable);
622-
}
623-
}
624602

625-
return new ExperimentDeletionResponse(request.projectId(), request.experimentId(),
626-
request.requestId(), new ExperimentalVariables(removedVariables));
603+
@Override
604+
public Mono<FundingInformationDeletionResponse> delete(
605+
FundingInformationDeletionRequest request) {
606+
var call = Mono.fromCallable(() -> {
607+
projectService.removeFunding(ProjectId.parse(request.projectId()));
608+
return new FundingInformationDeletionResponse(request.requestId(), request.projectId());
609+
});
610+
611+
return applySecurityContext(call)
612+
.subscribeOn(VirtualThreadScheduler.getScheduler())
613+
.contextWrite(reactiveSecurity(SecurityContextHolder.getContext()))
614+
.doOnError(e -> log.error("Could not delete funding information", e))
615+
.onErrorMap(ProjectNotFoundException.class,
616+
e -> new RequestFailedException("Project was not found"))
617+
.retryWhen(defaultRetryStrategy());
618+
}
619+
620+
@Override
621+
public Mono<ProjectResponsibleCreationResponse> create(
622+
ProjectResponsibleCreationRequest request) {
623+
624+
var call = Mono.fromCallable(() -> {
625+
projectService.setResponsibility(ProjectId.parse(request.projectId()),
626+
request.projectResponsible());
627+
return new ProjectResponsibleCreationResponse(request.requestId(),
628+
request.projectResponsible(), request.projectId());
629+
});
630+
631+
return applySecurityContext(call)
632+
.subscribeOn(VirtualThreadScheduler.getScheduler())
633+
.contextWrite(reactiveSecurity(SecurityContextHolder.getContext()))
634+
.doOnError(e -> log.error("Could not set responsible person", e))
635+
.onErrorMap(ProjectNotFoundException.class,
636+
e -> new RequestFailedException("Project was not found"))
637+
.retryWhen(defaultRetryStrategy());
638+
}
639+
640+
@Override
641+
public Mono<ProjectResponsibleDeletionResponse> delete(
642+
ProjectResponsibleDeletionRequest request) {
643+
644+
var call = Mono.fromCallable(() -> {
645+
projectService.removeResponsibility(ProjectId.parse(request.projectId()));
646+
return new ProjectResponsibleDeletionResponse(request.requestId(), request.projectId());
627647
});
648+
649+
return applySecurityContext(call)
650+
.subscribeOn(VirtualThreadScheduler.getScheduler())
651+
.contextWrite(reactiveSecurity(SecurityContextHolder.getContext()))
652+
.doOnError(e -> log.error("Could not delete responsible person", e))
653+
.onErrorMap(ProjectNotFoundException.class,
654+
e -> new RequestFailedException("Project was not found"))
655+
.retryWhen(defaultRetryStrategy());
656+
}
657+
658+
@Override
659+
public Mono<ExperimentDeletionResponse> delete(ExperimentDeletionRequest request) {
660+
// TODO implement
661+
throw new RuntimeException("Not yet implemented");
662+
}
663+
664+
@Override
665+
public Flux<ExperimentalVariable> getExperimentalVariables(String projectId,
666+
String experimentId) {
667+
var call = Flux.fromStream(() -> experimentInformationService.getVariablesOfExperiment(projectId,
668+
ExperimentId.parse(experimentId))
669+
.stream()
670+
.map(this::convertToApi));
671+
672+
return applySecurityContextMany(call)
673+
.subscribeOn(VirtualThreadScheduler.getScheduler())
674+
.contextWrite(reactiveSecurity(SecurityContextHolder.getContext()))
675+
.doOnError(e -> log.error("Could not load experimental variables", e))
676+
.onErrorMap(org.springframework.security.access.AccessDeniedException.class, e -> new AccessDeniedException(ACCESS_DENIED))
677+
.onErrorMap(ProjectNotFoundException.class,
678+
e -> new RequestFailedException("Project was not found"))
679+
.retryWhen(defaultRetryStrategy());
680+
}
681+
682+
private ExperimentalVariable convertToApi(
683+
life.qbic.projectmanagement.domain.model.experiment.ExperimentalVariable experimentalVariable) {
684+
return new ExperimentalVariable(experimentalVariable.name().value(),
685+
experimentalVariable.levels()
686+
.stream().map(level -> level.variableName().value()).collect(
687+
Collectors.toSet()),
688+
experimentalVariable.levels().getFirst().experimentalValue().unit().orElse(null));
689+
}
690+
691+
@Override
692+
public Mono<ExperimentalVariablesCreationResponse> create(
693+
ExperimentalVariablesCreationRequest request) {
694+
// TODO implement
695+
throw new RuntimeException("Not yet implemented");
696+
}
697+
698+
@Override
699+
public Mono<ExperimentalVariablesUpdateResponse> update(
700+
ExperimentalVariablesUpdateRequest request) {
701+
// TODO implement
702+
throw new RuntimeException("Not yet implemented");
703+
}
704+
705+
@Override
706+
public Mono<ExperimentalVariablesDeletionResponse> delete(
707+
ExperimentalVariablesDeletionRequest request) {
708+
// TODO implement
709+
throw new RuntimeException("Not yet implemented");
628710
}
629711

630712
private Mono<ProjectDeletionResponse> delete(String projectId, String requestId,
@@ -668,33 +750,8 @@ private <T> Mono<T> unknownRequest() {
668750
private Mono<ExperimentUpdateResponse> updateExperimentDescription(String projectId,
669751
String experimentId,
670752
ExperimentDescription experimentDescription) {
671-
throw new RuntimeException("Not implemented");
672-
}
673-
674-
private Mono<ExperimentUpdateResponse> addExperimentalVariables(
675-
String projectId,
676-
ExperimentalVariableAdditions experimentalVariableAdditions, ExperimentId experimentId,
677-
String requestId) {
678-
679-
var variableAdditions = experimentalVariableAdditions.experimentalVariables().stream()
680-
.map(experimentalVariable -> new ExperimentalVariableAddition(experimentalVariable.name(),
681-
experimentalVariable.unit(), java.util.List.copyOf(experimentalVariable.levels())))
682-
.toList();
683-
684-
return applySecurityContext(Mono.fromSupplier(
685-
() -> experimentInformationService.addVariablesToExperiment(projectId, experimentId,
686-
variableAdditions)
687-
))
688-
.map(experimentalVariableInformation -> experimentalVariableInformation.stream()
689-
.map(info -> new ExperimentalVariable(info.name(), new HashSet<>(info.levels()),
690-
info.unit())
691-
)
692-
.toList())
693-
.map(ExperimentalVariables::new)
694-
.map(experimentalVariables ->
695-
new ExperimentUpdateResponse(experimentId.value(), experimentalVariables,
696-
requestId)
697-
).subscribeOn(scheduler);
753+
// TODO implement
754+
throw new RuntimeException("Not yet implemented");
698755
}
699756

700757
private Mono<ProjectUpdateResponse> update(ProjectId projectId, String requestId,
@@ -715,12 +772,4 @@ private Mono<ProjectUpdateResponse> update(ProjectId projectId, String requestId
715772
}
716773
);
717774
}
718-
719-
public static class ExperimentNotFoundException extends RuntimeException {
720-
721-
public ExperimentNotFoundException(String message) {
722-
super(message);
723-
}
724-
}
725-
726775
}

project-management/src/main/java/life/qbic/projectmanagement/application/experiment/ExperimentInformationService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ public ExperimentInformationService(@Autowired ExperimentRepository experimentRe
6464
this.sampleInformationService = sampleInformationService;
6565
}
6666

67+
68+
@PreAuthorize(
69+
"hasPermission(#projectId, 'life.qbic.projectmanagement.domain.model.project.Project', 'READ') ")
70+
public Optional<Experiment> find(String projectId, String experimentId) {
71+
Objects.requireNonNull(experimentId);
72+
return experimentRepository.find(ExperimentId.parse(experimentId));
73+
}
74+
75+
6776
@PreAuthorize(
6877
"hasPermission(#projectId, 'life.qbic.projectmanagement.domain.model.project.Project', 'READ') ")
6978
public Optional<Experiment> find(String projectId, ExperimentId experimentId) {

0 commit comments

Comments
 (0)