diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectService.java b/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectService.java index 1fe02ad04..4dbb5e76f 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectService.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectService.java @@ -1,12 +1,19 @@ package life.qbic.projectmanagement.application.api; +import java.util.Collection; import java.util.List; import static java.util.Objects.nonNull; import java.util.Optional; import java.util.Set; import java.util.UUID; +import life.qbic.projectmanagement.application.batch.SampleUpdateRequest.SampleInformation; import life.qbic.projectmanagement.application.confounding.ConfoundingVariableService.ConfoundingVariableInformation; +import life.qbic.projectmanagement.application.sample.SampleIdCodeEntry; +import life.qbic.projectmanagement.application.sample.SamplePreview; +import life.qbic.projectmanagement.domain.model.sample.Sample; +import life.qbic.projectmanagement.domain.model.sample.SampleRegistrationRequest; import org.springframework.lang.Nullable; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** @@ -26,7 +33,7 @@ public interface AsyncProjectService { /** - * Submits a project update request and returns a reactive {@link Mono} + * Submits a project update request and returns a reactive {@link Mono< ProjectUpdateResponse >} * object immediately. *

* The method implementation must be non-blocking. @@ -73,10 +80,8 @@ Mono update( Mono update(ExperimentUpdateRequest request) throws RequestFailedException, AccessDeniedException; - - /** - * Submits a project creation request and returns a {@link Mono} + * Submits a project creation request and returns a {@link Mono< ProjectCreationResponse >} * immediately. *

* This implementation must be non-blocking. @@ -93,6 +98,64 @@ Mono create(ProjectCreationRequest request) throws UnknownRequestException, RequestFailedException, AccessDeniedException; + /** + * Requests {@link SamplePreview} for a given experiment. + * + * @param projectId the project ID for the project to get the samples for + * @param experimentId the experiment ID for which the sample preview shall be retrieved + * @return a reactive stream of {@link SamplePreview} objects of the experiment + * @throws RequestFailedException if the request could not be executed + * @since 1.10.0 + */ + Flux getSamplePreviews(String projectId, String experimentId) throws RequestFailedException; + + /** + * Requests {@link SamplePreview} for a given experiment with pagination support. + * + * @param projectId the project ID for the project to get the samples for + * @param experimentId the experiment ID for which the sample preview shall be retrieved + * @param offset the offset from 0 of all available previews the returned previews should + * start + * @param limit the maximum number of previews that should be returned + * @return a reactive stream of {@link SamplePreview} objects in the experiment + * @since 1.10.0 + */ + Flux getSamplePreviews(String projectId, String experimentId, int offset, int limit); + + /** + * Requests all {@link Sample} for a given experiment. + * + * @param projectId the project ID for the project to get the samples for + * @param experimentId the experiment ID for which the samples shall be retrieved + * @return a reactive stream of {@link Sample} objects + * @throws RequestFailedException in case the request cannot be executed + * @since 1.10.0 + */ + Flux getSamples(String projectId, String experimentId) throws RequestFailedException; + + /** + * Requests all {@link Sample} for a given batch + * + * @param projectId the project ID for the project to get the samples for + * @param batchId the batch ID the samples shall be retrieved for + * @return a reactive stream of {@link Sample} objects for the given batch + * @throws RequestFailedException in case the request cannot be executed + * @since 1.10.0 + */ + Flux getSamplesForBatch(String projectId, String batchId) throws RequestFailedException; + + /** + * Find the sample ID for a given sample code + * + * @param projectId the project ID for the project to get the samples for + * @param sampleCode the sample code (e.g. Q2TEST001AE) for the project + * @return a reactive container of {@link SampleIdCodeEntry} for the sample code + * @throws RequestFailedException in case the request cannot be executed + * @since 1.10.0 + */ + Mono findSampleId(String projectId, String sampleCode) + throws RequestFailedException; + /** * Container of an update request for a service call and part of the * {@link ProjectUpdateRequest}. @@ -211,6 +274,7 @@ record ExperimentalVariable(String name, Set levels, @Nullable String un /** * Container of experimental variables. Can be used in {@link #update(ExperimentUpdateRequest)}. + * * @param experimentalVariables the list of experimental variables * @since 1.9.0 */ @@ -257,6 +321,7 @@ record ExperimentalGroup(@Nullable Long groupId, String name, int sampleSize, /** * A container for experimental groups. Can be used in {@link #update(ExperimentUpdateRequest)} + * * @param experimentalGroups the list of experimental groups * @since 1.9.0 */ @@ -293,7 +358,9 @@ record ExperimentDescription(String experimentName, Set species, Set confoundingVariables) implements @@ -307,10 +374,13 @@ record ConfoundingVariables(List confoundingVari /** * A service request to update an experiment - * @param projectId the project's identifier. The project containing the experiment. + * + * @param projectId the project's identifier. The project containing the experiment. * @param experimentId the experiment's identifier - * @param body the request body containing information on what was updated - * @param requestId The identifier of the request. Please use {@link #ExperimentUpdateRequest(String, String, ExperimentUpdateRequestBody)} if it is not determined yet. + * @param body the request body containing information on what was updated + * @param requestId The identifier of the request. Please use + * {@link #ExperimentUpdateRequest(String, String, + * ExperimentUpdateRequestBody)} if it is not determined yet. * @since 1.9.0 */ record ExperimentUpdateRequest(String projectId, String experimentId, @@ -319,9 +389,10 @@ record ExperimentUpdateRequest(String projectId, String experimentId, /** * A service request to update an experiment - * @param projectId the project's identifier. The project containing the experiment. + * + * @param projectId the project's identifier. The project containing the experiment. * @param experimentId the experiment's identifier - * @param body the request body containing information on what was updated + * @param body the request body containing information on what was updated * @since 1.9.0 */ public ExperimentUpdateRequest(String projectId, String experimentId, @@ -332,9 +403,10 @@ public ExperimentUpdateRequest(String projectId, String experimentId, /** * A service response from a {@link ExperimentUpdateRequest} + * * @param experimentId the experiment's identifier - * @param body information about the update - * @param requestId the identifier of the original request to which this is a response. + * @param body information about the update + * @param requestId the identifier of the original request to which this is a response. * @since 1.9.0 */ record ExperimentUpdateResponse(String experimentId, ExperimentUpdateResponseBody body, @@ -355,6 +427,47 @@ record ProjectCreationRequest(ProjectDesign design, ProjectContacts contacts, } + /** + * A service request to create one or more new samples for a project. + * + * @param projectId the project ID of the project the samples shall be created for + * @param requests a collection of {@link SampleRegistrationRequest} items + * @since 1.10.0 + */ + record SampleCreationRequest(String projectId, Collection requests) { + + public SampleCreationRequest(String projectId, Collection requests) { + this.projectId = projectId; + this.requests = List.copyOf(requests); + } + } + + /** + * A service request to update one or more samples in a project. + * + * @param projectId the project ID of the project the samples shall be updated in + * @param requests a collection for {@link SampleUpdate} items + * @since 1.10.0 + */ + record SampleUpdateRequest(String projectId, Collection requests) { + + public SampleUpdateRequest(String projectId, Collection requests) { + this.projectId = projectId; + this.requests = List.copyOf(requests); + } + } + + /** + * A container for a sample update request, containing the sample identifier and the updated + * information. + * + * @param sampleId the sample ID of the sample to update + * @param information the new information + * @since 1.10.0 + */ + record SampleUpdate(String sampleId, SampleInformation information) { + + } /** * A service response from a project creation request @@ -366,7 +479,6 @@ record ProjectCreationResponse(String projectId) { } - /** * A service request to update project information. * @@ -441,6 +553,7 @@ boolean hasRequestId() { * @since 1.9.0 */ class UnknownRequestException extends RuntimeException { + private String requestId; public UnknownRequestException(String message) { diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectServiceImpl.java b/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectServiceImpl.java index cc60a573d..9a597e15e 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectServiceImpl.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/api/AsyncProjectServiceImpl.java @@ -1,19 +1,21 @@ package life.qbic.projectmanagement.application.api; -import static life.qbic.logging.service.LoggerFactory.logger; -import static life.qbic.projectmanagement.application.authorization.ReactiveSecurityContextUtils.applySecurityContext; -import static life.qbic.projectmanagement.application.authorization.ReactiveSecurityContextUtils.writeSecurityContext; - import java.util.Objects; import life.qbic.logging.api.Logger; import life.qbic.logging.service.LoggerFactory; import life.qbic.projectmanagement.application.ProjectInformationService; +import static life.qbic.projectmanagement.application.authorization.ReactiveSecurityContextUtils.applySecurityContext; +import static life.qbic.projectmanagement.application.authorization.ReactiveSecurityContextUtils.writeSecurityContext; +import life.qbic.projectmanagement.application.sample.SampleIdCodeEntry; +import life.qbic.projectmanagement.application.sample.SamplePreview; import life.qbic.projectmanagement.domain.model.project.ProjectId; +import life.qbic.projectmanagement.domain.model.sample.Sample; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.util.retry.Retry; @@ -29,9 +31,9 @@ @Service public class AsyncProjectServiceImpl implements AsyncProjectService { + private static final Logger log = LoggerFactory.logger(AsyncProjectServiceImpl.class); private final ProjectInformationService projectService; private final Scheduler scheduler; - private static final Logger log = LoggerFactory.logger(AsyncProjectServiceImpl.class); public AsyncProjectServiceImpl(@Autowired ProjectInformationService projectService, @Autowired Scheduler scheduler) { @@ -39,6 +41,11 @@ public AsyncProjectServiceImpl(@Autowired ProjectInformationService projectServi this.scheduler = Objects.requireNonNull(scheduler); } + private static Retry defaultRetryStrategy() { + return Retry.maxInARow(5) + .doBeforeRetry(retrySignal -> log.warn("Operation failed (" + retrySignal + ")")); + } + @Override public Mono update(@NonNull ProjectUpdateRequest request) throws UnknownRequestException, RequestFailedException, AccessDeniedException { @@ -62,6 +69,35 @@ public Mono create(ProjectCreationRequest request) throw new RuntimeException("not implemented"); } + @Override + public Flux getSamplePreviews(String projectId, String experimentId) + throws RequestFailedException { + throw new RuntimeException("not implemented"); + } + + @Override + public Flux getSamplePreviews(String projectId, String experimentId, int offset, + int limit) { + throw new RuntimeException("not implemented"); + } + + @Override + public Flux getSamples(String projectId, String experimentId) + throws RequestFailedException { + throw new RuntimeException("not implemented"); + } + + @Override + public Flux getSamplesForBatch(String projectId, String batchId) + throws RequestFailedException { + throw new RuntimeException("not implemented"); + } + + @Override + public Mono findSampleId(String projectId, String sampleCode) + throws RequestFailedException { + throw new RuntimeException("not implemented"); + } @Override public Mono update( @@ -86,11 +122,6 @@ public Mono update( .retryWhen(defaultRetryStrategy()); } - private static Retry defaultRetryStrategy() { - return Retry.maxInARow(5) - .doBeforeRetry(retrySignal -> log.warn("Operation failed (" + retrySignal + ")")); - } - private Mono unknownRequest() { return Mono.error(() -> new UnknownRequestException("Invalid request body")); } @@ -116,7 +147,8 @@ private Mono updateExperimentalVariables(String projec } - private Mono updateProjectDesign(String projectId, ProjectDesign design, String requestId) { + private Mono updateProjectDesign(String projectId, ProjectDesign design, + String requestId) { return applySecurityContext( Mono.create(sink -> { try {