diff --git a/.gitignore b/.gitignore index 3fc1d884b983db7cd48581b4198239e70ebc5119..174f39620a3a67eaad565eae9eaff3b03614809e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ target/ .idea *.iml *.orig +.vscode/ diff --git a/nachrichten-manager-interface/src/main/protobuf/antragraum.model.proto b/nachrichten-manager-interface/src/main/protobuf/antragraum.model.proto index 34504138fbe034e800a6d2d02eeb90b78bac9f6f..ada0e22babb0fa9f35fb2f5c0980a7cb4cfd3719 100644 --- a/nachrichten-manager-interface/src/main/protobuf/antragraum.model.proto +++ b/nachrichten-manager-interface/src/main/protobuf/antragraum.model.proto @@ -62,9 +62,9 @@ message GrpcRueckfrage { string status = 7; string trustLevel = 8; bool accessible = 9; - + string text = 10; - + repeated string attachmentFileId = 11; repeated GrpcRueckfrageAnswer answers = 12; } @@ -92,4 +92,30 @@ message GrpcGetRueckfrageRequest { message GrpcGetRueckfrageResponse { GrpcRueckfrage rueckfrage = 1; +} + +message GrpcGetAttachmentContentRequest { + string samlToken = 1; + string nachrichtId = 2; + string fileId = 3; +} + +message GrpcGetAttachmentContentResponse { + bytes fileContent = 1; +} + +message GrpcGetAttachmentMetadataRequest { + string samlToken = 1; + string nachrichtId = 2; + string fileId = 3; +} + +message GrpcGetAttachmentMetadataResponse { + GrpcFileMetadata fileMetadata = 1; +} + +message GrpcFileMetadata { + string name = 1; + int64 size = 2; + string contentType = 3; } \ No newline at end of file diff --git a/nachrichten-manager-interface/src/main/protobuf/antragraum.proto b/nachrichten-manager-interface/src/main/protobuf/antragraum.proto index e22f3bb0fa2226094b2551d8748c1095f1ded8f5..f5f20747e1bc8474544d4dd47f48a62fac218b53 100644 --- a/nachrichten-manager-interface/src/main/protobuf/antragraum.proto +++ b/nachrichten-manager-interface/src/main/protobuf/antragraum.proto @@ -26,7 +26,6 @@ syntax = "proto3"; package de.ozgcloud.nachrichten.antragraum; import "antragraum.model.proto"; -import "command.model.proto"; option java_multiple_files = true; option java_package = "de.ozgcloud.nachrichten.antragraum"; @@ -38,7 +37,14 @@ service AntragraumService { rpc SendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest) returns (GrpcSendRueckfrageAnswerResponse) { } - + rpc GetRueckfrage(GrpcGetRueckfrageRequest) returns (GrpcGetRueckfrageResponse){ } + + rpc GetAttachmentContent (GrpcGetAttachmentContentRequest) returns (stream GrpcGetAttachmentContentResponse){ + } + + rpc GetAttachmentMetadata (GrpcGetAttachmentMetadataRequest) returns (stream GrpcGetAttachmentMetadataResponse){ + } + } \ No newline at end of file diff --git a/nachrichten-manager-server/pom.xml b/nachrichten-manager-server/pom.xml index c21fc8f0060c8323b22c26782700cea82082ba9f..d5d89202e1e1ad453a3d8b3f459f17eeab789aac 100644 --- a/nachrichten-manager-server/pom.xml +++ b/nachrichten-manager-server/pom.xml @@ -24,7 +24,8 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> @@ -86,8 +87,8 @@ <artifactId>muk-postfach</artifactId> <version>${muk-postfach.version}</version> </dependency> - - <dependency> + + <dependency> <groupId>de.ozgcloud.api-lib</groupId> <artifactId>ozg-cloud-spring-boot-starter</artifactId> <version>${ozgcloud-starter.version}</version> @@ -221,6 +222,13 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>de.ozgcloud.api-lib</groupId> + <artifactId>api-lib-core</artifactId> + <version>${ozgcloud-starter.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -334,4 +342,4 @@ </snapshots> </repository> </repositories> -</project> +</project> \ No newline at end of file diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerConfiguration.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerConfiguration.java index 26f1ff18efd89ac60df5af212f5f410cbb3a1732..33d728479a996174c842c03f058e2862043dd23b 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerConfiguration.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerConfiguration.java @@ -6,6 +6,11 @@ import org.springframework.context.annotation.Configuration; import de.ozgcloud.apilib.common.command.OzgCloudCommandService; import de.ozgcloud.apilib.common.command.grpc.CommandMapper; import de.ozgcloud.apilib.common.command.grpc.GrpcOzgCloudCommandService; +import de.ozgcloud.apilib.file.OzgCloudFileService; +import de.ozgcloud.apilib.file.grpc.GrpcOzgCloudFileService; +import de.ozgcloud.apilib.file.grpc.OzgCloudFileMapper; +import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceBlockingStub; +import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc; import net.devh.boot.grpc.client.inject.GrpcClient; @@ -13,19 +18,31 @@ import net.devh.boot.grpc.client.inject.GrpcClient; public class NachrichtenManagerConfiguration { public static final String OZG_CLOUD_COMMAND_SERVICE_NAME = "nachrichten_OzgCloudCommandService"; + public static final String OZG_CLOUD_FILE_SERVICE_NAME = "nachrichten_OzgCloudFileService"; public static final String NACHRICHTEN_VORGANG_SERVICE = "nachrichten_vorgangService"; public static final String NACHRICHTEN_VORGANG_REMOTE_SERVICE = "nachrichten_vorgangRemoteService"; public static final String GRPC_VORGANG_MANAGER_NAME = "vorgang-manager"; public static final String GRPC_COMMAND_MANAGER_NAME = "command-manager"; + public static final String GRPC_FILE_MANAGER_FILE = "file-manager"; @GrpcClient(GRPC_COMMAND_MANAGER_NAME) private CommandServiceGrpc.CommandServiceBlockingStub commandServiceStub; + @GrpcClient(GRPC_FILE_MANAGER_FILE) + private BinaryFileServiceBlockingStub fileServiceBlockingStub; + @GrpcClient(GRPC_FILE_MANAGER_FILE) + private BinaryFileServiceStub fileServiceAsyncServiceStub; + @Bean(OZG_CLOUD_COMMAND_SERVICE_NAME) OzgCloudCommandService grpcOzgCloudCommandService(CommandMapper commandMapper, NachrichtenManagerCallContextProvider contextProvider) { return new GrpcOzgCloudCommandService(commandServiceStub, commandMapper, contextProvider, GrpcOzgCloudCommandService.DEFAULT_COMMAND_REQUEST_THRESHOLD_MILLIS); } + + @Bean(OZG_CLOUD_FILE_SERVICE_NAME) + OzgCloudFileService grpcOzgCloudFileService(NachrichtenManagerCallContextProvider contextProvider, OzgCloudFileMapper mapper) { + return new GrpcOzgCloudFileService(fileServiceBlockingStub, fileServiceAsyncServiceStub, contextProvider, mapper); + } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java index 60fb62d6bfa8384eb093780e82411474bec22373..4f2ebf90155643067693364030eb15698ddd5dad 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java @@ -23,15 +23,24 @@ package de.ozgcloud.nachrichten.antragraum; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.time.ZonedDateTime; import java.util.List; import java.util.stream.Stream; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import com.google.protobuf.ByteString; + import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.nachrichten.common.vorgang.VorgangService; +import de.ozgcloud.nachrichten.postfach.AttachmentFile; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import io.grpc.stub.StreamObserver; import lombok.RequiredArgsConstructor; @@ -41,9 +50,14 @@ import net.devh.boot.grpc.server.service.GrpcService; @RequiredArgsConstructor @ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL) class AntragraumGrpcService extends AntragraumServiceGrpc.AntragraumServiceImplBase { + + static final int CHUNK_SIZE = 256 * 1024; + private final AntragraumService service; private final AntragraumNachrichtMapper mapper; private final RueckfrageMapper rueckfrageMapper; + private final AttachmentFileMapper attachmentFileMapper; + private final RueckfrageAttachmentFileRequestMapper rueckfrageAttachmentFileRequestMapper; private final VorgangService vorgangService; @@ -113,4 +127,56 @@ class AntragraumGrpcService extends AntragraumServiceGrpc.AntragraumServiceImplB GrpcGetRueckfrageResponse buildGetRueckfrageResponse(GrpcRueckfrage rueckfrage) { return GrpcGetRueckfrageResponse.newBuilder().setRueckfrage(rueckfrage).build(); } + + @Override + public void getAttachmentContent(GrpcGetAttachmentContentRequest request, StreamObserver<GrpcGetAttachmentContentResponse> responseObserver) { + try (var pipedInputStream = new PipedInputStream(); var pipedOutputStream = new PipedOutputStream(pipedInputStream)) { + getAttachmentFileContent(request, pipedOutputStream); + sendFileContent(pipedInputStream, responseObserver); + } catch (Exception e) { + throw new TechnicalException("Error on sending attachment content!", e); + } + } + + void getAttachmentFileContent(GrpcGetAttachmentContentRequest request, PipedOutputStream pipedOutputStream) { + new Thread(() -> service.getAttachmentContent(rueckfrageAttachmentFileRequestMapper.fromGrpc(request), pipedOutputStream)).start(); + } + + void sendFileMetadata(AttachmentFile attachmentFile, + StreamObserver<GrpcGetAttachmentMetadataResponse> responseObserver) { + responseObserver.onNext(GrpcGetAttachmentMetadataResponse.newBuilder() + .setFileMetadata(attachmentFileMapper.toMetadata(attachmentFile)).build()); + } + + void sendFileContent(InputStream fileContent, StreamObserver<GrpcGetAttachmentContentResponse> responseObserver) { + var fileChunk = new byte[CHUNK_SIZE]; + int length; + try (var bufferedInputStream = createBufferedInputStream(fileContent)) { + while ((length = bufferedInputStream.read(fileChunk)) != -1) { + sendChunk(responseObserver, fileChunk, length); + } + responseObserver.onCompleted(); + } catch (IOException e) { + handleException(fileContent, e, "Error on sending file!"); + } + } + + InputStream createBufferedInputStream(InputStream fileContent) { + return new BufferedInputStream(fileContent, CHUNK_SIZE); + } + + private void sendChunk(StreamObserver<GrpcGetAttachmentContentResponse> responseObserver, byte[] fileChunk, int length) { + responseObserver.onNext(GrpcGetAttachmentContentResponse.newBuilder() + .setFileContent(ByteString.copyFrom(fileChunk, 0, length)) + .build()); + } + + private void handleException(InputStream inputStream, IOException e, String message) { + IOUtils.closeQuietly(inputStream); + throw new TechnicalException(message, e); + } + + @Override + public void getAttachmentMetadata(GrpcGetAttachmentMetadataRequest request, StreamObserver<GrpcGetAttachmentMetadataResponse> responseObserver) { + } } diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java index bbb328c92b80034e5edd6897b3b4f9f49c03fab7..4aa7276aaaedc7df2c88b3cdd05c7db1c22c26c3 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java @@ -25,6 +25,7 @@ package de.ozgcloud.nachrichten.antragraum; import static java.util.Objects.*; +import java.io.PipedOutputStream; import java.time.ZonedDateTime; import java.util.Comparator; import java.util.Optional; @@ -41,10 +42,13 @@ import org.springframework.stereotype.Service; import de.ozgcloud.apilib.common.datatypes.GenericId; import de.ozgcloud.apilib.common.errorhandling.NotFoundException; +import de.ozgcloud.apilib.file.OzgCloudFileId; +import de.ozgcloud.apilib.file.OzgCloudFileService; import de.ozgcloud.nachrichten.NachrichtenManagerConfiguration; import de.ozgcloud.nachrichten.NachrichtenManagerProperties; import de.ozgcloud.nachrichten.common.vorgang.Vorgang; import de.ozgcloud.nachrichten.common.vorgang.VorgangService; +import de.ozgcloud.nachrichten.postfach.AttachedItemService; import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.PostfachNachrichtMapper; @@ -77,9 +81,14 @@ public class AntragraumService { private final AntragraumProperties properties; private final NachrichtenManagerProperties nachrichtenManagerProperties; - private final RueckfrageMapper rueckfrageMapper; + @Qualifier(NachrichtenManagerConfiguration.NACHRICHTEN_VORGANG_SERVICE) private final VorgangService vorgangService; + @Qualifier(NachrichtenManagerConfiguration.OZG_CLOUD_FILE_SERVICE_NAME) + private final OzgCloudFileService ozgCloudFileService; + private final AttachedItemService attachedItemService; + + private final RueckfrageMapper rueckfrageMapper; private final PostfachNachrichtMapper nachrichtMapper; @PostConstruct @@ -139,7 +148,7 @@ public class AntragraumService { public String sendRueckfrageAnswer(String samlToken, String rueckfrageId, PostfachNachricht nachricht) { verifyToken(samlToken); - verifyPostfachId(samlToken, nachricht); + verifyPostfachIdFromNachricht(samlToken, nachricht); return postfachNachrichtService.persistAnswer(rueckfrageId, nachricht); } @@ -150,20 +159,50 @@ public class AntragraumService { return postfachNachrichtService.findAnswers(BAYERN_ID_SERVICE_KONTO_TYPE, getPostfachId(samlToken), rueckfrageId); } - String getPostfachId(String samlToken) { - return decrypter.decryptPostfachId(parser.parse(samlToken)); - } - public PostfachNachricht getRueckfrage(String samlToken, String id) { verifyToken(samlToken); var nachricht = nachrichtMapper.fromMapToPostfachMail(postfachNachrichtService.getById(id)); - verifyPostfachId(samlToken, nachricht); + verifyPostfachIdFromNachricht(samlToken, nachricht); return nachricht; } + void verifyPostfachIdFromNachricht(String samlToken, PostfachNachricht nachricht) { + var vorgang = vorgangService.getVorgang(nachricht.getVorgangId()); + + verifyPostfachId(samlToken, nachricht, vorgang); + } + + String getPostfachId(String samlToken) { + return decrypter.decryptPostfachId(parser.parse(samlToken)); + } + + public boolean isAccessible(String samlToken, String trustLevel) { + return getTrustLevel(samlToken).getIntValue() >= TrustLevel.fromString(trustLevel).getIntValue(); + } + + TrustLevel getTrustLevel(String samlToken) { + return TrustLevel.fromString(decrypter.decryptTrustLevel(parseSamlToken(samlToken))); + } + + Response parseSamlToken(String samlToken) { + return parser.parse(samlToken); + } + + public void getAttachmentContent(AttachmentFileRequest request, PipedOutputStream pipedOutputStream) { + verifyAccessToFile(request); + ozgCloudFileService.writeFileDataToStream(OzgCloudFileId.from(request.fileId()), pipedOutputStream); + } + + void verifyAccessToFile(AttachmentFileRequest request) { + verifyToken(request.samlToken()); + var nachricht = attachedItemService.getPostfachNachricht(request.nachrichtId()); + verifyAccessToVorgang(request.samlToken(), nachricht); + verifyFileId(request.fileId(), nachricht); + } + void verifyToken(String token) { var errors = verifier.verify(token); if (CollectionUtils.isNotEmpty(errors)) { @@ -171,24 +210,30 @@ public class AntragraumService { } } - void verifyPostfachId(String samlToken, PostfachNachricht nachricht) { + void verifyAccessToVorgang(String token, PostfachNachricht nachricht) { var vorgang = vorgangService.getVorgang(nachricht.getVorgangId()); + verifyPostfachId(token, nachricht, vorgang); + verifyTrustLevel(token, nachricht, vorgang); + } + void verifyPostfachId(String samlToken, PostfachNachricht nachricht, Vorgang vorgang) { if (!StringUtils.equals(vorgang.getPostfachId(), getPostfachId(samlToken))) { LOG.info("PostfachId in token is not matching postfachId in vorgang."); throw new NotFoundException(GenericId.from(nachricht.getId()), "PostfachNachricht"); } } - public boolean isAccessible(String samlToken, String trustLevel) { - return getTrustLevel(samlToken).getIntValue() >= TrustLevel.fromString(trustLevel).getIntValue(); - } - - TrustLevel getTrustLevel(String samlToken) { - return TrustLevel.fromString(decrypter.decryptTrustLevel(parseSamlToken(samlToken))); + void verifyTrustLevel(String token, PostfachNachricht nachricht, Vorgang vorgang) { + if (!isAccessible(token, vorgang.getTrustLevel())) { + LOG.info("Trustlevel in token is not great enough to access vorgang."); + throw new NotFoundException(GenericId.from(nachricht.getId()), "PostfachNachricht"); + } } - Response parseSamlToken(String samlToken) { - return parser.parse(samlToken); + void verifyFileId(String fileId, PostfachNachricht nachricht) { + if (!nachricht.getAttachments().contains(fileId)) { + LOG.info("FileId is not present in attachment list of PostfachNachricht"); + throw new NotFoundException(GenericId.from(nachricht.getId()), "PostfachNachricht"); + } } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileMapper.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..7195b3122090501d4c5a03b251646908b2a04d2a --- /dev/null +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileMapper.java @@ -0,0 +1,23 @@ +package de.ozgcloud.nachrichten.antragraum; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; + +import de.ozgcloud.nachrichten.postfach.AttachmentFile; + +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.WARN) +interface AttachmentFileMapper { + + @Mapping(target = "mergeFrom", ignore = true) + @Mapping(target = "clearField", ignore = true) + @Mapping(target = "clearOneof", ignore = true) + @Mapping(target = "mergeUnknownFields", ignore = true) + @Mapping(target = "contentTypeBytes", ignore = true) + @Mapping(target = "nameBytes", ignore = true) + @Mapping(target = "unknownFields", ignore = true) + @Mapping(target = "allFields", ignore = true) + @Mapping(target = "size", ignore = true) + GrpcFileMetadata toMetadata(AttachmentFile file); +} diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileRequest.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..0ec4c7b56153a7b005defb45970b6af200ac17ef --- /dev/null +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileRequest.java @@ -0,0 +1,4 @@ +package de.ozgcloud.nachrichten.antragraum; + +public record AttachmentFileRequest(String samlToken, String nachrichtId, String fileId) { +} diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/RueckfrageAttachmentFileRequestMapper.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/RueckfrageAttachmentFileRequestMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..3f0bedb7dabcde94e8d130082a0d80de202e322c --- /dev/null +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/RueckfrageAttachmentFileRequestMapper.java @@ -0,0 +1,11 @@ +package de.ozgcloud.nachrichten.antragraum; + +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; + +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.WARN) +interface RueckfrageAttachmentFileRequestMapper { + + AttachmentFileRequest fromGrpc(GrpcGetAttachmentContentRequest request); +} diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteService.java index d854927466082238e4b4e09f111c53ff5cc0c453..b04d4daeafce7b3b4b1d5c725e043c66d4dd686e 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteService.java @@ -35,6 +35,7 @@ import de.ozgcloud.nachrichten.common.grpc.NachrichtenCallContextAttachingInterc import de.ozgcloud.vorgang.common.GrpcProperty; import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcFindVorgangAttachedItemRequest; import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItem; +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; import de.ozgcloud.vorgang.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub; import net.devh.boot.grpc.client.inject.GrpcClient; @@ -70,10 +71,6 @@ class AttachedItemRemoteService { return response.getVorgangAttachedItemsList().stream().map(postfachNachrichtMapper::fromAttachedItem); } - VorgangAttachedItemServiceBlockingStub getVorgangAttachedItemServiceStub() { - return vorgangAttachedItemServiceStub.withInterceptors(new NachrichtenCallContextAttachingInterceptor()); - } - GrpcFindVorgangAttachedItemRequest buildFindRequest(String vorgangId) { return GrpcFindVorgangAttachedItemRequest.newBuilder() .setVorgangId(vorgangId) @@ -81,4 +78,19 @@ class AttachedItemRemoteService { .setItemName(ITEM_NAME) .build(); } + + public PostfachNachricht getPostfachNachrichtById(String nachrichtId) { + var response = getVorgangAttachedItemServiceStub().getById(buildGetByIdRequest(nachrichtId)); + return postfachNachrichtMapper.fromAttachedItem(response.getVorgangAttachedItem()); + } + + VorgangAttachedItemServiceBlockingStub getVorgangAttachedItemServiceStub() { + return vorgangAttachedItemServiceStub.withInterceptors(new NachrichtenCallContextAttachingInterceptor()); + } + + GrpcVorgangAttachedItemRequest buildGetByIdRequest(String id) { + return GrpcVorgangAttachedItemRequest.newBuilder() + .setId(id) + .build(); + } } diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemService.java new file mode 100644 index 0000000000000000000000000000000000000000..92713cb7bda4835a048a95f4ad86fe4b1b6bef6c --- /dev/null +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachedItemService.java @@ -0,0 +1,16 @@ +package de.ozgcloud.nachrichten.postfach; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class AttachedItemService { + + private final AttachedItemRemoteService remoteService; + + public PostfachNachricht getPostfachNachricht(String id) { + return remoteService.getPostfachNachrichtById(id); + } +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java index a3815062085899da5db72cba9430d03f317a8c93..d6ea7add22aa4f5cf077400c844a7d74060af7b3 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java @@ -21,9 +21,16 @@ package de.ozgcloud.nachrichten.antragraum; 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.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.List; @@ -31,6 +38,8 @@ import java.util.Optional; import java.util.UUID; import java.util.stream.Stream; +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -39,17 +48,23 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; import org.mockito.Spy; +import com.google.protobuf.ByteString; + import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.nachrichten.common.vorgang.GrpcServiceKontoTestFactory; import de.ozgcloud.nachrichten.common.vorgang.Vorgang; import de.ozgcloud.nachrichten.common.vorgang.VorgangService; import de.ozgcloud.nachrichten.common.vorgang.VorgangTestFactory; +import de.ozgcloud.nachrichten.postfach.AttachmentFile; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory; import de.ozgcloud.nachrichten.postfach.osi.MessageTestFactory; import io.grpc.stub.StreamObserver; +import lombok.SneakyThrows; class AntragraumGrpcServiceTest { @@ -62,7 +77,10 @@ class AntragraumGrpcServiceTest { private AntragraumNachrichtMapper nachrichtMapper; @Mock private RueckfrageMapper rueckfrageMapper; - + @Mock + private AttachmentFileMapper attachmentFileMapper; + @Mock + private RueckfrageAttachmentFileRequestMapper rueckfrageAttachmentFileRequestMapper; @Mock private VorgangService vorgangService; @@ -448,4 +466,388 @@ class AntragraumGrpcServiceTest { assertThat(rueckfrageResponse.getRueckfrage()).isEqualTo(grpcRueckfrage); } } + + @Nested + class TestGetAttachmentContent { + + @Mock + private StreamObserver<GrpcGetAttachmentContentResponse> responseObserver; + + private final GrpcGetAttachmentContentRequest grpcRequest = GrpcGetAttachmentContentRequestTestFactory.create(); + + @Nested + class OnNoException { + + private PipedInputStream pipedInputStream; + private MockedConstruction<PipedInputStream> mockedConstructionInput; + + private PipedOutputStream pipedOutputStream; + private MockedConstruction<PipedOutputStream> mockedConstructionOutput; + private InputStream connectedInput; + + @BeforeEach + void setUpMockedConstruction() { + mockedConstructionInput = mockConstruction(PipedInputStream.class, (pipedInputStream, context) -> { + this.pipedInputStream = pipedInputStream; + doNothing().when(grpcService).sendFileContent(pipedInputStream, responseObserver); + }); + mockedConstructionOutput = mockConstruction(PipedOutputStream.class, (pipedOutputStream, context) -> { + this.pipedOutputStream = pipedOutputStream; + connectedInput = (InputStream) context.arguments().get(0); + }); + } + + @AfterEach + void closeMocks() { + mockedConstructionInput.close(); + mockedConstructionOutput.close(); + } + + @Test + void shouldConstructInputStream() { + getAttachmentContent(); + + assertThat(mockedConstructionInput.constructed()).hasSize(1); + } + + @Test + void shouldConstructOutputStream() { + getAttachmentContent(); + + assertThat(mockedConstructionOutput.constructed()).hasSize(1); + } + + @Test + void shouldConnectPipes() { + getAttachmentContent(); + + assertThat(connectedInput).isEqualTo(pipedInputStream); + } + + @Test + void shouldCallGetAttachmentFileContent() { + getAttachmentContent(); + + verify(grpcService).getAttachmentFileContent(grpcRequest, pipedOutputStream); + } + + @Test + void shouldSendFileContent() { + getAttachmentContent(); + + verify(grpcService).sendFileContent(pipedInputStream, responseObserver); + } + } + + @Nested + class OnIOException { + + @BeforeEach + void setUpMock() { + doNothing().when(grpcService).sendFileContent(any(), eq(responseObserver)); + } + + @Test + @SneakyThrows + void shouldThrowTechnicalExceptionOnErrorOnInputStream() { + try (var mockedConstructionInput = mockConstruction(PipedInputStream.class, + (mock, context) -> doThrow(IOException.class).when(mock).close())) { + + assertThrows(TechnicalException.class, () -> getAttachmentContent()); + } + } + + @Test + @SneakyThrows + void shouldThrowTechnicalExceptionOnErrorOnOutputStream() { + try (var mockedConstructionInput = mockConstruction(PipedOutputStream.class, + (mock, context) -> doThrow(IOException.class).when(mock).close())) { + + assertThrows(TechnicalException.class, () -> getAttachmentContent()); + } + } + } + + private void getAttachmentContent() { + grpcService.getAttachmentContent(grpcRequest, responseObserver); + } + } + + @Nested + class TestGetAttachmentFileContent { + @Mock + private PipedOutputStream pipedOutputStream; + + private MockedConstruction<Thread> mockedConstructionThread; + private Runnable passedRunnable; + private Thread thread; + + private final GrpcGetAttachmentContentRequest grpcRequest = GrpcGetAttachmentContentRequestTestFactory.create(); + private final AttachmentFileRequest request = AttachmentFileRequestTestFactory.create(); + + @BeforeEach + void mock() { + mockedConstructionThread = mockConstruction(Thread.class, (mock, context) -> { + passedRunnable = (Runnable) context.arguments().get(0); + thread = mock; + }); + } + + @AfterEach + void closeMock() { + mockedConstructionThread.close(); + } + + @Test + void shouldConstructNewThread() { + getRueckfrageAttachmentFile(); + + assertThat(mockedConstructionThread.constructed()).hasSize(1); + } + + @Test + void shouldStartThread() { + getRueckfrageAttachmentFile(); + + verify(thread).start(); + } + + @Nested + class TestRunnable { + @BeforeEach + void mock() { + when(rueckfrageAttachmentFileRequestMapper.fromGrpc(grpcRequest)).thenReturn(request); + } + + @Test + void shouldMapRequest() { + getRueckfrageAttachmentFile(); + + passedRunnable.run(); + + verify(rueckfrageAttachmentFileRequestMapper).fromGrpc(grpcRequest); + } + + @Test + void shouldCallServiceToGetAttachmentContent() { + getRueckfrageAttachmentFile(); + + passedRunnable.run(); + + verify(service).getAttachmentContent(request, pipedOutputStream); + } + + } + + private void getRueckfrageAttachmentFile() { + grpcService.getAttachmentFileContent(grpcRequest, pipedOutputStream); + } + } + + @Nested + class TestSendFileMetadata { + + @Mock + private StreamObserver<GrpcGetAttachmentMetadataResponse> responseObserver; + + private AttachmentFile attachmentFile; + + private final GrpcFileMetadata metadata = GrpcFileMetadataTestFactory.create(); + + @BeforeEach + void mock() { + when(attachmentFileMapper.toMetadata(attachmentFile)).thenReturn(metadata); + } + + @Test + void shouldMapMetadata() { + sendFileMetadata(); + + verify(attachmentFileMapper).toMetadata(attachmentFile); + } + + @Test + void shouldSendMetadata() { + sendFileMetadata(); + + verify(responseObserver).onNext(argThat((response) -> response.getFileMetadata().equals(metadata))); + } + + private void sendFileMetadata() { + grpcService.sendFileMetadata(attachmentFile, responseObserver); + } + } + + @Nested + class TestSendFileContent { + + @Mock + private StreamObserver<GrpcGetAttachmentContentResponse> responseObserver; + + @Mock + private InputStream fileContent; + + @Nested + class TestWithByteArrayInputStream { + private final byte[] byteContent = FileContentTestFactory.createContentInByte((int) (AntragraumGrpcService.CHUNK_SIZE * 1.5)); + private final InputStream inputStream = new ByteArrayInputStream(byteContent); + + @BeforeEach + void setUpMock() { + doReturn(inputStream).when(grpcService).createBufferedInputStream(fileContent); + } + + @Test + void shouldCreateBufferedInputStream() { + sendFileContent(); + + verify(grpcService).createBufferedInputStream(fileContent); + } + + @Test + void shouldSendFirstDataChunk() { + sendFileContent(); + + verify(responseObserver) + .onNext(argThat((response) -> response.getFileContent() + .equals(ByteString.copyFrom(byteContent, 0, AntragraumGrpcService.CHUNK_SIZE)))); + } + + @Test + void shouldSendSecondDataChunk() { + sendFileContent(); + + verify(responseObserver) + .onNext(argThat((response) -> response.getFileContent() + .equals(ByteString.copyFrom(byteContent, AntragraumGrpcService.CHUNK_SIZE, + byteContent.length - AntragraumGrpcService.CHUNK_SIZE)))); + } + + @Test + void shouldComplete() { + sendFileContent(); + + verify(responseObserver).onCompleted(); + } + } + + @Nested + class TestWithMockedInputStream { + + @Mock + private BufferedInputStream inputStream; + + @BeforeEach + void setUpMock() { + doReturn(inputStream).when(grpcService).createBufferedInputStream(fileContent); + } + + @Test + @SneakyThrows + void shouldCloseInputStream() { + when(inputStream.read(any())).thenReturn(-1); + + sendFileContent(); + + verify(inputStream).close(); + } + + @Nested + class OnIOException { + + private MockedStatic<IOUtils> mockedIOUtils; + + @BeforeEach + @SneakyThrows + void setUpMock() { + when(inputStream.read(any())).thenThrow(new IOException()); + mockedIOUtils = mockStatic(IOUtils.class); + } + + @AfterEach + void cleanUp() { + mockedIOUtils.close(); + } + + @Test + void shouldThrowTechnicalException() { + assertThrows(TechnicalException.class, + () -> sendFileContent()); + } + + @Test + void shouldCloseFileContentStreamQuietly() { + try { + sendFileContent(); + } catch (TechnicalException e) { + } + + mockedIOUtils.verify(() -> IOUtils.closeQuietly(fileContent)); + } + } + } + + private void sendFileContent() { + grpcService.sendFileContent(fileContent, responseObserver); + } + } + + @Nested + class TestCreateBufferedInputStream { + + private MockedConstruction<BufferedInputStream> mockConstructionBufferedInputStream; + private InputStream passedInputStream; + private BufferedInputStream constructedInputStream; + private int chunkSize; + + @Mock + private InputStream fileContent; + + @BeforeEach + void setUpMock() { + mockConstructionBufferedInputStream = mockConstruction(BufferedInputStream.class, (mock, context) -> { + passedInputStream = (InputStream) context.arguments().get(0); + chunkSize = (int) context.arguments().get(1); + constructedInputStream = mock; + }); + } + + @AfterEach + void closeMock() { + mockConstructionBufferedInputStream.close(); + } + + @Test + void shouldConstructBufferedInputStream() { + createBufferedInputStream(); + + assertThat(mockConstructionBufferedInputStream.constructed()).hasSize(1); + } + + @Test + void shouldConstructBufferedInputStreamWithFileContent() { + createBufferedInputStream(); + + assertThat(passedInputStream).isEqualTo(fileContent); + } + + @Test + void shouldConstructBufferedInputStreamWithChunkSize() { + createBufferedInputStream(); + + assertThat(chunkSize).isEqualTo(AntragraumGrpcService.CHUNK_SIZE); + } + + @Test + void shouldReturnBufferedInputStream() { + var returnedInputStream = createBufferedInputStream(); + + assertThat(returnedInputStream).isEqualTo(constructedInputStream); + } + + private InputStream createBufferedInputStream() { + return grpcService.createBufferedInputStream(fileContent); + } + + } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java index f5220ab82ed6c6dfa41fb8aba83a080aa1785837..e0575c49f466d1771c828b5cfb9c416af21f6c1f 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.io.PipedOutputStream; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; @@ -22,12 +23,15 @@ import org.mockito.Spy; import org.opensaml.saml.saml2.core.Response; import de.ozgcloud.apilib.common.errorhandling.NotFoundException; +import de.ozgcloud.apilib.file.OzgCloudFileId; +import de.ozgcloud.apilib.file.OzgCloudFileService; import de.ozgcloud.nachrichten.NachrichtenManagerProperties; import de.ozgcloud.nachrichten.common.vorgang.GrpcPostfachAddressTestFactory; import de.ozgcloud.nachrichten.common.vorgang.GrpcServiceKontoTestFactory; import de.ozgcloud.nachrichten.common.vorgang.Vorgang; import de.ozgcloud.nachrichten.common.vorgang.VorgangService; import de.ozgcloud.nachrichten.common.vorgang.VorgangTestFactory; +import de.ozgcloud.nachrichten.postfach.AttachedItemService; import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.PostfachNachrichtMapper; @@ -58,6 +62,10 @@ class AntragraumServiceTest { private VorgangService vorgangService; @Mock private PostfachNachrichtMapper nachrichtMapper; + @Mock + private AttachedItemService attachedItemService; + @Mock + private OzgCloudFileService ozgCloudFileService; @Nested class TestGetAntragsraumUrl { @@ -305,7 +313,7 @@ class AntragraumServiceTest { @BeforeEach void mock() { doNothing().when(service).verifyToken(any()); - doNothing().when(service).verifyPostfachId(any(), any()); + doNothing().when(service).verifyPostfachIdFromNachricht(any(), any()); } @Test @@ -323,10 +331,10 @@ class AntragraumServiceTest { } @Test - void shouldVerifyPostfachId() { + void shouldVerifyPostfachIdFromNachricht() { service.sendRueckfrageAnswer(SAML_TOKEN, RUECKFRAGE_ID, NACHRICHT); - verify(service).verifyPostfachId(SAML_TOKEN, NACHRICHT); + verify(service).verifyPostfachIdFromNachricht(SAML_TOKEN, NACHRICHT); } } @@ -402,7 +410,7 @@ class AntragraumServiceTest { when(nachrichtMapper.fromMapToPostfachMail(any())).thenReturn(nachricht); doNothing().when(service).verifyToken(any()); - doNothing().when(service).verifyPostfachId(any(), any()); + doNothing().when(service).verifyPostfachIdFromNachricht(any(), any()); } @Test @@ -434,10 +442,10 @@ class AntragraumServiceTest { } @Test - void shouldCallVerifyPostfachId() { + void shouldCallVerifyPostfachIdFromNachricht() { getRueckfrage(); - verify(service).verifyPostfachId(samlToken, nachricht); + verify(service).verifyPostfachIdFromNachricht(samlToken, nachricht); } private PostfachNachricht getRueckfrage() { @@ -445,32 +453,47 @@ class AntragraumServiceTest { } } - @DisplayName("Verify postfachId") + @DisplayName("Verify postfachId from Nachricht") @Nested - class TestVerifyPostfachId { + class TestVerifyPostfachIdFromNachricht { private static final String SAML_TOKEN = "TOKEN"; private static final PostfachNachricht POSTFACH_NACHRICHT = PostfachNachrichtTestFactory.create(); + private final Vorgang vorgang = VorgangTestFactory.create(); @BeforeEach void mock() { - when(vorgangService.getVorgang(any())).thenReturn(VorgangTestFactory.create()); + when(vorgangService.getVorgang(any())).thenReturn(vorgang); + doNothing().when(service).verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT, vorgang); } @Test void shouldCallVorgangService() { - doReturn(GrpcPostfachAddressTestFactory.STRING_BASED_IDENTIFIER_POSTFACH_ID_VALUE).when(service).getPostfachId(any()); - - service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT); + service.verifyPostfachIdFromNachricht(SAML_TOKEN, POSTFACH_NACHRICHT); verify(vorgangService).getVorgang(MessageTestFactory.VORGANG_ID); } + @Test + void shouldCallVerifyPostfachId() { + service.verifyPostfachIdFromNachricht(SAML_TOKEN, POSTFACH_NACHRICHT); + + verify(service).verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT, vorgang); + } + } + + @Nested + class TestVerifyPostfachId { + + private final String SAML_TOKEN = "TOKEN"; + private final PostfachNachricht POSTFACH_NACHRICHT = PostfachNachrichtTestFactory.create(); + private final Vorgang vorgang = VorgangTestFactory.create(); + @Test void shouldGetPostfachIdFromToken() { doReturn(GrpcPostfachAddressTestFactory.STRING_BASED_IDENTIFIER_POSTFACH_ID_VALUE).when(service).getPostfachId(any()); - service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT); + service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT, vorgang); verify(service).getPostfachId(SAML_TOKEN); } @@ -479,7 +502,7 @@ class AntragraumServiceTest { void shouldThrowExceptionOnMismatchPostfachId() { doReturn("not-matching-postfach-id").when(service).getPostfachId(any()); - assertThatThrownBy(() -> service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT)) + assertThatThrownBy(() -> service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT, vorgang)) .isInstanceOf(NotFoundException.class) .hasMessageContaining("PostfachNachricht") .hasMessageContaining(PostfachNachrichtTestFactory.ID); @@ -489,7 +512,7 @@ class AntragraumServiceTest { void shouldNotThrowExceptionOnMatchingPostfachId() { doReturn(GrpcPostfachAddressTestFactory.STRING_BASED_IDENTIFIER_POSTFACH_ID_VALUE).when(service).getPostfachId(any()); - assertDoesNotThrow(() -> service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT)); + assertDoesNotThrow(() -> service.verifyPostfachId(SAML_TOKEN, POSTFACH_NACHRICHT, vorgang)); } } @@ -587,4 +610,177 @@ class AntragraumServiceTest { assertThat(parseResponse).isEqualTo(response); } } + + @Nested + class TestGetAttachmentContent { + + @Mock + private PipedOutputStream pipedOutputStream; + + private final AttachmentFileRequest request = AttachmentFileRequestTestFactory.create(); + + @BeforeEach + void mock() { + doNothing().when(service).verifyAccessToFile(request); + } + + @Test + void shouldCallVerifyAccessToFile() { + getAttachmentContent(); + + verify(service).verifyAccessToFile(request); + } + + @Test + void shouldCallOzgCloudFileServiceToWriteData() { + getAttachmentContent(); + + verify(ozgCloudFileService).writeFileDataToStream(OzgCloudFileId.from(AttachmentFileRequestTestFactory.FILE_ID), pipedOutputStream); + } + + private void getAttachmentContent() { + service.getAttachmentContent(request, pipedOutputStream); + } + } + + @Nested + class TestVerifyAccessToFile { + + private final AttachmentFileRequest request = AttachmentFileRequestTestFactory.create(); + private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create(); + + @BeforeEach + void mock() { + when(attachedItemService.getPostfachNachricht(AttachmentFileRequestTestFactory.NACHRICHT_ID)).thenReturn(nachricht); + doNothing().when(service).verifyAccessToVorgang(AttachmentFileRequestTestFactory.TOKEN, nachricht); + doNothing().when(service).verifyFileId(AttachmentFileRequestTestFactory.FILE_ID, nachricht); + } + + @Test + void shouldVerifySamlToken() { + verifyAccessToFile(); + + verify(service).verifyToken(AttachmentFileRequestTestFactory.TOKEN); + } + + @Test + void shouldCallAttachedItemServiceToGetPostfachNachricht() { + verifyAccessToFile(); + + verify(attachedItemService).getPostfachNachricht(AttachmentFileRequestTestFactory.NACHRICHT_ID); + } + + @Test + void shouldCallVerifyAccessToVorgang() { + verifyAccessToFile(); + + verify(service).verifyAccessToVorgang(AttachmentFileRequestTestFactory.TOKEN, nachricht); + } + + @Test + void shouldVerifyFileId() { + verifyAccessToFile(); + + verify(service).verifyFileId(AttachmentFileRequestTestFactory.FILE_ID, nachricht); + } + + private void verifyAccessToFile() { + service.verifyAccessToFile(request); + } + + } + + @Nested + class TestVerifyAccessToVorgang { + + private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create(); + private final Vorgang vorgang = VorgangTestFactory.create(); + + @BeforeEach + void mock() { + when(vorgangService.getVorgang(MessageTestFactory.VORGANG_ID)).thenReturn(vorgang); + doNothing().when(service).verifyPostfachId(AttachmentFileRequestTestFactory.TOKEN, nachricht, vorgang); + doNothing().when(service).verifyTrustLevel(AttachmentFileRequestTestFactory.TOKEN, nachricht, vorgang); + } + + @Test + void shouldCallVorgangService() { + verifyAccessToVorgang(); + + verify(vorgangService).getVorgang(MessageTestFactory.VORGANG_ID); + } + + @Test + void shouldCallVerifyPostfachId() { + verifyAccessToVorgang(); + + verify(service).verifyPostfachId(AttachmentFileRequestTestFactory.TOKEN, nachricht, vorgang); + } + + @Test + void shouldVerifyTrustLevel() { + verifyAccessToVorgang(); + + verify(service).verifyTrustLevel(AttachmentFileRequestTestFactory.TOKEN, nachricht, vorgang); + } + + private void verifyAccessToVorgang() { + service.verifyAccessToVorgang(AttachmentFileRequestTestFactory.TOKEN, nachricht); + } + } + + @Nested + class TestVerifyTrustLevel { + + private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create(); + private final Vorgang vorgang = VorgangTestFactory.create(); + + @Test + void shouldCallIsAccessible() { + doReturn(true).when(service).isAccessible(AttachmentFileRequestTestFactory.TOKEN, GrpcServiceKontoTestFactory.TRUST_LEVEL); + + verifyTrustLevel(); + + verify(service).isAccessible(AttachmentFileRequestTestFactory.TOKEN, GrpcServiceKontoTestFactory.TRUST_LEVEL); + } + + @Test + void shouldThrowExceptionOnNotAccessible() { + doReturn(false).when(service).isAccessible(AttachmentFileRequestTestFactory.TOKEN, GrpcServiceKontoTestFactory.TRUST_LEVEL); + + assertThatThrownBy(() -> verifyTrustLevel()) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("PostfachNachricht") + .hasMessageContaining(PostfachNachrichtTestFactory.ID); + } + + @Test + void shouldNotThrowExceptionOnAccessible() { + doReturn(true).when(service).isAccessible(AttachmentFileRequestTestFactory.TOKEN, GrpcServiceKontoTestFactory.TRUST_LEVEL); + + assertDoesNotThrow(() -> verifyTrustLevel()); + } + + private void verifyTrustLevel() { + service.verifyTrustLevel(AttachmentFileRequestTestFactory.TOKEN, nachricht, vorgang); + } + } + + @Nested + class TestVerifyFileId { + private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create(); + + @Test + void shouldThrowExceptionOnNotMatchingFileId() { + assertThatThrownBy(() -> service.verifyFileId(PostfachNachrichtTestFactory.ATTACHMENT_FILE_ID + "wrong", nachricht)) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("PostfachNachricht") + .hasMessageContaining(PostfachNachrichtTestFactory.ID); + } + + @Test + void shouldNotThrowExceptionOnMatchingFileId() { + assertDoesNotThrow(() -> service.verifyFileId(PostfachNachrichtTestFactory.ATTACHMENT_FILE_ID, nachricht)); + } + } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileRequestTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..52afb9d5b5175fc4b165441a831425b55274a68a --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AttachmentFileRequestTestFactory.java @@ -0,0 +1,12 @@ +package de.ozgcloud.nachrichten.antragraum; + +public class AttachmentFileRequestTestFactory { + + public static final String NACHRICHT_ID = GrpcGetAttachmentContentRequestTestFactory.NACHRICHT_ID; + public static final String FILE_ID = GrpcGetAttachmentContentRequestTestFactory.FILE_ID; + public static final String TOKEN = GrpcGetAttachmentContentRequestTestFactory.TOKEN; + + public static AttachmentFileRequest create() { + return new AttachmentFileRequest(TOKEN, NACHRICHT_ID, FILE_ID); + } +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/FileContentTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/FileContentTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e36617b77b7921b98aae465fdcde16710eb43084 --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/FileContentTestFactory.java @@ -0,0 +1,16 @@ +package de.ozgcloud.nachrichten.antragraum; + +import java.util.Random; + +public class FileContentTestFactory { + + public static byte[] createContentInByte(int fileSize) { + Random r = new Random(); + byte[] chunk = new byte[fileSize]; + for (int i = 0; i < chunk.length; i++) { + chunk[i] = (byte) (r.nextInt('z' - 'a') + 'a'); + } + return chunk; + } + +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcFileMetadataTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcFileMetadataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..1115f0df5776d7b95629eaae03603cb9dec3de32 --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcFileMetadataTestFactory.java @@ -0,0 +1,24 @@ +package de.ozgcloud.nachrichten.antragraum; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.nachrichten.antragraum.GrpcFileMetadata.Builder; + +public class GrpcFileMetadataTestFactory { + + public static final long SIZE = 1000; + public static final String NAME = LoremIpsum.getInstance().getName(); + public static final String CONTENT_TYPE = LoremIpsum.getInstance().getWords(1); + + public static GrpcFileMetadata create() { + return createBuilder().build(); + + } + + public static Builder createBuilder() { + return GrpcFileMetadata.newBuilder() + .setContentType(CONTENT_TYPE) + .setName(NAME) + .setSize(SIZE); + } +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcGetAttachmentContentRequestTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcGetAttachmentContentRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a3dc528e0970802b9c400235aa624d32392c132d --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcGetAttachmentContentRequestTestFactory.java @@ -0,0 +1,25 @@ +package de.ozgcloud.nachrichten.antragraum; + +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory; + +public class GrpcGetAttachmentContentRequestTestFactory { + + public static final String TOKEN = LoremIpsum.getInstance().getWords(1); + public static final String FILE_ID = UUID.randomUUID().toString(); + public static final String NACHRICHT_ID = PostfachNachrichtTestFactory.ID; + + public static GrpcGetAttachmentContentRequest create() { + return createBuilder().build(); + } + + public static GrpcGetAttachmentContentRequest.Builder createBuilder() { + return GrpcGetAttachmentContentRequest.newBuilder() + .setSamlToken(TOKEN) + .setNachrichtId(NACHRICHT_ID) + .setFileId(FILE_ID); + } +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/RueckfrageAttachmentFileRequestMapperTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/RueckfrageAttachmentFileRequestMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4df7907fa9b8d6807423e2b3210c1e2f90f9d940 --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/RueckfrageAttachmentFileRequestMapperTest.java @@ -0,0 +1,23 @@ +package de.ozgcloud.nachrichten.antragraum; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class RueckfrageAttachmentFileRequestMapperTest { + + private final RueckfrageAttachmentFileRequestMapper mapper = Mappers.getMapper(RueckfrageAttachmentFileRequestMapper.class); + + @Nested + class TestFromGrpc { + + @Test + void shouldMapToRueckfrageAttachmentFileRequest() { + var request = mapper.fromGrpc(GrpcGetAttachmentContentRequestTestFactory.create()); + + assertThat(request).isEqualTo(AttachmentFileRequestTestFactory.create()); + } + } +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteServiceTest.java index 0c879370f9fc9d636ca233686e98a63d69f816d5..a300996e44977ea8c6e02853bc11c172131f13a6 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteServiceTest.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemRemoteServiceTest.java @@ -46,6 +46,8 @@ import de.ozgcloud.vorgang.common.GrpcProperty; import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcFindVorgangAttachedItemRequest; import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcFindVorgangAttachedItemResponse; import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItem; +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemResponse; import de.ozgcloud.vorgang.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub; class AttachedItemRemoteServiceTest { @@ -264,4 +266,75 @@ class AttachedItemRemoteServiceTest { assertThat(postfachNachrichten).containsExactly(postfachNachricht); } } + + @Nested + class TestGetPostfachNachrichtById { + + @Mock + private VorgangAttachedItemServiceBlockingStub stubWithInterceptor; + + private final GrpcVorgangAttachedItemRequest request = GrpcVorgangAttachedItemRequestTestFactory.create(); + private final GrpcVorgangAttachedItemResponse response = GrpcVorgangAttachedItemResponseTestFactory.create(); + + @BeforeEach + void mock() { + doReturn(stubWithInterceptor).when(service).getVorgangAttachedItemServiceStub(); + doReturn(request).when(service).buildGetByIdRequest(PostfachNachrichtTestFactory.ID); + when(stubWithInterceptor.getById(request)).thenReturn(response); + } + + @Test + void shouldCallGetVorgangAttachedItemServiceStub() { + getPostfachNachrichtById(); + + verify(service).getVorgangAttachedItemServiceStub(); + } + + @Test + void shouldCallBuildGetByIdRequest() { + getPostfachNachrichtById(); + + verify(service).buildGetByIdRequest(PostfachNachrichtTestFactory.ID); + } + + @Test + void shouldCallServiceStubWithInterceptor() { + getPostfachNachrichtById(); + + verify(stubWithInterceptor).getById(request); + } + + @Test + void shouldMapToPostfachNachricht() { + getPostfachNachrichtById(); + + verify(postfachNachrichtMapper).fromAttachedItem(GrpcVorgangAttachedItemResponseTestFactory.ATTACHED_ITEM); + } + + @Test + void shouldReturnMappedPostfachNachricht() { + var mappedPostfachNachricht = PostfachNachrichtTestFactory.create(); + when(postfachNachrichtMapper.fromAttachedItem(GrpcVorgangAttachedItemResponseTestFactory.ATTACHED_ITEM)) + .thenReturn(mappedPostfachNachricht); + + var postfachNachricht = getPostfachNachrichtById(); + + assertThat(postfachNachricht).isEqualTo(mappedPostfachNachricht); + } + + private PostfachNachricht getPostfachNachrichtById() { + return service.getPostfachNachrichtById(PostfachNachrichtTestFactory.ID); + } + } + + @Nested + class TestBuildGetByIdRequest { + + @Test + void shouldReturnGrpcRequest() { + var request = service.buildGetByIdRequest(PostfachNachrichtTestFactory.ID); + + assertThat(request).isEqualTo(GrpcVorgangAttachedItemRequestTestFactory.create()); + } + } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c4377c8d2b9ac3d0f7eb3b20dc3160eea7781385 --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/AttachedItemServiceTest.java @@ -0,0 +1,45 @@ +package de.ozgcloud.nachrichten.postfach; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +class AttachedItemServiceTest { + + @InjectMocks + private AttachedItemService service; + + @Mock + private AttachedItemRemoteService remoteService; + + @Nested + class TestGetPostfachNachricht { + + private final PostfachNachricht postfachNachricht = PostfachNachrichtTestFactory.create(); + + @Test + void shouldCallRemoteService() { + getPostfachNachricht(); + + verify(remoteService).getPostfachNachrichtById(PostfachNachrichtTestFactory.ID); + } + + @Test + void shouldReturnPostfachNachricht() { + when(remoteService.getPostfachNachrichtById(PostfachNachrichtTestFactory.ID)).thenReturn(postfachNachricht); + + var result = getPostfachNachricht(); + + assertThat(result).isEqualTo(postfachNachricht); + } + + private PostfachNachricht getPostfachNachricht() { + return service.getPostfachNachricht(PostfachNachrichtTestFactory.ID); + } + } + +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/GrpcVorgangAttachedItemRequestTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/GrpcVorgangAttachedItemRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2f371dd485369072b0e7b36aafa917c2eb469966 --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/GrpcVorgangAttachedItemRequestTestFactory.java @@ -0,0 +1,16 @@ +package de.ozgcloud.nachrichten.postfach; + +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemRequest.Builder; + +public class GrpcVorgangAttachedItemRequestTestFactory { + + public static GrpcVorgangAttachedItemRequest create() { + return createBuilder().build(); + } + + public static Builder createBuilder() { + return GrpcVorgangAttachedItemRequest.newBuilder() + .setId(PostfachNachrichtTestFactory.ID); + } +} diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/GrpcVorgangAttachedItemResponseTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/GrpcVorgangAttachedItemResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ad87b1859ce2cd0e2bd47a7b468a31aae735983e --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/GrpcVorgangAttachedItemResponseTestFactory.java @@ -0,0 +1,19 @@ +package de.ozgcloud.nachrichten.postfach; + +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItem; +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemResponse; +import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemResponse.Builder; + +public class GrpcVorgangAttachedItemResponseTestFactory { + + public static final GrpcVorgangAttachedItem ATTACHED_ITEM = GrpcVorgangAttachedItemTestFactory.create(); + + public static GrpcVorgangAttachedItemResponse create() { + return createBuilder().build(); + } + + public static Builder createBuilder() { + return GrpcVorgangAttachedItemResponse.newBuilder() + .setVorgangAttachedItem(ATTACHED_ITEM); + } +}