diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserver.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserver.java deleted file mode 100644 index 368563ae5f200722690525324ada3383c33ae4c8..0000000000000000000000000000000000000000 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserver.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2023-2024. - * 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.antragsraum.attachments; - -import de.ozgcloud.antragsraum.common.TechnicalException; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataResponse; -import io.grpc.stub.StreamObserver; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.CompletableFuture; - -@Log4j2 -@RequiredArgsConstructor(access = AccessLevel.PROTECTED) -class FileDownloadStreamObserver implements StreamObserver<GrpcGetBinaryFileDataResponse> { - private final CompletableFuture<Boolean> streamFuture; - private final OutputStream out; - - @Override - public void onNext(GrpcGetBinaryFileDataResponse response) { - writeToStream(response.getFileContent().toByteArray()); - } - - void writeToStream(byte[] contentPart) { - try { - out.write(contentPart); - } catch (IOException e) { - LOG.error("Download file error writing on output stream", e); - throw new TechnicalException(e); - } - } - - @Override - public void onError(Throwable t) { - streamFuture.completeExceptionally(t); - } - - @Override - public void onCompleted() { - streamFuture.complete(true); - } -} diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileGrpcClient.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileGrpcClient.java deleted file mode 100644 index c6b455095a4f44465240c30648c376df66168b71..0000000000000000000000000000000000000000 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileGrpcClient.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2023-2024. - * 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.antragsraum.attachments; - -import java.io.OutputStream; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.springframework.stereotype.Component; - -import com.google.protobuf.ByteString; - -import de.ozgcloud.antragsraum.callcontext.ContextService; -import de.ozgcloud.antragsraum.common.TechnicalException; -import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFilesRequest; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcFindFilesResponse; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataRequest; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileMetaData; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest; -import io.grpc.stub.StreamObserver; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -class FileGrpcClient { - public static final int CHUNK_SIZE = 255 * 1024; - - private final @NonNull ContextService contextService; - - GrpcFindFilesResponse findBinaryFilesMetaData(GrpcBinaryFilesRequest request, String address) { - /*try (GrpcChannelFactory channelFactory = getChannelFactory(address)) { - var channel = channelFactory.createChannel(address); - var serviceStub = BinaryFileServiceGrpc.newBlockingStub(channel); - - return getBinaryFilesMetaData(request, serviceStub); - }*/ - - return null; - } - - GrpcFindFilesResponse getBinaryFilesMetaData(GrpcBinaryFilesRequest request, - BinaryFileServiceGrpc.BinaryFileServiceBlockingStub fileServiceBlockingStub) { - return fileServiceBlockingStub.findBinaryFilesMetaData(request); - } - - StreamObserver<GrpcUploadBinaryFileRequest> uploadFile(OzgUploadFile uploadFile, String address, CompletableFuture<String> fileIdFuture) { - /*try (GrpcChannelFactory channelFactory = getChannelFactory(address)) { - var channel = channelFactory.createChannel(address); - var responseObserver = createUploadFileObserver(fileIdFuture, uploadFile); - var serviceStub = BinaryFileServiceGrpc.newStub(channel); - - return doUploadFiles(responseObserver, serviceStub); - }*/ - - return null; - } - - StreamObserver<GrpcUploadBinaryFileRequest> doUploadFiles(FileUploadStreamObserver observer, - BinaryFileServiceGrpc.BinaryFileServiceStub asyncServiceStub) { - return asyncServiceStub.uploadBinaryFileAsStream(observer); - } - - FileUploadStreamObserver createUploadFileObserver(CompletableFuture<String> fileIdFuture, OzgUploadFile uploadFile) { - var metadataRequest = buildMetaDataRequest(uploadFile); - var streamer = new ChunkedFileSender<>(uploadFile.fileContent(), CHUNK_SIZE, this::buildChunkRequest, metadataRequest); - return new FileUploadStreamObserver(fileIdFuture, streamer); - } - - private GrpcUploadBinaryFileRequest buildMetaDataRequest(OzgUploadFile uploadFile) { - return GrpcUploadBinaryFileRequest.newBuilder() - .setMetadata(GrpcUploadBinaryFileMetaData.newBuilder() - .setContext(contextService.createCallContext()) - .setVorgangId(uploadFile.fileData().getVorgangId()) - .setContentType(uploadFile.fileData().getContentType()) - .setFileName(uploadFile.fileData().getFileName())) - .build(); - } - - GrpcUploadBinaryFileRequest buildChunkRequest(byte[] bytes) { - return GrpcUploadBinaryFileRequest.newBuilder().setFileContent((ByteString.copyFrom(bytes))).build(); - } - - void downloadFileContent(String fileId, OutputStream out, String address) { - /*try (GrpcChannelFactory channelFactory = getChannelFactory(address)) { - var channel = channelFactory.createChannel(address); - var serviceStub = BinaryFileServiceGrpc.newStub(channel); - - doDownloadFileContent(fileId, out, serviceStub); - }*/ - } - - void doDownloadFileContent(String fileId, OutputStream out, BinaryFileServiceGrpc.BinaryFileServiceStub binaryFileServiceStub) { - var streamFuture = new CompletableFuture<Boolean>(); - var responseObserver = createDownloadFileObserver(streamFuture, out); - binaryFileServiceStub.getBinaryFileContent(buildGrpcGetBinaryFileDataRequest(fileId), responseObserver); - - waitUntilFutureToComplete(streamFuture); - - } - - FileDownloadStreamObserver createDownloadFileObserver(CompletableFuture<Boolean> streamFuture, OutputStream out) { - return new FileDownloadStreamObserver(streamFuture, out); - } - - private GrpcGetBinaryFileDataRequest buildGrpcGetBinaryFileDataRequest(String fileId) { - return GrpcGetBinaryFileDataRequest.newBuilder().setFileId(fileId).build(); - } - - void waitUntilFutureToComplete(CompletableFuture<Boolean> streamFuture) { - try { - streamFuture.get(10, TimeUnit.MINUTES); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new TechnicalException(e); - } catch (ExecutionException | TimeoutException e) { - throw new TechnicalException(e); - } - } -} \ No newline at end of file diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java index cb2d5b0a17d2d366c29f466bec5777262b170e9e..bc0972de004cb834f28abcf52bb107459719e572 100644 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java +++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java @@ -20,12 +20,13 @@ package de.ozgcloud.antragsraum.attachments; +import java.io.IOException; import java.io.OutputStream; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.springframework.stereotype.Service; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFilesRequest; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -34,13 +35,18 @@ import lombok.extern.log4j.Log4j2; @Service @RequiredArgsConstructor class FileRemoteService { - private final @NonNull FileGrpcClient grpcClient; + private final @NonNull FileRestClient restClient; OzgFile getFile(String fileId, String address) { - var grpcFileMetadata = grpcClient.findBinaryFilesMetaData(buildGrpcBinaryFileRequest(fileId), address); + var grpcFileMetadata = restClient.findBinaryFilesMetaData(fileId, address); - if (!grpcFileMetadata.getFileList().isEmpty()) { - return OzgFileMapper.fromGrpcFile(grpcFileMetadata.getFile(0)); + if (Objects.isNull(grpcFileMetadata.getFile())) { + LOG.warn("Received file id {} from {}, but findBinaryFilesMetaData returned empty file list", fileId, address); + return null; + } + + if (!grpcFileMetadata.getFile().isEmpty()) { + return OzgFileMapper.fromRestFile(grpcFileMetadata.getFile().getFirst()); } LOG.warn("Received file id {} from {}, but findBinaryFilesMetaData returned empty file list", fileId, address); @@ -48,21 +54,18 @@ class FileRemoteService { return null; } - private GrpcBinaryFilesRequest buildGrpcBinaryFileRequest(String fileId) { - return GrpcBinaryFilesRequest.newBuilder() - .addFileId(fileId) - .build(); - } - CompletableFuture<String> uploadFile(OzgUploadFile uploadFile, String address) { var fileIdFuture = new CompletableFuture<String>(); - var request = grpcClient.uploadFile(uploadFile, address, fileIdFuture); - LOG.debug("Grpc File Upload Request: {}", request); + restClient.uploadFile(uploadFile, address, fileIdFuture); return fileIdFuture; } void downloadFileContent(String fileId, OutputStream out, String address) { - grpcClient.downloadFileContent(fileId, out, address); + try { + restClient.downloadFileContent(fileId, out, address); + } catch (IOException e) { + throw new RuntimeException(e); + } } } diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRestClient.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRestClient.java index 3969b6b415027eda8382accb7a61d03ebd12458c..09a71960bc56a9171d8e1b3d9afb9430886c106f 100644 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRestClient.java +++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRestClient.java @@ -22,15 +22,98 @@ package de.ozgcloud.antragsraum.attachments; +import static de.ozgcloud.antragsraum.WebConfiguration.*; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.io.IOUtils; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; import de.ozgcloud.antragsraum.callcontext.ContextService; +import de.ozgcloud.antragsraum.common.RestClientUtils; +import de.ozgcloud.antragsraum.common.TechnicalException; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcFindFilesResponse; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@Log4j2 @Component @RequiredArgsConstructor public class FileRestClient { - public static final int CHUNK_SIZE = 255 * 1024; + private static final String FIND_FILE_METADATA_API = "/api/v1/file/metadata/{fileId}"; + private static final String GET_FILE_CONTENT_API = "/api/v1/file/content/{fileId}"; + static final String UPLOAD_FILE_API = "/api/v1/file"; private final @NonNull ContextService contextService; + private final @NonNull RestClient nachrichtRestClient; + + public AntragraumproxyGrpcFindFilesResponse findBinaryFilesMetaData(String fileId, final String address) { + return nachrichtRestClient + .get() + .uri(FIND_FILE_METADATA_API, fileId) + .accept(MediaType.APPLICATION_JSON) + .header(X_GRPC_ADDRESS, RestClientUtils.sanitizeAddress(address)) + .retrieve() + .toEntity(AntragraumproxyGrpcFindFilesResponse.class).getBody(); + } + + public void uploadFile(final OzgUploadFile uploadFile, final String address, final CompletableFuture<String> fileIdFuture) { + try { + var response = sendFile(uploadFile, address); + if (Objects.nonNull(response)) { + fileIdFuture.complete(response); + } else { + fileIdFuture.completeExceptionally(new IllegalStateException("No fileId received")); + } + } catch (Exception e) { + fileIdFuture.completeExceptionally(e); + } + } + + private String sendFile(OzgUploadFile uploadFile, String address) { + + MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>(); + requestBody.add("file", uploadFile.file().getResource()); + requestBody.add("vorgangId", uploadFile.vorgangId()); + + var fileId = nachrichtRestClient + .post() + .uri(UPLOAD_FILE_API) + .header(X_GRPC_ADDRESS, RestClientUtils.sanitizeAddress(address)) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(requestBody) + .retrieve().toEntity(String.class).getBody(); + LOG.info("received {}", fileId); + + return fileId; + } + + public void downloadFileContent(final String fileId, final OutputStream out, final String address) throws IOException { + var fileContent = Optional.ofNullable(nachrichtRestClient + .get() + .uri(GET_FILE_CONTENT_API, fileId) + .header(X_GRPC_ADDRESS, RestClientUtils.sanitizeAddress(address)) + .accept(MediaType.APPLICATION_OCTET_STREAM) + .retrieve().toEntity(Resource.class).getBody()); + + fileContent.ifPresent(file -> processFile(out, file)); + } + + private static void processFile(final OutputStream out, Resource fileContent) { + try { + IOUtils.copy(fileContent.getInputStream(), out); + } catch (IOException e) { + LOG.error("Error writing file content to output", e); + throw new TechnicalException(e); + } + } } diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java index 56b6e2d270cee833ebee006176f11f9c09b3a456..99041ce9e5862efcd444b2baf613fcc1949f4886 100644 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java +++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java @@ -20,18 +20,14 @@ package de.ozgcloud.antragsraum.attachments; -import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.CompletableFuture; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import de.ozgcloud.antragsraum.common.TechnicalException; import de.ozgcloud.antragsraum.common.VirusFoundException; import de.ozgcloud.antragsraum.events.NachrichtEventService; -import de.ozgcloud.apilib.file.OzgCloudUploadFile; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -67,12 +63,8 @@ public class FileService { LOG.info("File {} is clean, preparing upload", file.getOriginalFilename()); return OzgUploadFile.builder() - .fileData(OzgCloudUploadFile.builder() - .fileName(file.getOriginalFilename()) - .contentType(file.getContentType()) - .vorgangId(vorgangId) - .build()) - .fileContent(getInputStream(file)) + .vorgangId(vorgangId) + .file(file) .build(); } @@ -83,13 +75,4 @@ public class FileService { boolean isClean(MultipartFile file) { return scanner.scan(file).isEmpty(); } - - private InputStream getInputStream(MultipartFile file) { - try { - return file.getInputStream(); - } catch (IOException e) { - LOG.error("Error reading file data as stream.", e); - throw new TechnicalException(e); - } - } } diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileUploadStreamObserver.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileUploadStreamObserver.java deleted file mode 100644 index 534fd691d3c60241e9dcffcd9dba4c8a9f9b7ae3..0000000000000000000000000000000000000000 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileUploadStreamObserver.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2023-2024. - * 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.antragsraum.attachments; - -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileResponse; -import io.grpc.stub.ClientCallStreamObserver; -import io.grpc.stub.ClientResponseObserver; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.concurrent.CompletableFuture; - -@RequiredArgsConstructor(access = AccessLevel.PROTECTED) -class FileUploadStreamObserver implements ClientResponseObserver<GrpcUploadBinaryFileRequest, GrpcUploadBinaryFileResponse> { - private final CompletableFuture<String> fileIdFuture; - private final ChunkedFileSender<GrpcUploadBinaryFileRequest> fileStreamer; - - @Getter - private String fileId; - - private ClientCallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver; - - @Override - public void beforeStart(ClientCallStreamObserver<GrpcUploadBinaryFileRequest> requestStream) { - this.requestObserver = requestStream; - requestObserver.setOnReadyHandler(() -> fileStreamer.sendChunkTo(requestObserver)); - } - - @Override - public void onNext(GrpcUploadBinaryFileResponse response) { - fileId = response.getFileId(); - } - - @Override - public void onError(Throwable t) { - fileIdFuture.completeExceptionally(t); - } - - @Override - public void onCompleted() { - fileIdFuture.complete(fileId); - } -} diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java index 8ff6526dacc8a1f43b27d6fef0b240759766305c..885f8d8d1aa6a9a2b3bdc69d3925983053eb6b02 100644 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java +++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java @@ -20,19 +20,33 @@ package de.ozgcloud.antragsraum.attachments; -import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile; +import java.util.Objects; + +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcOzgFile; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcUploadBinaryFileMetaData; +import de.ozgcloud.apilib.file.OzgCloudUploadFile; import lombok.AccessLevel; import lombok.NoArgsConstructor; @NoArgsConstructor(access = AccessLevel.PRIVATE) class OzgFileMapper { - static OzgFile fromGrpcFile(GrpcOzgFile grpcFile) { + static OzgFile fromRestFile(AntragraumproxyGrpcOzgFile grpcFile) { return OzgFile.builder() .id(grpcFile.getId()) .fileName(grpcFile.getName()) .contentType(grpcFile.getContentType()) - .fileSize(grpcFile.getSize()) + .fileSize(Objects.nonNull(grpcFile.getSize()) ? Long.parseLong(grpcFile.getSize()) : -1L) .build(); } + + static AntragraumproxyGrpcUploadBinaryFileMetaData toGrpcUploadBinaryFileMetaData(OzgCloudUploadFile ozgFile) { + var metaData = new AntragraumproxyGrpcUploadBinaryFileMetaData(); + metaData.contentType(ozgFile.getContentType()); + metaData.setFileName(ozgFile.getFileName()); + metaData.setVorgangId(ozgFile.getVorgangId()); + metaData.setField(ozgFile.getFieldName()); + + return metaData; + } } diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java index 67060e8ca477c699f35f85f33f20039b8536e34b..e716d36724a3725f1e0af677a4ab6573972c3757 100644 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java +++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java @@ -20,14 +20,12 @@ package de.ozgcloud.antragsraum.attachments; -import java.io.InputStream; - import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; -import de.ozgcloud.apilib.file.OzgCloudUploadFile; import lombok.Builder; @Validated @Builder -public record OzgUploadFile(OzgCloudUploadFile fileData, InputStream fileContent) { +public record OzgUploadFile(String vorgangId, MultipartFile file) { } diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSender.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/RestChunkedFileSender.java similarity index 58% rename from server/src/main/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSender.java rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/RestChunkedFileSender.java index ac2b961157598151ef985865988374c69189c547..a2b0cd49cf2d1848b067535a740a1092b215d39e 100644 --- a/server/src/main/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSender.java +++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/RestChunkedFileSender.java @@ -22,48 +22,80 @@ package de.ozgcloud.antragsraum.attachments; -import de.ozgcloud.antragsraum.common.TechnicalException; -import io.grpc.stub.CallStreamObserver; -import lombok.AllArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.io.IOUtils; +import static de.ozgcloud.antragsraum.WebConfiguration.*; +import static de.ozgcloud.antragsraum.attachments.FileRestClient.*; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; +import org.apache.commons.io.IOUtils; +import org.springframework.http.MediaType; +import org.springframework.web.client.RestClient; + +import de.ozgcloud.antragsraum.common.TechnicalException; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcUploadBinaryFileResponse; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; + @Log4j2 @AllArgsConstructor -class ChunkedFileSender<T> { +class RestChunkedFileSender<T> { private final AtomicBoolean hasUploadFile = new AtomicBoolean(true); private final InputStream uploadStream; private final int chunkSize; private final Function<byte[], T> buildChunkRequest; private T requestMetadata; + private final RestClient restClient; + private String targetAddress; + private final CompletableFuture<String> fileIdFuture; - public void sendChunkTo(CallStreamObserver<T> streamObserver) { + public void sendChunkTo() { if (hasUploadFile.get()) { - sendMetadata(streamObserver); - int size = sendNextChunk(streamObserver); + var response = sendMetadata(); + int size = sendNextChunk(); if (size < chunkSize) { - handleFileEndReached(streamObserver); + handleFileEndReached(); + fileIdFuture.complete(response.getFileId()); } } } - private void sendMetadata(CallStreamObserver<T> streamObserver) { + private AntragraumproxyGrpcUploadBinaryFileResponse sendMetadata() { if (requestMetadata != null) { - streamObserver.onNext(requestMetadata); + var res = restClient + .post() + .uri(UPLOAD_FILE_API) + .header(X_GRPC_ADDRESS, targetAddress) + .contentType(MediaType.APPLICATION_JSON) + .body(requestMetadata) + .retrieve().toEntity(AntragraumproxyGrpcUploadBinaryFileResponse.class).getBody(); + LOG.info("recieved {}", res); + requestMetadata = null; + + return res; } + + return null; } - private int sendNextChunk(CallStreamObserver<T> streamObserver) { + private int sendNextChunk() { byte[] content = readFromStream(); var size = content.length; if (size > 0) { - streamObserver.onNext(buildChunkRequest.apply(content)); + var restUploadFile = buildChunkRequest.apply(content); + var res = restClient + .post() + .uri(UPLOAD_FILE_API) + .header(X_GRPC_ADDRESS, targetAddress) + .contentType(MediaType.APPLICATION_JSON) + .body(restUploadFile) + .retrieve().toEntity(AntragraumproxyGrpcUploadBinaryFileResponse.class); + + LOG.info("Send binary chunk result: {}", res); } return size; } @@ -77,9 +109,9 @@ class ChunkedFileSender<T> { } } - private void handleFileEndReached(CallStreamObserver<T> streamObserver) { + private void handleFileEndReached() { IOUtils.closeQuietly(uploadStream); - streamObserver.onCompleted(); + hasUploadFile.getAndSet(false); } } diff --git a/server/src/main/resources/application-local.yml b/server/src/main/resources/application-local.yml index fcc20fc6f6bdea5caeaf3f2bf4854aa81d716d5d..dd87a88dc3cbdae321115bfcc17871c9faa65746 100644 --- a/server/src/main/resources/application-local.yml +++ b/server/src/main/resources/application-local.yml @@ -11,9 +11,9 @@ clamav: logging: level: - "org.springframework.security": DEBUG + "org.springframework.security": WARN "org.springframework.security.access": WARN - "org.springframework.http": DEBUG + "org.springframework.http": INFO "org.springframework.web": DEBUG "org.springframework.web.client": DEBUG "de.ozgcloud.antragsraum": DEBUG diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSenderTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSenderTest.java deleted file mode 100644 index 64ba29a6b8127ca300cb36c1a25b5f1d7287dca4..0000000000000000000000000000000000000000 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSenderTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2024. 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.antragsraum.attachments; - -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.InputStream; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.google.protobuf.ByteString; - -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest; -import io.grpc.stub.ClientCallStreamObserver; - -@ExtendWith(MockitoExtension.class) -class ChunkedFileSenderTest { - - private ChunkedFileSender<GrpcUploadBinaryFileRequest> chunkedFileSender; - - @Spy - InputStream inputStream = OzgUploadFileTestFactory.create().fileContent(); - - @BeforeEach - void setUp() { - var metaDataRequest = GrpcUploadBinaryFileRequest.newBuilder().build(); - - chunkedFileSender = spy(new ChunkedFileSender<>(inputStream, FileGrpcClient.CHUNK_SIZE, - this::buildChunkRequest, metaDataRequest)); - } - - GrpcUploadBinaryFileRequest buildChunkRequest(byte[] bytes) { - return GrpcUploadBinaryFileRequest.newBuilder().setFileContent((ByteString.copyFrom(bytes))).build(); - } - - @Nested - class TestSendingFileChunks { - @Test - @SuppressWarnings("unchecked") - void shouldCallOnNext() { - var observer = mock(ClientCallStreamObserver.class); - - chunkedFileSender.sendChunkTo(observer); - - verify(observer, atLeast(1)).onNext(any()); - } - - @Test - @SuppressWarnings("unchecked") - void shouldCallOnCompleted() { - var observer = mock(ClientCallStreamObserver.class); - - chunkedFileSender.sendChunkTo(observer); - - verify(observer).onCompleted(); - } - - @Test - @SuppressWarnings("unchecked") - void shouldCallReadNBytes() throws IOException { - var observer = mock(ClientCallStreamObserver.class); - - chunkedFileSender.sendChunkTo(observer); - - verify(inputStream).readNBytes(anyInt()); - } - - @Test - @SuppressWarnings("unchecked") - void shouldCallInputStreamClose() throws IOException { - var observer = mock(ClientCallStreamObserver.class); - - chunkedFileSender.sendChunkTo(observer); - - verify(inputStream).close(); - } - } -} \ No newline at end of file diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java index 3f0d52ee4e3357eb8d02d09cef3d5677f410a83f..374a5c968a203a6ce23f33e8715c64abf895ce5f 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java @@ -21,8 +21,8 @@ package de.ozgcloud.antragsraum.attachments; import static de.ozgcloud.antragsraum.attachments.FileController.*; -import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; import static de.ozgcloud.antragsraum.attachments.FileValidatorTest.*; +import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.any; import static org.mockito.Mockito.*; @@ -49,6 +49,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; +import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import de.ozgcloud.antragsraum.common.NotFoundException; @@ -69,6 +70,9 @@ public class FileControllerITCase { @MockBean private RestTemplate restTemplate; + @MockBean + private RestClient restClient; + @Autowired private MockMvc mockMvc; diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserverTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserverTest.java deleted file mode 100644 index e03c730c85c2bb5357f7c53611bfde03316fec8a..0000000000000000000000000000000000000000 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserverTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2023-2024. - * 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.antragsraum.attachments; - -import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.CompletableFuture; - -import org.junit.jupiter.api.Nested; -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 com.google.protobuf.ByteString; - -import de.ozgcloud.antragsraum.common.TechnicalException; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataResponse; - -@ExtendWith(MockitoExtension.class) -class FileDownloadStreamObserverTest { - @InjectMocks - private FileDownloadStreamObserver downloadStreamObserver; - @Mock - private CompletableFuture<Boolean> streamFuture; - @Mock - private OutputStream out; - - @Nested - class TestOnNext { - @Test - void shouldWriteToStreamOnReceivingContentPart() throws IOException { - downloadStreamObserver.onNext(createFileContentResponse()); - - verify(out).write(DATA); - } - - private GrpcGetBinaryFileDataResponse createFileContentResponse() { - return GrpcGetBinaryFileDataResponse.newBuilder().setFileContent(ByteString.copyFrom(DATA)).build(); - } - - @Test - void shouldHandleException() throws IOException { - doThrow(IOException.class).when(out).write(any()); - - assertThatExceptionOfType(TechnicalException.class).isThrownBy( - () -> downloadStreamObserver.onNext(createFileContentResponse())).withMessage("TechnicalException happened! Message: null"); - } - } - - @Nested - class TestOnError { - @Mock - private Throwable throwable; - - @Test - void shouldCompleteFutureExceptionally() { - downloadStreamObserver.onError(throwable); - - verify(streamFuture).completeExceptionally(throwable); - } - } - - @Nested - class TestOnCompleted { - @Test - void shouldCompleteFuture() { - downloadStreamObserver.onCompleted(); - - verify(streamFuture).complete(true); - } - } -} \ No newline at end of file diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcClientTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcClientTest.java deleted file mode 100644 index 35c1a5b3f034d87ff685f7f1324c7a8e4953712a..0000000000000000000000000000000000000000 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcClientTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2023-2024. - * 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.antragsraum.attachments; - -import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.google.protobuf.ByteString; - -import de.ozgcloud.antragsraum.callcontext.ContextService; -import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc; -import de.ozgcloud.vorgang.grpc.command.GrpcCallContext; - -@ExtendWith(MockitoExtension.class) -@Disabled("Needs to be fixed when switched to rest") -class FileGrpcClientTest { - @Spy - @InjectMocks - private FileGrpcClient fileGrpcClient; - - @Mock - private ContextService contextService; - - @Nested - class TestDownloadObserver { - @Test - void shouldCreateDownloadObserver() { - var observer = fileGrpcClient.createDownloadFileObserver(CompletableFuture.completedFuture(Boolean.TRUE), new ByteArrayOutputStream()); - - assertThat(observer).isNotNull(); - } - } - - @Nested - class TestUploadingFile { - @Mock - private BinaryFileServiceGrpc.BinaryFileServiceStub asyncStub; - - private final OzgUploadFile file = OzgUploadFileTestFactory.create(); - - CompletableFuture<String> fileIdFuture = new CompletableFuture<>(); - - @BeforeEach - void init() { - when(contextService.createCallContext()).thenReturn(GrpcCallContext.newBuilder().build()); - } - - @Test - void shouldCallGetChannel() { - fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, fileIdFuture); - } - - @Test - void shouldCallBuildCallStreamObserver() { - fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, fileIdFuture); - - verify(fileGrpcClient).createUploadFileObserver(any(), any()); - } - } - - @Nested - class TestBuildChunkRequest { - @Test - void shouldContainContent() { - var chunkRequest = fileGrpcClient.buildChunkRequest(DATA); - - assertThat(chunkRequest.getFileContent()).isEqualTo(ByteString.copyFrom(DATA)); - } - } - - @Nested - class TestDownloadingFiles { - @Mock - private FileDownloadStreamObserver responseObserver; - - @Mock - private BinaryFileServiceGrpc.BinaryFileServiceStub asyncStub; - - private final ByteArrayOutputStream output = new ByteArrayOutputStream(); - - @BeforeEach - void init() { - doReturn(responseObserver).when(fileGrpcClient).createDownloadFileObserver(any(), any(OutputStream.class)); - doNothing().when(fileGrpcClient).waitUntilFutureToComplete(any()); - } - - @Test - void shouldCallCreateDownloadFileObserver() { - fileGrpcClient.downloadFileContent(FILE_ID, output, CHANNEL_ADDRESS); - - verify(fileGrpcClient).createDownloadFileObserver(any(), any(OutputStream.class)); - } - } - - @Nested - class TestWaitUntilFutureToComplete { - @Mock - private CompletableFuture<Boolean> streamFuture; - - @Test - void shouldNotThrowException() { - assertDoesNotThrow(() -> fileGrpcClient.waitUntilFutureToComplete(streamFuture)); - } - - @ParameterizedTest - @ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class }) - void shouldRethrowAsRuntimeException(Class<Exception> exception) - throws InterruptedException, ExecutionException, TimeoutException { - doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class)); - - assertThrows(RuntimeException.class, () -> fileGrpcClient.waitUntilFutureToComplete(streamFuture)); - } - } -} \ No newline at end of file diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java index d2e9348f4def6a6757357707af309b2936b71de3..8eb0b43f1cd2b10e7cd8282a84d63809a88e7543 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java @@ -25,6 +25,7 @@ package de.ozgcloud.antragsraum.attachments; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestClient; import de.ozgcloud.antragsraum.callcontext.ContextService; import de.ozgcloud.antragsraum.common.AuthenticationHelper; @@ -33,13 +34,13 @@ import lombok.NonNull; @Configuration public class FileGrpcTestConfiguration { @Bean - FileGrpcClient fileGrpcClient() { - return new FileGrpcClient(contextProvider()); + FileRestClient fileRestClient() { + return new FileRestClient(contextProvider(), RestClient.builder().baseUrl("http://localhost:8080").build()); } @Bean - FileRemoteService fileRemoteService(@Autowired FileGrpcClient fileGrpcClient) { - return new FileRemoteService(fileGrpcClient); + FileRemoteService fileRemoteService(@Autowired FileRestClient fileRestClient) { + return new FileRemoteService(fileRestClient); } @NonNull diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java index b32d69abd87ad219e7bbdaf0ae8d98b437b98f98..4d337cf57354e5850c6fec2e1532b5f83fdeebcb 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.*; import java.io.ByteArrayOutputStream; import java.io.OutputStream; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -40,9 +41,8 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import de.ozgcloud.vorgang.grpc.binaryFile.GrpcFindFilesResponse; -import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile; -import io.grpc.stub.StreamObserver; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcFindFilesResponse; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcOzgFile; @ExtendWith(MockitoExtension.class) class FileRemoteServiceTest { @@ -51,20 +51,21 @@ class FileRemoteServiceTest { private FileRemoteService remoteService; @Mock - private FileGrpcClient grpcClient; + private FileRestClient restClient; @Nested class TestLoadingFile { @BeforeEach void init() { - var file = GrpcOzgFile.newBuilder() - .setName(FILE_NAME) - .setSize(FILE_SIZE) - .setContentType(CONTENT_TYPE) - .setId(FILE_ID).build(); - when(grpcClient.findBinaryFilesMetaData(any(), any())).thenReturn( - GrpcFindFilesResponse.newBuilder().addFile(file).build()); + var response = new AntragraumproxyGrpcFindFilesResponse(); + var file = new AntragraumproxyGrpcOzgFile(); + file.setName(FILE_NAME); + file.setSize(FILE_SIZE_STRING); + file.setContentType(CONTENT_TYPE); + file.setId(FILE_ID); + response.setFile(List.of(file)); + when(restClient.findBinaryFilesMetaData(any(), any())).thenReturn(response); } @Test @@ -104,8 +105,8 @@ class FileRemoteServiceTest { @Test void shouldHandleEmpty() { - when(grpcClient.findBinaryFilesMetaData(any(), any())).thenReturn( - GrpcFindFilesResponse.newBuilder().build()); + var response = new AntragraumproxyGrpcFindFilesResponse(); + when(restClient.findBinaryFilesMetaData(any(), any())).thenReturn(response); var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS); @@ -119,10 +120,10 @@ class FileRemoteServiceTest { private final ByteArrayOutputStream output = new ByteArrayOutputStream(); @Test - void shouldCallDownloadFileObserver() { + void shouldCallDownloadFileObserver() throws Exception { remoteService.downloadFileContent(FILE_ID, output, CHANNEL_ADDRESS); - verify(grpcClient).downloadFileContent(eq(FILE_ID), any(OutputStream.class), eq(CHANNEL_ADDRESS)); + verify(restClient).downloadFileContent(eq(FILE_ID), any(OutputStream.class), eq(CHANNEL_ADDRESS)); } } @@ -130,14 +131,10 @@ class FileRemoteServiceTest { class TestUploadFile { @Test - void shouldCallGrpcClient() { - var observer = mock(StreamObserver.class); - - when(grpcClient.uploadFile(any(OzgUploadFile.class), eq(CHANNEL_ADDRESS), any())).thenReturn(observer); - + void shouldCallRestClient() { remoteService.uploadFile(OzgUploadFileTestFactory.create(), CHANNEL_ADDRESS); - verify(grpcClient).uploadFile(any(OzgUploadFile.class), eq(CHANNEL_ADDRESS), any()); + verify(restClient).uploadFile(any(OzgUploadFile.class), eq(CHANNEL_ADDRESS), any()); } } } \ No newline at end of file diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java index 51727bb2b3f4a8804ca47197c386e6b1081b6461..e0b3260d71a1f8e1e5d49b4973c13d6364fb39be 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java @@ -24,7 +24,6 @@ import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -37,11 +36,9 @@ import java.util.concurrent.TimeoutException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.web.multipart.MultipartFile; import de.ozgcloud.antragsraum.common.TechnicalException; @@ -49,7 +46,6 @@ import de.ozgcloud.antragsraum.common.VirusFoundException; import de.ozgcloud.antragsraum.events.NachrichtEventService; import de.ozgcloud.antragsraum.events.NachrichtEventTestFactory; -@ExtendWith(MockitoExtension.class) class FileServiceTest { @Spy @InjectMocks @@ -66,14 +62,14 @@ class FileServiceTest { @Nested class TestFileUpload { - @Mock - private MultipartFile multipartFile; + @Spy + private MultipartFile multipartFile = OzgUploadFileTestFactory.MULTIPART_FILE; @BeforeEach void init() throws IOException { when(remoteService.uploadFile(any(OzgUploadFile.class), anyString())).thenReturn( CompletableFuture.completedFuture(FILE_ID)); - when(multipartFile.getInputStream()).thenReturn(new ByteArrayInputStream(DATA)); + when(nachrichtEventService.getNachrichtEventById(NACHRICHT_EVENT_ID)).thenReturn(NachrichtEventTestFactory.createNachrichtEvent()); } @@ -98,16 +94,16 @@ class FileServiceTest { @Nested class TestExceptionWhenSendingFile { - @Mock - private MultipartFile multipartFile; + @Spy + private final MultipartFile multipartFile = OzgUploadFileTestFactory.MULTIPART_FILE; @Test - void shouldThrowTechnicalExceptionOnUpload() throws IOException { - doThrow(IOException.class).when(multipartFile).getInputStream(); + void shouldThrowTechnicalExceptionOnUpload() { + when(nachrichtEventService.getNachrichtEventById(NACHRICHT_EVENT_ID)).thenReturn(NachrichtEventTestFactory.createNachrichtEvent()); + doThrow(TechnicalException.class).when(remoteService).uploadFile(any(), anyString()); assertThatExceptionOfType(TechnicalException.class).isThrownBy( - () -> fileService.upload(VORGANG_ID, NACHRICHT_EVENT_ID, multipartFile)) - .withMessage("TechnicalException happened! Message: null"); + () -> fileService.upload(VORGANG_ID, NACHRICHT_EVENT_ID, multipartFile)); } } diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcOzgFileTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcOzgFileTestFactory.java index 62f361aa5b175e2a0016503e62d869a3497b2682..d9b9dc223df6ce0896bfaf3c23bcc0480fd5e918 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcOzgFileTestFactory.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcOzgFileTestFactory.java @@ -22,18 +22,16 @@ package de.ozgcloud.antragsraum.attachments; import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; -import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcOzgFile; public class GrpcOzgFileTestFactory { - static GrpcOzgFile create() { - return createBuilder().build(); - } + static AntragraumproxyGrpcOzgFile create() { + var file = new AntragraumproxyGrpcOzgFile(); + file.setId(FILE_ID); + file.setContentType(CONTENT_TYPE); + file.setSize(FILE_SIZE_STRING); + file.setName(FILE_NAME); - static GrpcOzgFile.Builder createBuilder() { - return GrpcOzgFile.newBuilder() - .setId(FILE_ID) - .setContentType(CONTENT_TYPE) - .setSize(FILE_SIZE) - .setName(FILE_NAME); + return file; } } diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java index 144e9223095bbdf1b102ec096fdbcd831724c034..b61e1e22334a5eb6b3ed2850a56d5901a932abd4 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java @@ -28,37 +28,37 @@ import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile; +import de.ozgcloud.antragsraum.proxy.AntragraumproxyGrpcOzgFile; class OzgFileMapperTest { @Nested class TestMappingGrpcOzgFile { - private final GrpcOzgFile grpcOzgFile = GrpcOzgFileTestFactory.create(); + private final AntragraumproxyGrpcOzgFile grpcOzgFile = GrpcOzgFileTestFactory.create(); @Test void shouldHaveFileName() { - var file = OzgFileMapper.fromGrpcFile(grpcOzgFile); + var file = OzgFileMapper.fromRestFile(grpcOzgFile); assertThat(file.fileName()).isEqualTo(FILE_NAME); } @Test void shouldHaveFileSize() { - var file = OzgFileMapper.fromGrpcFile(grpcOzgFile); + var file = OzgFileMapper.fromRestFile(grpcOzgFile); assertThat(file.fileSize()).isEqualTo(FILE_SIZE); } @Test void shouldHaveContentType() { - var file = OzgFileMapper.fromGrpcFile(grpcOzgFile); + var file = OzgFileMapper.fromRestFile(grpcOzgFile); assertThat(file.contentType()).isEqualTo(CONTENT_TYPE); } @Test void shouldHaveFileId() { - var file = OzgFileMapper.fromGrpcFile(grpcOzgFile); + var file = OzgFileMapper.fromRestFile(grpcOzgFile); assertThat(file.id()).isEqualTo(FILE_ID); } diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java index 7ffd19c0f4eb69de55d103523e5e6b7253c7bc70..5621a8a567b722cb0fa9b8a3355b94c1106b5280 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java @@ -29,6 +29,7 @@ public class OzgFileTestFactory { static final String VORGANG_ID = UUID.randomUUID().toString(); static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString(); static final long FILE_SIZE = 10; + static final String FILE_SIZE_STRING = String.valueOf(FILE_SIZE); static final String FILE_NAME = "Testfile.pdf"; static final String NACHRICHT_EVENT_ID = UUID.randomUUID().toString(); static final String CHANNEL_ADDRESS = "static://localhost:9090"; diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java index 4fdcb9d201a0f6e9241a1ac2014de43bbefc001c..040bdd6d9660e967369f342433827aed70f0726e 100644 --- a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java +++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java @@ -23,21 +23,69 @@ package de.ozgcloud.antragsraum.attachments; import static de.ozgcloud.antragsraum.attachments.OzgFileTestFactory.*; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; -import de.ozgcloud.apilib.file.OzgCloudUploadFile; +import org.jetbrains.annotations.NotNull; +import org.springframework.http.MediaType; +import org.springframework.web.multipart.MultipartFile; public class OzgUploadFileTestFactory { + public final static MultipartFile MULTIPART_FILE = new TestMultipartFile(); + static OzgUploadFile create() { return createBuilder().build(); } static OzgUploadFile.OzgUploadFileBuilder createBuilder() { return new OzgUploadFile.OzgUploadFileBuilder() - .fileData(OzgCloudUploadFile.builder() - .fileName(FILE_NAME) - .contentType(CONTENT_TYPE) - .vorgangId(VORGANG_ID) - .build()) - .fileContent(new ByteArrayInputStream(DATA)); + .vorgangId(VORGANG_ID) + .file(MULTIPART_FILE); + } + + static class TestMultipartFile implements MultipartFile { + @Override + public @NotNull String getName() { + return "test.txt"; + } + + @Override + public String getOriginalFilename() { + return "test.txt"; + } + + @Override + public String getContentType() { + return MediaType.TEXT_PLAIN_VALUE; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public long getSize() { + return DATA.length; + } + + @Override + public byte @NotNull [] getBytes() throws IOException { + return DATA.clone(); + } + + @Override + public @NotNull InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(DATA); + } + + @Override + public void transferTo(final @NotNull File dest) throws IOException, IllegalStateException { + try (FileOutputStream fos = new FileOutputStream(dest)) { + fos.write(DATA); + } + } } }