diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/EingangForwarder.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/EingangForwarder.java new file mode 100644 index 0000000000000000000000000000000000000000..5136f428512fca9ff395b2700578f63875a21bf4 --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/EingangForwarder.java @@ -0,0 +1,238 @@ +package de.ozgcloud.vorgang.vorgang.redirect; + +import java.io.InputStream; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Function; + +import com.google.protobuf.ByteString; + +import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils; +import de.ozgcloud.eingang.forwarder.RouteForwardingServiceGrpc; +import de.ozgcloud.eingang.forwarding.GrpcAttachment; +import de.ozgcloud.eingang.forwarding.GrpcFileContent; +import de.ozgcloud.eingang.forwarding.GrpcRepresentation; +import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding; +import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest; +import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse; +import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor; +import de.ozgcloud.vorgang.files.FileService; +import de.ozgcloud.vorgang.vorgang.IncomingFile; +import de.ozgcloud.vorgang.vorgang.IncomingFileGroup; +import de.ozgcloud.vorgang.vorgang.IncomingFileMapper; +import io.grpc.stub.ClientCallStreamObserver; +import io.grpc.stub.ClientResponseObserver; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +class EingangForwarder { + + private final RouteForwardingServiceGrpc.RouteForwardingServiceStub serviceStub; + private final FileService fileService; + + private final IncomingFileMapper incomingFileMapper; + + private ForwardingResponseObserver responseObserver; + private ClientCallStreamObserver<GrpcRouteForwardingRequest> requestObserver; + + public CompletableFuture<Void> forward(GrpcRouteForwarding grpcRouteForwarding, List<IncomingFileGroup> attachments, + List<IncomingFile> representations) { + return CompletableFuture.allOf( + callService(), + sendRouteForwarding(grpcRouteForwarding) + .thenCompose(ignored -> sendAttachments(attachments)) + .thenCompose(ignored -> sendRepresentations(representations)) + .whenComplete((result, ex) -> { + if (ex != null) { + responseObserver.onError(ex); + } else { + responseObserver.onCompleted(); + } + }) + ); + } + + CompletableFuture<GrpcRouteForwardingResponse> callService() { + CompletableFuture<GrpcRouteForwardingResponse> responseFuture = new CompletableFuture<>(); + responseObserver = new ForwardingResponseObserver(responseFuture); + requestObserver = (ClientCallStreamObserver<GrpcRouteForwardingRequest>) serviceStub.withInterceptors( + new VorgangManagerClientCallContextAttachingInterceptor()) + .routeForwarding(responseObserver); + return responseFuture; + } + + CompletableFuture<GrpcRouteForwardingResponse> sendRouteForwarding(GrpcRouteForwarding grpcRouteForwarding) { + CompletableFuture<GrpcRouteForwardingResponse> future = new CompletableFuture<>(); + responseObserver.registerOnReadyHandler(() -> { + requestObserver.onNext(GrpcRouteForwardingRequest.newBuilder().setRouteForwarding(grpcRouteForwarding).build()); + future.complete(GrpcRouteForwardingResponse.newBuilder().build()); + }); + return future; + } + + CompletableFuture<GrpcRouteForwardingResponse> sendAttachments(List<IncomingFileGroup> attachments) { + return attachments.stream() + .flatMap(attachment -> { + var groupName = attachment.getName(); + return attachment.getFiles().stream().map(file -> getSendAttachmentFileFunction(groupName, file)); + }) + .reduce( + CompletableFuture.completedFuture(GrpcRouteForwardingResponse.newBuilder().build()), + CompletableFuture::thenCompose, + (f1, f2) -> f1.thenCompose(ignored -> f2) + ); + } + + private Function<GrpcRouteForwardingResponse, CompletableFuture<GrpcRouteForwardingResponse>> getSendAttachmentFileFunction(String groupName, + IncomingFile file) { + return ignored -> sendAttachmentFile(groupName, file); + } + + private CompletableFuture<GrpcRouteForwardingResponse> sendAttachmentFile(String groupName, IncomingFile file) { + var fileContentStream = fileService.getUploadedFileStream(file.getId()); + var sender = createAttachmentFileSender(groupName, file, fileContentStream).send(responseObserver::registerOnReadyHandler); + return sender.getResultFuture(); + } + + GrpcFileUploadUtils.FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createAttachmentFileSender(String groupName, + IncomingFile file, + InputStream fileContentStream) { + return createSenderWithoutMetadata(this::buildAttachmentChunk, fileContentStream) + .withMetaData(buildGrpcAttachmentFile(groupName, file)); + } + + GrpcRouteForwardingRequest buildAttachmentChunk(byte[] chunk, int length) { + return GrpcRouteForwardingRequest.newBuilder() + .setAttachment(GrpcAttachment.newBuilder() + .setContent(buildGrpcFileContent(chunk, length)) + .build()) + .build(); + } + + GrpcRouteForwardingRequest buildGrpcAttachmentFile(String name, IncomingFile file) { + return GrpcRouteForwardingRequest.newBuilder() + .setAttachment(GrpcAttachment.newBuilder() + .setFile(incomingFileMapper.toAttachmentFile(name, file)) + .build()) + .build(); + } + + CompletableFuture<GrpcRouteForwardingResponse> sendRepresentations(List<IncomingFile> representations) { + return representations.stream() + .map(this::getSendRepresentationFileFunction) + .reduce( + CompletableFuture.completedFuture(GrpcRouteForwardingResponse.newBuilder().build()), + CompletableFuture::thenCompose, + (f1, f2) -> f1.thenCompose(ignored -> f2) + ); + } + + private Function<GrpcRouteForwardingResponse, CompletableFuture<GrpcRouteForwardingResponse>> getSendRepresentationFileFunction(IncomingFile file) { + return ignored -> sendRepresentationFile(file); + } + + private CompletableFuture<GrpcRouteForwardingResponse> sendRepresentationFile(IncomingFile file) { + var fileContentStream = fileService.getUploadedFileStream(file.getId()); + var sender = createRepresentationFileSender(file, fileContentStream).send(responseObserver::registerOnReadyHandler); + return sender.getResultFuture(); + } + + GrpcFileUploadUtils.FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createRepresentationFileSender(IncomingFile file, + InputStream fileContentStream) { + return createSenderWithoutMetadata(this::buildRepresentationChunk, fileContentStream).withMetaData(buildGrpcRepresentationFile(file)); + } + + GrpcRouteForwardingRequest buildRepresentationChunk(byte[] chunk, int length) { + return GrpcRouteForwardingRequest.newBuilder() + .setRepresentation(GrpcRepresentation.newBuilder() + .setContent(buildGrpcFileContent(chunk, length)) + .build()) + .build(); + } + + GrpcRouteForwardingRequest buildGrpcRepresentationFile(IncomingFile file) { + return GrpcRouteForwardingRequest.newBuilder() + .setRepresentation(GrpcRepresentation.newBuilder() + .setFile(incomingFileMapper.toRepresentationFile(file)) + .build()) + .build(); + } + + GrpcFileContent buildGrpcFileContent(byte[] chunk, int length) { + var fileContentBuilder = GrpcFileContent.newBuilder(); + if (length <= 0) { + fileContentBuilder.setIsEndOfFile(true); + } else { + fileContentBuilder.setContent(ByteString.copyFrom(chunk)); + } + return fileContentBuilder.build(); + } + + GrpcFileUploadUtils.FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createSenderWithoutMetadata( + BiFunction<byte[], Integer, GrpcRouteForwardingRequest> chunkBuilder, InputStream fileContentStream) { + return GrpcFileUploadUtils.createSender(chunkBuilder, fileContentStream, response -> requestObserver, false); + } + + @RequiredArgsConstructor + static class ForwardingResponseObserver implements ClientResponseObserver<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> { + private final CompletableFuture<GrpcRouteForwardingResponse> future; + private DelegatingOnReadyHandler onReadyHandler; + private GrpcRouteForwardingResponse response; + + @Override + public void beforeStart(ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStream) { + onReadyHandler = new DelegatingOnReadyHandler(requestStream); + requestStream.setOnReadyHandler(onReadyHandler); + } + + @Override + public void onNext(GrpcRouteForwardingResponse response) { + this.response = response; + } + + @Override + public void onError(Throwable t) { + onReadyHandler.stop(); + future.completeExceptionally(t); + } + + @Override + public void onCompleted() { + onReadyHandler.stop(); + future.complete(response); + } + + public void registerOnReadyHandler(Runnable onReadyHandler) { + this.onReadyHandler.setDelegate(onReadyHandler); + } + } + + @RequiredArgsConstructor + static class DelegatingOnReadyHandler implements Runnable { + + private final ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStream; + private final AtomicReference<Runnable> onReadyHandler = new AtomicReference<>(); + private final AtomicBoolean done = new AtomicBoolean(false); + + public void setDelegate(Runnable onReadyHandler) { + this.onReadyHandler.set(onReadyHandler); + } + + public void stop() { + done.set(true); + } + + @Override + public void run() { + while (!done.get() && requestStream.isReady()) { + var runnable = onReadyHandler.get(); + if (runnable != null) { + runnable.run(); + } + } + } + } +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java index 5edee75f41c32c2428c42ab12bb9211433abf089..371374eca99a57ee7d85200e254df8d20348851b 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java @@ -23,39 +23,18 @@ */ package de.ozgcloud.vorgang.vorgang.redirect; -import java.io.InputStream; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.BiFunction; -import java.util.function.Function; import org.springframework.stereotype.Service; -import com.google.protobuf.ByteString; - -import de.ozgcloud.common.binaryfile.BinaryFileUploadStreamObserver; -import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils; -import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils.FileSender; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.eingang.forwarder.RouteForwardingServiceGrpc; -import de.ozgcloud.eingang.forwarding.GrpcAttachment; -import de.ozgcloud.eingang.forwarding.GrpcFileContent; -import de.ozgcloud.eingang.forwarding.GrpcRepresentation; -import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest; -import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse; -import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor; import de.ozgcloud.vorgang.files.FileService; -import de.ozgcloud.vorgang.vorgang.Eingang; -import de.ozgcloud.vorgang.vorgang.IncomingFile; -import de.ozgcloud.vorgang.vorgang.IncomingFileGroup; import de.ozgcloud.vorgang.vorgang.IncomingFileMapper; import de.ozgcloud.vorgang.vorgang.VorgangService; -import io.grpc.stub.CallStreamObserver; -import io.grpc.stub.ClientCallStreamObserver; -import io.grpc.stub.StreamObserver; import lombok.RequiredArgsConstructor; import net.devh.boot.grpc.client.inject.GrpcClient; @@ -63,7 +42,7 @@ import net.devh.boot.grpc.client.inject.GrpcClient; @RequiredArgsConstructor class ForwardingRemoteService { - private static final int TIMEOUT_MINUTES = 2; + private static final int TIMEOUT_MINUTES = 10; private final VorgangService vorgangService; private final ForwardingRequestMapper forwardingRequestMapper; @GrpcClient("forwarder") @@ -72,124 +51,10 @@ class ForwardingRemoteService { private final IncomingFileMapper incomingFileMapper; public void forward(ForwardingRequest request) { - CompletableFuture<Void> responseFuture = new CompletableFuture<>(); - routeForwarding(request, new ForwardingResponseObserver(responseFuture)); - waitForCompletion(responseFuture); - } - - void routeForwarding(ForwardingRequest request, ForwardingResponseObserver responseObserver) { - var requestStreamObserver = (ClientCallStreamObserver<GrpcRouteForwardingRequest>) serviceStub.withInterceptors(new VorgangManagerClientCallContextAttachingInterceptor()) - .routeForwarding(responseObserver); - try { - sendEingang(request, requestStreamObserver); - requestStreamObserver.onCompleted(); - } catch (Exception e) { - requestStreamObserver.onError(e); - throw e; - } - } - - void sendEingang(ForwardingRequest request, ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver) { var eingang = vorgangService.getById(request.getVorgangId()).getEingangs().getFirst(); - requestStreamObserver.onNext(buildRouteForwardingRequest(request, eingang)); - sendAttachments(eingang.getAttachments(), requestStreamObserver); - sendRepresentations(eingang.getRepresentations(), requestStreamObserver); - } - - GrpcRouteForwardingRequest buildRouteForwardingRequest(ForwardingRequest request, Eingang eingang) { - var routeForwarding = forwardingRequestMapper.toGrpcRouteForwarding(request, eingang); - return GrpcRouteForwardingRequest.newBuilder().setRouteForwarding(routeForwarding).build(); - } - - void sendAttachments(List<IncomingFileGroup> attachments, ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver) { - for (var attachment : attachments) { - var groupName = attachment.getName(); - attachment.getFiles().forEach(file -> sendAttachmentFile(requestStreamObserver, groupName, file)); - } - } - - private void sendAttachmentFile(ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver, String groupName, IncomingFile file) { - var fileContentStream = fileService.getUploadedFileStream(file.getId()); - var sender = createAttachmentFileSender(requestStreamObserver, groupName, file, fileContentStream).send(); - waitForCompletion(sender.getResultFuture()); - } - - FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createAttachmentFileSender( - ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver, String groupName, IncomingFile file, InputStream fileContentStream) { - return createSenderWithoutMetadata(this::buildAttachmentChunk, requestStreamObserver, fileContentStream) - .withMetaData(buildGrpcAttachmentFile(groupName, file)); - } - - GrpcRouteForwardingRequest buildAttachmentChunk(byte[] chunk, int length) { - return GrpcRouteForwardingRequest.newBuilder() - .setAttachment(GrpcAttachment.newBuilder() - .setContent(buildGrpcFileContent(chunk, length)) - .build()) - .build(); - } - - GrpcRouteForwardingRequest buildGrpcAttachmentFile(String name, IncomingFile file) { - return GrpcRouteForwardingRequest.newBuilder() - .setAttachment(GrpcAttachment.newBuilder() - .setFile(incomingFileMapper.toAttachmentFile(name, file)) - .build()) - .build(); - } - - void sendRepresentations(List<IncomingFile> representations, ClientCallStreamObserver<GrpcRouteForwardingRequest> requestObserver) { - representations.forEach(representation -> { - var fileContentStream = fileService.getUploadedFileStream(representation.getId()); - var sender = createRepresentationFileSender(requestObserver, representation, fileContentStream).send(); - waitForCompletion(sender.getResultFuture()); - }); - } - - FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createRepresentationFileSender( - ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver, IncomingFile file, InputStream fileContentStream) { - return createSenderWithoutMetadata(this::buildRepresentationChunk, requestStreamObserver, fileContentStream) - .withMetaData(buildGrpcRepresentationFile(file)); - } - - FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createSenderWithoutMetadata( - BiFunction<byte[], Integer, GrpcRouteForwardingRequest> chunkBuilder, - ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver, InputStream fileContentStream) { - return GrpcFileUploadUtils - .createSender(chunkBuilder, fileContentStream, requestCallStreamObserverProvider(requestStreamObserver), false); - } - - private Function<StreamObserver<GrpcRouteForwardingResponse>, CallStreamObserver<GrpcRouteForwardingRequest>> requestCallStreamObserverProvider( - ClientCallStreamObserver<GrpcRouteForwardingRequest> requestStreamObserver) { - // responseObserver should be passed to GrpcService used to transfer files, otherwise onNext()-method won't be called - return response -> { - ((BinaryFileUploadStreamObserver<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse>) response).beforeStart(requestStreamObserver); - return (CallStreamObserver<GrpcRouteForwardingRequest>) requestStreamObserver; - }; - } - - GrpcRouteForwardingRequest buildRepresentationChunk(byte[] chunk, int length) { - return GrpcRouteForwardingRequest.newBuilder() - .setRepresentation(GrpcRepresentation.newBuilder() - .setContent(buildGrpcFileContent(chunk, length)) - .build()) - .build(); - } - - GrpcFileContent buildGrpcFileContent(byte[] chunk, int length) { - var fileContentBuilder = GrpcFileContent.newBuilder(); - if (length <= 0) { - fileContentBuilder.setIsEndOfFile(true); - } else { - fileContentBuilder.setContent(ByteString.copyFrom(chunk)); - } - return fileContentBuilder.build(); - } - - GrpcRouteForwardingRequest buildGrpcRepresentationFile(IncomingFile file) { - return GrpcRouteForwardingRequest.newBuilder() - .setRepresentation(GrpcRepresentation.newBuilder() - .setFile(incomingFileMapper.toRepresentationFile(file)) - .build()) - .build(); + var grpcRouteForwarding = forwardingRequestMapper.toGrpcRouteForwarding(request, eingang); + var responseFuture = new EingangForwarder(serviceStub, fileService, incomingFileMapper).forward(grpcRouteForwarding, eingang.getAttachments(), eingang.getRepresentations()); + waitForCompletion(responseFuture); } <T> void waitForCompletion(CompletableFuture<T> responseFuture) { @@ -204,24 +69,4 @@ class ForwardingRemoteService { throw new TechnicalException("Timeout on uploading file content.", e); } } - - @RequiredArgsConstructor - static class ForwardingResponseObserver implements StreamObserver<GrpcRouteForwardingResponse> { - private final CompletableFuture<Void> future; - - @Override - public void onNext(GrpcRouteForwardingResponse value) { - // noop - } - - @Override - public void onError(Throwable t) { - future.completeExceptionally(t); - } - - @Override - public void onCompleted() { - future.complete(null); - } - } } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java deleted file mode 100644 index d39685c147df39baf62ae0eea6ffcf3ec74802d9..0000000000000000000000000000000000000000 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java +++ /dev/null @@ -1,796 +0,0 @@ -///* -// * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den -// * Ministerpräsidenten des Landes Schleswig-Holstein -// * Staatskanzlei -// * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung -// * -// * Lizenziert unter der EUPL, Version 1.2 oder - sobald -// * diese von der Europäischen Kommission genehmigt wurden - -// * Folgeversionen der EUPL ("Lizenz"); -// * Sie dürfen dieses Werk ausschließlich gemäß -// * dieser Lizenz nutzen. -// * Eine Kopie der Lizenz finden Sie hier: -// * -// * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 -// * -// * Sofern nicht durch anwendbare Rechtsvorschriften -// * gefordert oder in schriftlicher Form vereinbart, wird -// * die unter der Lizenz verbreitete Software "so wie sie -// * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - -// * ausdrücklich oder stillschweigend - verbreitet. -// * Die sprachspezifischen Genehmigungen und Beschränkungen -// * unter der Lizenz sind dem Lizenztext zu entnehmen. -// */ -//package de.ozgcloud.vorgang.vorgang.redirect; -// -//import static org.assertj.core.api.Assertions.*; -//import static org.junit.jupiter.api.Assertions.*; -//import static org.mockito.ArgumentMatchers.*; -//import static org.mockito.Mockito.*; -// -//import java.io.InputStream; -//import java.util.List; -//import java.util.concurrent.CompletableFuture; -//import java.util.concurrent.ExecutionException; -//import java.util.concurrent.TimeUnit; -//import java.util.concurrent.TimeoutException; -//import java.util.function.BiFunction; -//import java.util.function.Function; -// -//import org.apache.commons.lang3.RandomUtils; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Nested; -//import org.junit.jupiter.api.Test; -//import org.mockito.ArgumentCaptor; -//import org.mockito.Captor; -//import org.mockito.InjectMocks; -//import org.mockito.Mock; -//import org.mockito.MockedStatic; -//import org.mockito.Spy; -// -//import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils; -//import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils.FileSender; -//import de.ozgcloud.common.errorhandling.TechnicalException; -//import de.ozgcloud.common.test.ReflectionTestUtils; -//import de.ozgcloud.eingang.forwarder.RouteForwardingServiceGrpc; -//import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding; -//import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest; -//import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse; -//import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor; -//import de.ozgcloud.vorgang.files.FileService; -//import de.ozgcloud.vorgang.vorgang.Eingang; -//import de.ozgcloud.vorgang.vorgang.EingangTestFactory; -//import de.ozgcloud.vorgang.vorgang.IncomingFile; -//import de.ozgcloud.vorgang.vorgang.IncomingFileGroup; -//import de.ozgcloud.vorgang.vorgang.IncomingFileGroupTestFactory; -//import de.ozgcloud.vorgang.vorgang.IncomingFileMapper; -//import de.ozgcloud.vorgang.vorgang.IncomingFileTestFactory; -//import de.ozgcloud.vorgang.vorgang.Vorgang; -//import de.ozgcloud.vorgang.vorgang.VorgangService; -//import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; -//import de.ozgcloud.vorgang.vorgang.redirect.ForwardingRemoteService.ForwardingResponseObserver; -//import io.grpc.stub.CallStreamObserver; -//import io.grpc.stub.StreamObserver; -//import lombok.SneakyThrows; -// -//class ForwardingRemoteServiceTest { -// -// @Spy -// @InjectMocks -// private ForwardingRemoteService service; -// @Mock -// private VorgangService vorgangService; -// @Mock -// private ForwardingRequestMapper forwardingRequestMapper; -// @Mock -// private RouteForwardingServiceGrpc.RouteForwardingServiceStub serviceStub; -// @Mock -// private FileService fileService; -// @Mock -// private IncomingFileMapper incomingFileMapper; -// -// @Mock -// private StreamObserver<GrpcRouteForwardingRequest> requestObserver; -// private final ForwardingRequest request = ForwardingRequestTestFactory.create(); -// private final Eingang eingang = EingangTestFactory.create(); -// private final Vorgang vorgang = VorgangTestFactory.createBuilder().clearEingangs().eingang(eingang).build(); -// -// @Nested -// class TestForward { -// -// @Captor -// private ArgumentCaptor<ForwardingResponseObserver> responseObserverCaptor; -// @Captor -// private ArgumentCaptor<CompletableFuture<Void>> futureCaptor; -// -// @BeforeEach -// void init() { -// doNothing().when(service).routeForwarding(any(), any()); -// doNothing().when(service).waitForCompletion(any()); -// } -// -// @Test -// void shouldRouteForwarding() { -// forward(); -// -// verify(service).routeForwarding(eq(request), any(ForwardingResponseObserver.class)); -// } -// -// @Test -// void shouldWaitForCompletion() { -// forward(); -// -// verify(service).waitForCompletion(futureCaptor.capture()); -// verify(service).routeForwarding(any(), responseObserverCaptor.capture()); -// assertThat(futureCaptor.getValue()) -// .isSameAs(ReflectionTestUtils.getField(responseObserverCaptor.getValue(), "future", CompletableFuture.class)); -// } -// -// private void forward() { -// service.forward(request); -// } -// } -// -// @Nested -// class TestRouteForwarding { -// -// @Mock -// private ForwardingResponseObserver responseObserver; -// -// @BeforeEach -// void init() { -// when(serviceStub.withInterceptors(any())).thenReturn(serviceStub); -// } -// -// @Test -// void shouldAttachClientCallContextToServiceStub() { -// givenGrpcCallCompletedSuccessfully(); -// doNothing().when(service).sendEingang(any(), any()); -// -// routeForwarding(); -// -// verify(serviceStub).withInterceptors(any(VorgangManagerClientCallContextAttachingInterceptor.class)); -// } -// -// @Test -// void shouldMakeGrpcCallToRouteForwarding() { -// givenGrpcCallCompletedSuccessfully(); -// doNothing().when(service).sendEingang(any(), any()); -// -// routeForwarding(); -// -// verify(serviceStub).routeForwarding(responseObserver); -// } -// -// @Nested -// class OnSuccess { -// -// @BeforeEach -// void init() { -// givenGrpcCallCompletedSuccessfully(); -// doNothing().when(service).sendEingang(any(), any()); -// } -// -// @Test -// void shouldSendEingang() { -// routeForwarding(); -// -// verify(service).sendEingang(request, requestObserver); -// } -// -// @Test -// void shouldCallOnCompleted() { -// routeForwarding(); -// -// verify(requestObserver).onCompleted(); -// } -// } -// -// @Nested -// class OnFailure { -// -// private final RuntimeException error = new RuntimeException(); -// -// @BeforeEach -// void init() { -// givenGrpcCallCompletedSuccessfully(); -// doThrow(error).when(service).sendEingang(any(), any()); -// } -// -// @SuppressWarnings("ResultOfMethodCallIgnored") -// @Test -// void shouldCallOnError() { -// catchThrowableOfType(RuntimeException.class, TestRouteForwarding.this::routeForwarding); -// -// verify(requestObserver).onError(error); -// } -// -// @Test -// void shouldThrowError() { -// assertThatThrownBy(TestRouteForwarding.this::routeForwarding).isSameAs(error); -// } -// } -// -// private void givenGrpcCallCompletedSuccessfully() { -// when(serviceStub.routeForwarding(any())).thenAnswer(invocation -> { -// ((ForwardingResponseObserver) invocation.getArgument(0)).onCompleted(); -// return requestObserver; -// }); -// } -// -// private void routeForwarding() { -// service.routeForwarding(request, responseObserver); -// } -// } -// -// @Nested -// class TestSendEingang { -// -// private final GrpcRouteForwardingRequest routeForwardingRequest = GrpcRouteForwardingRequestTestFactory.create(); -// -// @BeforeEach -// void init() { -// when(vorgangService.getById(any())).thenReturn(vorgang); -// doReturn(routeForwardingRequest).when(service).buildRouteForwardingRequest(any(), any()); -// doNothing().when(service).sendAttachments(any(), any()); -// doNothing().when(service).sendRepresentations(any(), any()); -// } -// -// @Test -// void shouldGetVorgangById() { -// sendEingang(); -// -// verify(vorgangService).getById(VorgangTestFactory.ID); -// } -// -// @Test -// void shouldBuildRouteForwardingRequest() { -// sendEingang(); -// -// verify(service).buildRouteForwardingRequest(request, eingang); -// } -// -// @Test -// void shouldSendForwardingRequest() { -// sendEingang(); -// -// verify(requestObserver).onNext(routeForwardingRequest); -// } -// -// @Test -// void shouldCallSendAttachments() { -// sendEingang(); -// -// verify(service).sendAttachments(List.of(EingangTestFactory.ATTACHMENT), requestObserver); -// } -// -// @Test -// void shouldCallSendRepresentations() { -// sendEingang(); -// -// verify(service).sendRepresentations(List.of(EingangTestFactory.REPRESENTATION), requestObserver); -// } -// -// private void sendEingang() { -// service.sendEingang(request, requestObserver); -// } -// } -// -// @Nested -// class TestBuildRouteForwardingRequest { -// -// private final GrpcRouteForwarding routeForwarding = GrpcRouteForwardingTestFactory.create(); -// -// @BeforeEach -// void init() { -// when(forwardingRequestMapper.toGrpcRouteForwarding(any(), any())).thenReturn(routeForwarding); -// } -// -// @Test -// void shouldMapToRouteForwarding() { -// buildRouteForwardingRequest(); -// -// verify(forwardingRequestMapper).toGrpcRouteForwarding(request, eingang); -// } -// -// @Test -// void shouldReturnRouteForwardingRequest() { -// var builtRequest = buildRouteForwardingRequest(); -// -// assertThat(builtRequest).isEqualTo(GrpcRouteForwardingRequestTestFactory.create()); -// } -// -// private GrpcRouteForwardingRequest buildRouteForwardingRequest() { -// return service.buildRouteForwardingRequest(request, eingang); -// } -// } -// -// @Nested -// class TestSendAttachments { -// -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender; -// @Mock -// private InputStream inputStream; -// -// private final IncomingFileGroup attachment = IncomingFileGroupTestFactory.create(); -// -// @BeforeEach -// void init() { -// when(fileService.getUploadedFileStream(any())).thenReturn(inputStream); -// doReturn(fileSender).when(service).createAttachmentFileSender(any(), any(), any(), any()); -// when(fileSender.send()).thenReturn(fileSender); -// } -// -// @Test -// void shouldGetUploadedFileContent() { -// sendAttachments(); -// -// verify(fileService).getUploadedFileStream(IncomingFileTestFactory.ID); -// } -// -// @Test -// void shouldCallCreateAttachmentFileSender() { -// sendAttachments(); -// -// verify(service).createAttachmentFileSender(requestObserver, IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.FILE, -// inputStream); -// } -// -// @Test -// void shouldSend() { -// sendAttachments(); -// -// verify(fileSender).send(); -// } -// -// private void sendAttachments() { -// service.sendAttachments(List.of(attachment), requestObserver); -// } -// } -// -// @Nested -// class TestCreateAttachmentFileSender { -// -// @Mock -// private InputStream inputStream; -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender; -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSenderWithMetadata; -// @Captor -// private ArgumentCaptor<BiFunction<byte[], Integer, GrpcRouteForwardingRequest>> chunkBuilderCaptor; -// -// private final byte[] chunk = RandomUtils.insecure().randomBytes(5); -// private final GrpcRouteForwardingRequest metadataRequest = GrpcRouteForwardingRequestTestFactory.create(); -// -// @BeforeEach -// void init() { -// doReturn(fileSender).when(service).createSenderWithoutMetadata(any(), any(), any()); -// doReturn(metadataRequest).when(service).buildGrpcAttachmentFile(any(), any()); -// when(fileSender.withMetaData(any())).thenReturn(fileSenderWithMetadata); -// } -// -// @Test -// void shouldCallCreateSenderWithoutMetadata() { -// createAttachmentFileSender(); -// -// verify(service).createSenderWithoutMetadata(chunkBuilderCaptor.capture(), eq(requestObserver), eq(inputStream)); -// chunkBuilderCaptor.getValue().apply(chunk, chunk.length); -// verify(service).buildAttachmentChunk(chunk, chunk.length); -// } -// -// @Test -// void shouldCallBuildGrpcAttachmentFile() { -// createAttachmentFileSender(); -// -// verify(service).buildGrpcAttachmentFile(IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.FILE); -// } -// -// @Test -// void shouldSetMetaData() { -// createAttachmentFileSender(); -// -// verify(fileSender).withMetaData(metadataRequest); -// } -// -// @Test -// void shouldReturnBuiltFileSender() { -// var returnedFileSender = createAttachmentFileSender(); -// -// assertThat(returnedFileSender).isSameAs(fileSenderWithMetadata); -// } -// -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createAttachmentFileSender() { -// return service.createAttachmentFileSender(requestObserver, IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.FILE, -// inputStream); -// } -// } -// -// @Nested -// class TestBuildAttachmentChunk { -// -// private final byte[] chunk = RandomUtils.insecure().randomBytes(5); -// -// @BeforeEach -// void mock() { -// doReturn(GrpcAttachmentTestFactory.CONTENT).when(service).buildGrpcFileContent(any(), anyInt()); -// } -// -// @Test -// void shouldCallBuildGrpcFileContent() { -// service.buildAttachmentChunk(chunk, chunk.length); -// -// verify(service).buildGrpcFileContent(chunk, chunk.length); -// } -// -// @Test -// void shouldReturnGrpcRouteForwardingRequest() { -// var result = service.buildAttachmentChunk(chunk, chunk.length); -// -// assertThat(result).isEqualTo(GrpcRouteForwardingRequestTestFactory.createWithAttachmentContent()); -// } -// } -// -// @Nested -// class TestBuildGrpcAttachmentFile { -// -// private final IncomingFile file = IncomingFileTestFactory.create(); -// -// @BeforeEach -// void mock() { -// when(incomingFileMapper.toAttachmentFile(any(), any())).thenReturn(GrpcAttachmentFileTestFactory.create()); -// } -// -// @Test -// void shouldCallIncomingFileMapper() { -// service.buildGrpcAttachmentFile(IncomingFileGroupTestFactory.NAME, file); -// -// verify(incomingFileMapper).toAttachmentFile(IncomingFileGroupTestFactory.NAME, file); -// } -// -// @Test -// void shouldReturnAttachmentMetadataRequest() { -// var result = service.buildGrpcAttachmentFile(IncomingFileGroupTestFactory.NAME, file); -// -// assertThat(result).isEqualTo(GrpcRouteForwardingRequestTestFactory.createWithAttachmentMetadata()); -// } -// } -// -// @Nested -// class TestSendRepresentations { -// -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender; -// @Mock -// private InputStream inputStream; -// -// private final IncomingFile representation = IncomingFileTestFactory.create(); -// -// @BeforeEach -// void init() { -// when(fileService.getUploadedFileStream(any())).thenReturn(inputStream); -// doReturn(fileSender).when(service).createRepresentationFileSender(any(), any(), any()); -// when(fileSender.send()).thenReturn(fileSender); -// } -// -// @Test -// void shouldGetUploadedFileContent() { -// sendRepresentations(); -// -// verify(fileService).getUploadedFileStream(IncomingFileTestFactory.ID); -// } -// -// @Test -// void shouldCallCreateRepresentationFileSender() { -// sendRepresentations(); -// -// verify(service).createRepresentationFileSender(requestObserver, representation, inputStream); -// } -// -// @Test -// void shouldSend() { -// sendRepresentations(); -// -// verify(fileSender).send(); -// } -// -// private void sendRepresentations() { -// service.sendRepresentations(List.of(representation), requestObserver); -// } -// } -// -// @Nested -// class TestCreateRepresentationFileSender { -// -// @Mock -// private InputStream inputStream; -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender; -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSenderWithMetadata; -// @Captor -// private ArgumentCaptor<BiFunction<byte[], Integer, GrpcRouteForwardingRequest>> chunkBuilderCaptor; -// -// private final byte[] chunk = RandomUtils.insecure().randomBytes(5); -// private final GrpcRouteForwardingRequest metadataRequest = GrpcRouteForwardingRequestTestFactory.create(); -// private final IncomingFile incomingFile = IncomingFileTestFactory.create(); -// -// @BeforeEach -// void init() { -// doReturn(fileSender).when(service).createSenderWithoutMetadata(any(), any(), any()); -// doReturn(metadataRequest).when(service).buildGrpcRepresentationFile(any()); -// when(fileSender.withMetaData(any())).thenReturn(fileSenderWithMetadata); -// } -// -// @Test -// void shouldCallCreateSenderWithoutMetadata() { -// createRepresentationFileSender(); -// -// verify(service).createSenderWithoutMetadata(chunkBuilderCaptor.capture(), eq(requestObserver), eq(inputStream)); -// chunkBuilderCaptor.getValue().apply(chunk, chunk.length); -// verify(service).buildRepresentationChunk(chunk, chunk.length); -// } -// -// @Test -// void shouldCallBuildGrpcRepresentationFile() { -// createRepresentationFileSender(); -// -// verify(service).buildGrpcRepresentationFile(incomingFile); -// } -// -// @Test -// void shouldSetMetaData() { -// createRepresentationFileSender(); -// -// verify(fileSender).withMetaData(metadataRequest); -// } -// -// @Test -// void shouldReturnBuiltFileSender() { -// var returnedFileSender = createRepresentationFileSender(); -// -// assertThat(returnedFileSender).isSameAs(fileSenderWithMetadata); -// } -// -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createRepresentationFileSender() { -// return service.createRepresentationFileSender(requestObserver, incomingFile, inputStream); -// } -// } -// -// @Nested -// class TestCreateSenderWithoutMetadata { -// -// private MockedStatic<GrpcFileUploadUtils> grpcFileUploadUtilsMock; -// @Mock -// private BiFunction<byte[], Integer, GrpcRouteForwardingRequest> chunkBuilder; -// @Mock -// private CallStreamObserver<GrpcRouteForwardingRequest> requestCallStreamObserver; -// @Mock -// private InputStream inputStream; -// @Mock -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender; -// @Mock -// private StreamObserver<GrpcRouteForwardingResponse> responseObserver; -// @Captor -// private ArgumentCaptor<Function<StreamObserver<GrpcRouteForwardingResponse>, CallStreamObserver<GrpcRouteForwardingRequest>>> reqObserverBuilderCaptor; -// -// @BeforeEach -// void init() { -// grpcFileUploadUtilsMock = mockStatic(GrpcFileUploadUtils.class); -// grpcFileUploadUtilsMock.when(() -> GrpcFileUploadUtils.createSender(any(), any(), any(), anyBoolean())).thenReturn(fileSender); -// } -// -// @AfterEach -// void tearDown() { -// grpcFileUploadUtilsMock.close(); -// } -// -// @Test -// void shouldCreateFileSender() { -// createSenderWithoutMetadata(); -// -// grpcFileUploadUtilsMock -// .verify(() -> GrpcFileUploadUtils.createSender(eq(chunkBuilder), eq(inputStream), reqObserverBuilderCaptor.capture(), eq(false))); -// assertThat(reqObserverBuilderCaptor.getValue().apply(responseObserver)).isSameAs(requestCallStreamObserver); -// } -// -// @Test -// void shouldReturnCreatedFileSender() { -// var returnedFileSender = createSenderWithoutMetadata(); -// -// assertThat(returnedFileSender).isSameAs(fileSender); -// } -// -// private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createSenderWithoutMetadata() { -// return service.createSenderWithoutMetadata(chunkBuilder, requestCallStreamObserver, inputStream); -// } -// } -// -// @Nested -// class TestBuildRepresentationChunk { -// -// private final byte[] chunk = RandomUtils.insecure().randomBytes(5); -// -// @BeforeEach -// void mock() { -// doReturn(GrpcRepresentationTestFactory.CONTENT).when(service).buildGrpcFileContent(any(), anyInt()); -// } -// -// @Test -// void shouldCallBuildGrpcFileContent() { -// service.buildRepresentationChunk(chunk, chunk.length); -// -// verify(service).buildGrpcFileContent(chunk, chunk.length); -// } -// -// @Test -// void shouldReturnGrpcRouteForwardingRequest() { -// var result = service.buildRepresentationChunk(chunk, chunk.length); -// -// assertThat(result).isEqualTo(GrpcRouteForwardingRequestTestFactory.createWithRepresentationContent()); -// } -// } -// -// @Nested -// class TestBuildGrpcFileContent { -// -// @Nested -// class TestOnEndOfFile { -// -// @Test -// void shouldBuildEndOfFileChunk() { -// var fileContent = service.buildGrpcFileContent(new byte[0], -1); -// -// assertThat(fileContent).isEqualTo(GrpcFileContentTestFactory.createEndOfFile()); -// } -// } -// -// @Nested -// class TestOnContentProvided { -// -// @Test -// void shouldBuildEndOfFileChunk() { -// var fileContent = service.buildGrpcFileContent(GrpcFileContentTestFactory.CONTENT, GrpcFileContentTestFactory.CONTENT.length); -// -// assertThat(fileContent).isEqualTo(GrpcFileContentTestFactory.create()); -// } -// } -// } -// -// @Nested -// class TestBuildGrpcRepresentationFile { -// -// private final IncomingFile file = IncomingFileTestFactory.create(); -// -// @BeforeEach -// void mock() { -// when(incomingFileMapper.toRepresentationFile(any())).thenReturn(GrpcRepresentationFileTestFactory.create()); -// } -// -// @Test -// void shouldCallIncomingFileMapper() { -// service.buildGrpcRepresentationFile(file); -// -// verify(incomingFileMapper).toRepresentationFile(file); -// } -// -// @Test -// void shouldReturnRepresentationMetadataRequest() { -// var result = service.buildGrpcRepresentationFile(file); -// -// assertThat(result).isEqualTo(GrpcRouteForwardingRequestTestFactory.createWithRepresentationMetadata()); -// } -// } -// -// @Nested -// class TestWaitForCompletion { -// -// @Mock -// private CompletableFuture<Void> future; -// -// @SneakyThrows -// @Test -// void shouldGetFromFuture() { -// waitForCompletion(); -// -// verify(future).get(2, TimeUnit.MINUTES); -// } -// -// @Nested -// class TestOnInterruptedException { -// -// private final InterruptedException exception = new InterruptedException(); -// -// @BeforeEach -// @SneakyThrows -// void mock() { -// when(future.get(anyLong(), any())).thenThrow(exception); -// } -// -// @Test -// void shouldThrowTechnicalException() { -// assertThrows(TechnicalException.class, TestWaitForCompletion.this::waitForCompletion); -// } -// -// @Test -// void shouldInterruptThread() { -// try { -// waitForCompletion(); -// } catch (TechnicalException e) { -// // expected -// } -// -// assertThat(Thread.currentThread().isInterrupted()).isTrue(); -// } -// } -// -// @Nested -// class TestOnExecutionException { -// -// private final ExecutionException exception = new ExecutionException(new Exception()); -// -// @BeforeEach -// @SneakyThrows -// void mock() { -// when(future.get(anyLong(), any())).thenThrow(exception); -// } -// -// @Test -// void shouldThrowTechnicalException() { -// assertThrows(TechnicalException.class, TestWaitForCompletion.this::waitForCompletion); -// } -// } -// -// @Nested -// class TestOnTimeoutException { -// -// private final TimeoutException exception = new TimeoutException(); -// -// @BeforeEach -// @SneakyThrows -// void mock() { -// when(future.get(anyLong(), any())).thenThrow(exception); -// } -// -// @Test -// void shouldThrowTechnicalException() { -// assertThrows(TechnicalException.class, TestWaitForCompletion.this::waitForCompletion); -// } -// } -// -// private void waitForCompletion() { -// service.waitForCompletion(future); -// } -// } -// -// @Nested -// class ForwardingResponseObserverTest { -// -// @Mock -// private CompletableFuture<Void> future; -// private ForwardingResponseObserver responseObserver; -// -// @BeforeEach -// void init() { -// responseObserver = new ForwardingResponseObserver(future); -// } -// -// @Test -// void shouldCompleteExceptionallyOnError() { -// var error = new Throwable(); -// -// responseObserver.onError(error); -// -// verify(future).completeExceptionally(error); -// } -// -// @Test -// void shouldCompleteOnCompleted() { -// responseObserver.onCompleted(); -// -// verify(future).complete(null); -// } -// } -//}