diff --git a/nachrichten-manager-interface/pom.xml b/nachrichten-manager-interface/pom.xml index e2741639bd1a779001d0cf6f84563a71f70702e9..525544d7ea16d624325341b9ecc01d87b52e7e4c 100644 --- a/nachrichten-manager-interface/pom.xml +++ b/nachrichten-manager-interface/pom.xml @@ -31,7 +31,7 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-dependencies</artifactId> - <version>4.5.0</version> + <version>4.7.0</version> <relativePath/> </parent> diff --git a/nachrichten-manager-server/pom.xml b/nachrichten-manager-server/pom.xml index 2983ff7d66ceb26c05ae5a31cbbcd880240d36ae..d7ea12760090097fa41473c9f2e81b397f125ccf 100644 --- a/nachrichten-manager-server/pom.xml +++ b/nachrichten-manager-server/pom.xml @@ -48,8 +48,8 @@ <bayernid-proxy-interface.version>0.7.0</bayernid-proxy-interface.version> <vorgang-manager.version>2.17.0</vorgang-manager.version> <muk-postfach.version>0.1.0</muk-postfach.version> - <api-lib.version>0.13.0</api-lib.version> - <ozgcloud-common.version>4.5.0</ozgcloud-common.version> + <api-lib.version>0.16.0-SNAPSHOT</api-lib.version> + <ozgcloud-common.version>4.7.0</ozgcloud-common.version> </properties> <dependencies> @@ -241,6 +241,10 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>de.ozgcloud.common</groupId> + <artifactId>ozgcloud-common-lib</artifactId> + </dependency> </dependencies> <build> diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProvider.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProvider.java index c64af3e512b11fece4881f0875c96b738561622d..fcf82624f923b069927a5aa7a70c65451905b77c 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProvider.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProvider.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Component; import de.ozgcloud.apilib.common.callcontext.CallContext; import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider; +import de.ozgcloud.apilib.vorgang.OzgCloudUserIdMapper; import de.ozgcloud.nachrichten.common.grpc.NachrichtenCallContextAttachingInterceptor; import lombok.RequiredArgsConstructor; @@ -11,8 +12,11 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class NachrichtenManagerCallContextProvider implements OzgCloudCallContextProvider { + private final OzgCloudUserIdMapper userIdMapper; + @Override public CallContext provideContext() { - return CallContext.builder().clientName(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_CLIENT_NAME).build(); + return CallContext.builder().clientName(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_CLIENT_NAME) + .userId(userIdMapper.toUserId(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_SENDER_USER_ID)).build(); } } \ 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 7f8d749fcc4f46c569fdc72071ebf779a13282d2..48d588ffcd8ed11533b5b5319393ecb4adf5c715 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 @@ -1,5 +1,7 @@ package de.ozgcloud.nachrichten; +import jakarta.activation.MimetypesFileTypeMap; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -24,6 +26,8 @@ public class NachrichtenManagerConfiguration { public static final String NACHRICHTEN_VORGANG_REMOTE_SERVICE = "nachrichten_vorgangRemoteService"; public static final String NACHRICHTEN_ATTACHED_ITEM_SERVICE = "nachrichten_attachedItemService"; public static final String NACHRICHTEN_OZG_CLOUD_FILE_MAPPER = "nachrichten_OzgCloudFileMapperImpl"; + public static final String ATTACHMENT_FILE_SERVICE_NAME = "nachrichten_AttachmentFileService"; + public static final String ATTACHMENT_FILE_MAPPER_NAME = "nachrichten_AttachmentFileMapper"; public static final String GRPC_VORGANG_MANAGER_NAME = "vorgang-manager"; public static final String GRPC_COMMAND_MANAGER_NAME = "command-manager"; @@ -47,4 +51,10 @@ public class NachrichtenManagerConfiguration { OzgCloudFileService grpcOzgCloudFileService(NachrichtenManagerCallContextProvider contextProvider, OzgCloudFileMapper mapper) { return new GrpcOzgCloudFileService(fileServiceBlockingStub, fileServiceAsyncServiceStub, contextProvider, mapper); } + + @Bean + MimetypesFileTypeMap mimetypesFileTypeMap() { + // uses map file: src/main/resources/mime.types + return new MimetypesFileTypeMap(); + } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptor.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptor.java index d3fa03da97bf23b79dfc2d31325b28777260d8be..90f7c91aa41ea10460a606281743bb1d6f47d2ef 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptor.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptor.java @@ -23,10 +23,11 @@ */ package de.ozgcloud.nachrichten.common.grpc; -import static de.ozgcloud.common.grpc.GrpcUtil.*; - import java.util.UUID; +import org.apache.logging.log4j.ThreadContext; + +import de.ozgcloud.common.grpc.GrpcUtil; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.ClientCall; @@ -37,12 +38,10 @@ import io.grpc.MethodDescriptor; public class NachrichtenCallContextAttachingInterceptor implements ClientInterceptor { - static final String KEY_USER_ID = "USER_ID-bin"; - static final String KEY_CLIENT_NAME = "CLIENT_NAME-bin"; - static final String KEY_REQUEST_ID = "REQUEST_ID-bin"; + static final String REQUEST_ID_KEY = "requestId"; public static final String NACHRICHTEN_MANAGER_CLIENT_NAME = "OzgCloud_NachrichtenManager"; - static final String NACHRICHTEN_MANAGER_SENDER_USER_ID = "system-nachrichten_manager-sender"; + public static final String NACHRICHTEN_MANAGER_SENDER_USER_ID = "system-nachrichten_manager-sender"; // <A> = Request, <B> = Response @Override @@ -52,7 +51,7 @@ public class NachrichtenCallContextAttachingInterceptor implements ClientInterce final class CallContextAttachingClientCall<A, B> extends SimpleForwardingClientCall<A, B> { - protected CallContextAttachingClientCall(ClientCall<A, B> delegate) { + CallContextAttachingClientCall(ClientCall<A, B> delegate) { super(delegate); } @@ -62,19 +61,18 @@ public class NachrichtenCallContextAttachingInterceptor implements ClientInterce super.start(responseListener, headers); } - private Metadata buildCallContextMetadata() { + Metadata buildCallContextMetadata() { var metadata = new Metadata(); - metadata.put(createKeyOf(KEY_USER_ID), NACHRICHTEN_MANAGER_SENDER_USER_ID.getBytes()); - metadata.put(createKeyOf(KEY_CLIENT_NAME), NACHRICHTEN_MANAGER_CLIENT_NAME.getBytes()); - metadata.put(createKeyOf(KEY_REQUEST_ID), generateRequestId().getBytes()); + metadata.put(GrpcUtil.HEADER_KEY_USER_ID, NACHRICHTEN_MANAGER_SENDER_USER_ID.getBytes()); + metadata.put(GrpcUtil.HEADER_KEY_CLIENT_NAME, NACHRICHTEN_MANAGER_CLIENT_NAME.getBytes()); + metadata.put(GrpcUtil.HEADER_KEY_REQUEST_ID, getRequestId()); return metadata; } - // TODO OZG-1974 requestId zentraler erzeugen - private String generateRequestId() { - return UUID.randomUUID().toString(); + byte[] getRequestId() { + return ThreadContext.getContext().getOrDefault(REQUEST_ID_KEY, UUID.randomUUID().toString()).getBytes(); } } diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/BinaryFileService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFile.java similarity index 71% rename from nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/BinaryFileService.java rename to nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFile.java index 6f7465cea0ab0426c3ddcf8f456a159479905a53..dacbc05680f126479548000d5c7b709595f13855 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/BinaryFileService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * 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 @@ -21,16 +21,17 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.nachrichten.postfach; +package de.ozgcloud.nachrichten.file; -import java.io.InputStream; +import lombok.Builder; +import lombok.Getter; -import com.mongodb.client.gridfs.model.GridFSFile; +@Builder(toBuilder = true) +@Getter +public class AttachmentFile { -//Temporally replacement for using GRPC Api -public interface BinaryFileService { + private String name; + private String contentType; + private String vorgangId; - InputStream getUploadedFileStream(FileId fileId); - - GridFSFile getFile(FileId fileId); } diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFileMapper.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFileMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..6ccc7ebe01892bc8bb3b158fc3ea5c9da8ded8f2 --- /dev/null +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFileMapper.java @@ -0,0 +1,47 @@ +/* + * 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.nachrichten.file; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; +import org.springframework.stereotype.Component; + +import de.ozgcloud.apilib.file.OzgCloudFile; +import de.ozgcloud.apilib.file.OzgCloudUploadFile; +import de.ozgcloud.nachrichten.NachrichtenManagerConfiguration; + +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.WARN) +@AnnotateWith(value = Component.class, elements = @AnnotateWith.Element(strings = NachrichtenManagerConfiguration.ATTACHMENT_FILE_MAPPER_NAME)) +interface AttachmentFileMapper { + + @Mapping(target = "vorgangId", ignore = true) + AttachmentFile fromOzgCloudFile(OzgCloudFile ozgCloudFile); + + @Mapping(target = "fileName", source = "name") + @Mapping(target = "fieldName", constant = AttachmentFileService.ATTACHMENT_NAME) + OzgCloudUploadFile toOzgCloudUploadFile(AttachmentFile attachmentFile); +} diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFileService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFileService.java new file mode 100644 index 0000000000000000000000000000000000000000..6fc1895762c6ad4c3305bed30e3cb9d84853b217 --- /dev/null +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/file/AttachmentFileService.java @@ -0,0 +1,140 @@ +/* + * 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.nachrichten.file; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.URLConnection; +import java.util.Base64; +import java.util.Optional; + +import jakarta.activation.MimetypesFileTypeMap; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.task.TaskExecutor; +import org.springframework.stereotype.Service; + +import de.ozgcloud.apilib.file.OzgCloudFileService; +import de.ozgcloud.apilib.vorgang.OzgCloudFileIdMapper; +import de.ozgcloud.common.errorhandling.TechnicalException; +import de.ozgcloud.nachrichten.NachrichtenManagerConfiguration; +import de.ozgcloud.nachrichten.postfach.FileId; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Service(NachrichtenManagerConfiguration.ATTACHMENT_FILE_SERVICE_NAME) // NOSONAR +@RequiredArgsConstructor +@Log4j2 +public class AttachmentFileService { + + private static final int BUFFER_SIZE = 4 * 1024; + static final String ATTACHMENT_NAME = "PostfachAttachment"; + + @Qualifier(NachrichtenManagerConfiguration.OZG_CLOUD_FILE_SERVICE_NAME) // NOSONAR + private final OzgCloudFileService ozgCloudFileService; + private final OzgCloudFileIdMapper fileIdMapper; + @Qualifier(NachrichtenManagerConfiguration.ATTACHMENT_FILE_MAPPER_NAME) // NOSONAR + private final AttachmentFileMapper attachmentFileMapper; + + private final TaskExecutor taskExecutor; + + private final MimetypesFileTypeMap mimetypesFileTypeMap; + + public AttachmentFile getFile(FileId fileId) { + var ozgCloudFile = ozgCloudFileService.getFile(fileIdMapper.toFileId(fileId.toString())); + return attachmentFileMapper.fromOzgCloudFile(ozgCloudFile); + } + + public InputStream getFileContent(FileId fileId) { + var inputStream = createInputStream(); + writeFileContent(fileId.toString(), connectToOutputStream(inputStream)); + return inputStream; + } + + PipedInputStream createInputStream() { + return new PipedInputStream(BUFFER_SIZE); + } + + OutputStream connectToOutputStream(PipedInputStream inputStream) { + try { + return new PipedOutputStream(inputStream); + } catch (IOException e) { + throw new TechnicalException("Cannot read file content", e); + } + } + + void writeFileContent(String fileId, OutputStream outputStream) { + taskExecutor.execute(() -> { + ozgCloudFileService.writeFileDataToStream(fileIdMapper.toFileId(fileId), outputStream); + IOUtils.closeQuietly(outputStream, e -> LOG.warn("Cannot close output stream", e)); + }); + } + + public String uploadAttachmentFile(AttachmentFile attachmentFile, String fileContent) { + var decodedContent = Base64.getDecoder().decode(fileContent); + var ozgCloudFileId = ozgCloudFileService.uploadFile( + attachmentFileMapper.toOzgCloudUploadFile(addContentTypeIfMissing(attachmentFile, decodedContent)), + toInputStream(decodedContent)); + return ozgCloudFileId.toString(); + } + + AttachmentFile addContentTypeIfMissing(AttachmentFile attachmentFile, byte[] fileContent) { + if (StringUtils.isBlank(attachmentFile.getContentType())) { + return attachmentFile.toBuilder().contentType(getContentType(attachmentFile, fileContent)).build(); + } + return attachmentFile; + } + + String getContentType(AttachmentFile attachmentFile, byte[] fileContent) { + return getTypeByFileName(attachmentFile).or(() -> getTypeByContent(fileContent)).orElseGet(() -> getByMimeTypes(attachmentFile)); + } + + Optional<String> getTypeByFileName(AttachmentFile attachmentFile) { + var contentType = URLConnection.getFileNameMap().getContentTypeFor(attachmentFile.getName()); + return Optional.ofNullable(contentType); + } + + Optional<String> getTypeByContent(byte[] fileContent) { + try (var contentStream = toInputStream(fileContent)) { + return Optional.ofNullable(URLConnection.guessContentTypeFromStream(contentStream)); + } catch (IOException e) { + LOG.warn("IO-Exception while guessing content type", e); + } + return Optional.empty(); + } + + String getByMimeTypes(AttachmentFile attachmentFile) { + return mimetypesFileTypeMap.getContentType(attachmentFile.getName()); + } + + InputStream toInputStream(byte[] content) { + return new ByteArrayInputStream(content); + } +} diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtService.java index 813c1732414b2cfde6aee08b8bea55f1e4a49afe..d21051cb54f7afdd2aeda4422a5cd1c482e4cc76 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtService.java @@ -27,6 +27,8 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Stream; +import de.ozgcloud.nachrichten.file.AttachmentFile; + //Temporally replacement for usign GRPC Api public interface PersistPostfachNachrichtService { diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImpl.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImpl.java index 154404a4d8c7fb71f22e51016e004f8f63a0f5d6..cacd0ccc3fdfa334e5cdd7567484b7268961697f 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImpl.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImpl.java @@ -42,6 +42,7 @@ import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; import de.ozgcloud.nachrichten.NachrichtenManagerConfiguration; import de.ozgcloud.nachrichten.attributes.ClientAttributeService; import de.ozgcloud.nachrichten.common.vorgang.VorgangService; +import de.ozgcloud.nachrichten.file.AttachmentFile; import de.ozgcloud.nachrichten.postfach.PostfachNachricht.Direction; @Service diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentService.java index 0110fcc97ad21678c6c98d0871ff129feef6c52a..d2d27121106bfcebd58a5602ae4c4c37f13c0f89 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentService.java @@ -24,45 +24,32 @@ package de.ozgcloud.nachrichten.postfach.bayernid; import java.io.InputStream; -import java.util.Optional; -import org.bson.Document; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; -import com.mongodb.client.gridfs.model.GridFSFile; - -import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.nachrichten.postfach.BinaryFileService; +import de.ozgcloud.nachrichten.file.AttachmentFile; +import de.ozgcloud.nachrichten.file.AttachmentFileService; import de.ozgcloud.nachrichten.postfach.FileId; +import lombok.RequiredArgsConstructor; @Service @ConditionalOnProperty(prefix = "ozgcloud.bayernid", name = { "enabled" }) +@RequiredArgsConstructor public class BayernIdAttachmentService { - static final String NAME_KEY = "name"; - static final String CONTENT_TYPE_KEY = "contentType"; - - @Autowired - private BinaryFileService binaryFileService; + private final AttachmentFileService attachmentFileService; public BayernIdAttachment getMessageAttachment(FileId fileId) { - return Optional.ofNullable(binaryFileService.getFile(fileId)) - .map(GridFSFile::getMetadata) - .map(metadata -> buildBayernIdAttachment(metadata, getAttachmentContentStream(fileId))) - .orElseThrow(() -> new TechnicalException("Can not find attachment with id " + fileId)); + return buildBayernIdAttachment(attachmentFileService.getFile(fileId), attachmentFileService.getFileContent(fileId)); } - BayernIdAttachment buildBayernIdAttachment(Document metadata, InputStream attachmentContent) { + BayernIdAttachment buildBayernIdAttachment(AttachmentFile attachmentFile, InputStream attachmentContent) { return BayernIdAttachment.builder() - .fileName(metadata.getString(NAME_KEY)) - .contentType(metadata.getString(CONTENT_TYPE_KEY)) + .fileName(attachmentFile.getName()) + .contentType(attachmentFile.getContentType()) .content(attachmentContent) .build(); } - InputStream getAttachmentContentStream(FileId fileId) { - return binaryFileService.getUploadedFileStream(fileId); - } } diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentService.java index c6c2170130d3064b96fcd183ac141eac0f93e890..47dd7b5c7815121bd4b762ed0ae776dd0bd1967e 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentService.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentService.java @@ -23,66 +23,60 @@ */ package de.ozgcloud.nachrichten.postfach.osi; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; import java.util.Base64; import org.apache.commons.io.IOUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.nachrichten.postfach.AttachmentFile; -import de.ozgcloud.nachrichten.postfach.BinaryFileService; +import de.ozgcloud.nachrichten.file.AttachmentFile; +import de.ozgcloud.nachrichten.file.AttachmentFileService; import de.ozgcloud.nachrichten.postfach.FileId; -import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService; -import lombok.val; +import lombok.RequiredArgsConstructor; @Service +@RequiredArgsConstructor public class MessageAttachmentService { - @Autowired - private PersistPostfachNachrichtService persistPostfachNachrichtService; - - @Autowired - private BinaryFileService binaryFileService; + private final AttachmentFileService attachmentFileService; public MessageAttachment getMessageAttachment(FileId fileId) { + return buildMessageAttachment(attachmentFileService.getFile(fileId), getAttachmentContent(fileId)); + } + + MessageAttachment buildMessageAttachment(AttachmentFile attachmentFile, String attachmentContent) { + return MessageAttachment.builder() + .fileName(attachmentFile.getName()) + .content(attachmentContent) + .build(); + } + + String getAttachmentContent(FileId fileId) { + return encodeAttachmentContent(getContent(fileId)); + } + + byte[] getContent(FileId fileId) { try { - val metadata = binaryFileService.getFile(fileId).getMetadata(); - return MessageAttachment.builder() - .fileName(metadata.getString("name")) - .content(getAttachmentContent(fileId)) - .build(); + return IOUtils.toByteArray(attachmentFileService.getFileContent(fileId)); } catch (IOException e) { - throw new TechnicalException(e.getMessage(), e); + throw new TechnicalException("Cannot get file content", e); } } - String getAttachmentContent(FileId fileId) throws IOException { - ByteArrayOutputStream contentStream = new ByteArrayOutputStream(); - IOUtils.copy(getAttachmentContentStream(fileId), contentStream); - return encodeAttachmentContent(contentStream.toByteArray()); - } - - private String encodeAttachmentContent(byte[] content) { + String encodeAttachmentContent(byte[] content) { return new String(Base64.getEncoder().encode(content)); } public String persistAttachment(String vorgangId, MessageAttachment attachment) { - return persistPostfachNachrichtService.persistAttachment(vorgangId, mapAttachmentFile(attachment)); + return attachmentFileService.uploadAttachmentFile(buildAttachmentFile(vorgangId, attachment), attachment.getContent()); } - AttachmentFile mapAttachmentFile(MessageAttachment attachment) { + AttachmentFile buildAttachmentFile(String vorgangId, MessageAttachment attachment) { return AttachmentFile.builder() .name(attachment.getFileName()) - .content(() -> IOUtils.toInputStream(attachment.getContent(), Charset.defaultCharset())).build(); - } - - InputStream getAttachmentContentStream(FileId fileId) { - return binaryFileService.getUploadedFileStream(fileId); + .vorgangId(vorgangId) + .build(); } } diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProviderTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProviderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7ac62d8ed944b5c1565472216f3ccccc909f6a5d --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerCallContextProviderTest.java @@ -0,0 +1,79 @@ +/* + * 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.nachrichten; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.ozgcloud.apilib.user.OzgCloudUserId; +import de.ozgcloud.apilib.vorgang.OzgCloudUserIdMapper; +import de.ozgcloud.nachrichten.common.grpc.NachrichtenCallContextAttachingInterceptor; + +class NachrichtenManagerCallContextProviderTest { + + @InjectMocks + private NachrichtenManagerCallContextProvider callContextProvider; + + @Mock + private OzgCloudUserIdMapper userIdMapper; + + @Nested + class TestProvideContext { + + @BeforeEach + void init() { + when(userIdMapper.toUserId(anyString())).thenReturn( + OzgCloudUserId.from(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_SENDER_USER_ID)); + } + + @Test + void shouldSetClientName() { + var result = callContextProvider.provideContext(); + + assertThat(result.getClientName()).isEqualTo(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_CLIENT_NAME); + } + + @Test + void shouldCallUserIdMapper() { + callContextProvider.provideContext(); + + verify(userIdMapper).toUserId(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_SENDER_USER_ID); + } + + @Test + void shouldSetUserId() { + var result = callContextProvider.provideContext(); + + assertThat(result.getUserId()).hasToString(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_SENDER_USER_ID); + } + } + +} \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerTestApplication.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerTestApplication.java index 489b714cbb2abbcc18c4167fd1446781565734ce..6403b80171ca2e8f10953186048b44b90ca20ebb 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerTestApplication.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/NachrichtenManagerTestApplication.java @@ -1,13 +1,16 @@ package de.ozgcloud.nachrichten; +import org.mapstruct.factory.Mappers; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import de.ozgcloud.apilib.common.command.OzgCloudCommandService; import de.ozgcloud.apilib.file.OzgCloudFileService; -import de.ozgcloud.nachrichten.postfach.BinaryFileService; +import de.ozgcloud.apilib.vorgang.OzgCloudFileIdMapper; +import de.ozgcloud.apilib.vorgang.OzgCloudUserIdMapper; import de.ozgcloud.nachrichten.postfach.muk.MukPostfachConfiguration; @SpringBootApplication @@ -22,7 +25,15 @@ public class NachrichtenManagerTestApplication { @MockBean @Qualifier(MukPostfachConfiguration.OZG_CLOUD_FILE_SERVICE_NAME) private OzgCloudFileService mukOzgCloudFileServices; - @MockBean - private BinaryFileService binaryFileService; + + @Bean + OzgCloudFileIdMapper ozgCloudFileIdMapper() { + return Mappers.getMapper(OzgCloudFileIdMapper.class); + } + + @Bean + OzgCloudUserIdMapper ozgCloudUserIdMapper() { + return Mappers.getMapper(OzgCloudUserIdMapper.class); + } } diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptorTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8deb34be0350b52067847f983e13063f20745cbb --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/common/grpc/NachrichtenCallContextAttachingInterceptorTest.java @@ -0,0 +1,176 @@ +/* + * 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.nachrichten.common.grpc; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Map; +import java.util.UUID; + +import org.apache.logging.log4j.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import de.ozgcloud.common.grpc.GrpcUtil; +import io.grpc.ClientCall; +import io.grpc.Metadata; + +class NachrichtenCallContextAttachingInterceptorTest { + + @InjectMocks + private NachrichtenCallContextAttachingInterceptor interceptor; + + @Mock + private ClientCall<RequestTest, ResponseTest> delegateCall; + @Mock + private ClientCall.Listener<ResponseTest> responseTestListener; + @Mock + private Metadata requestMetadata; + + private NachrichtenCallContextAttachingInterceptor.CallContextAttachingClientCall<RequestTest, ResponseTest> clientCall; + + @BeforeEach + void init() { + clientCall = spy(interceptor.new CallContextAttachingClientCall<>(delegateCall)); + } + + @Nested + class TestStart { + + @Mock + private Metadata customMetadata; + + @BeforeEach + void init() { + doReturn(customMetadata).when(clientCall).buildCallContextMetadata(); + } + + @Test + void shouldCallBuildCallContextMetadata() { + clientCall.start(responseTestListener, requestMetadata); + + verify(clientCall).buildCallContextMetadata(); + } + + @Test + void shouldMergeMetadata() { + clientCall.start(responseTestListener, requestMetadata); + + verify(requestMetadata).merge(customMetadata); + } + } + + @Nested + class TestBuildCallContextMetadata { + + @Test + void shouldSetUserId() { + var result = clientCall.buildCallContextMetadata(); + + assertThat(result.get(GrpcUtil.HEADER_KEY_USER_ID)) + .isEqualTo(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_SENDER_USER_ID.getBytes()); + } + + @Test + void shouldSetClientName() { + var result = clientCall.buildCallContextMetadata(); + + assertThat(result.get(GrpcUtil.HEADER_KEY_CLIENT_NAME)) + .isEqualTo(NachrichtenCallContextAttachingInterceptor.NACHRICHTEN_MANAGER_CLIENT_NAME.getBytes()); + } + + @Test + void shouldCallGetrequestId() { + clientCall.buildCallContextMetadata(); + + verify(clientCall).getRequestId(); + } + + @Test + void shouldSetRequestId() { + var requestId = UUID.randomUUID().toString().getBytes(); + doReturn(requestId).when(clientCall).getRequestId(); + + var result = clientCall.buildCallContextMetadata(); + + assertThat(result.get(GrpcUtil.HEADER_KEY_REQUEST_ID)).isEqualTo(requestId); + } + } + + @Nested + class TestGetRequestId { + + private MockedStatic<ThreadContext> threadContextMocked; + + @BeforeEach + void init() { + threadContextMocked = Mockito.mockStatic(ThreadContext.class); + } + + @AfterEach + void close() { + threadContextMocked.close(); + } + + @Test + void shouldCallGetContext() { + clientCall.getRequestId(); + + threadContextMocked.verify(ThreadContext::getContext); + } + + @Test + void shouldReturnRequestIdFromThreadContext() { + var requestId = UUID.randomUUID().toString(); + + threadContextMocked.when(ThreadContext::getContext) + .thenReturn(Map.of(NachrichtenCallContextAttachingInterceptor.REQUEST_ID_KEY, requestId)); + + var result = clientCall.getRequestId(); + + assertThat(result).isEqualTo(requestId.getBytes()); + } + + @Test + void shouldReturnNewRequestId() { + var result = clientCall.getRequestId(); + + assertThat(result).isNotNull(); + } + } + + private record RequestTest() { + } + + private record ResponseTest() { + } +} \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileMapperTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..68dd898d3e247acbbb0176ca1debf15eacf9ca4e --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileMapperTest.java @@ -0,0 +1,75 @@ +/* + * 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.nachrichten.file; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import de.ozgcloud.apilib.file.OzgCloudFile; +import de.ozgcloud.apilib.file.OzgCloudFileTestFactory; +import de.ozgcloud.apilib.file.OzgCloudUploadFile; +import de.ozgcloud.apilib.file.OzgCloudUploadFileTestFactory; + +class AttachmentFileMapperTest { + + private final AttachmentFileMapper mapper = Mappers.getMapper(AttachmentFileMapper.class); + + @Nested + class TestFromOzgCloudFile { + + private static final OzgCloudFile OZG_CLOUD_FILE = OzgCloudFileTestFactory.createBuilder() + .contentType(AttachmentFileTestFactory.CONTENT_TYPE) + .name(AttachmentFileTestFactory.NAME) + .build(); + + @Test + void shouldMapFromOzgCloudFile() { + + var result = mapper.fromOzgCloudFile(OZG_CLOUD_FILE); + + assertThat(result).usingRecursiveComparison().ignoringFields("vorgangId").isEqualTo(AttachmentFileTestFactory.create()); + } + } + + @Nested + class TestToOzgCloudUploadFile { + + private static final OzgCloudUploadFile OZG_CLOUD_UPLOAD_FILE = OzgCloudUploadFileTestFactory.createBuilder() + .fieldName(AttachmentFileService.ATTACHMENT_NAME) + .fileName(AttachmentFileTestFactory.NAME) + .contentType(AttachmentFileTestFactory.CONTENT_TYPE) + .vorgangId(AttachmentFileTestFactory.VORGANG_ID) + .build(); + + @Test + void shouldMapToOzgCloudUploadFile() { + var result = mapper.toOzgCloudUploadFile(AttachmentFileTestFactory.create()); + + assertThat(result).usingRecursiveComparison().isEqualTo(OZG_CLOUD_UPLOAD_FILE); + } + } +} \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a5936b2e23f732afcd1ad73e2b23722d04c09016 --- /dev/null +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileServiceTest.java @@ -0,0 +1,574 @@ +/* + * 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.nachrichten.file; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.net.FileNameMap; +import java.net.URLConnection; +import java.util.Base64; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; + +import jakarta.activation.MimetypesFileTypeMap; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Spy; +import org.springframework.core.task.TaskExecutor; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.apilib.file.OzgCloudFile; +import de.ozgcloud.apilib.file.OzgCloudFileId; +import de.ozgcloud.apilib.file.OzgCloudFileService; +import de.ozgcloud.apilib.file.OzgCloudUploadFile; +import de.ozgcloud.apilib.vorgang.OzgCloudFileIdMapper; +import de.ozgcloud.common.test.TestUtils; +import de.ozgcloud.nachrichten.postfach.FileId; +import lombok.SneakyThrows; + +class AttachmentFileServiceTest { + + private static final String FILE_ID = UUID.randomUUID().toString(); + private static final byte[] FILE_CONTENT = LoremIpsum.getInstance().getWords(1).getBytes(); + + @Spy + @InjectMocks + private AttachmentFileService service; + + @Mock + private OzgCloudFileService ozgCloudFileService; + @Mock + private OzgCloudFileIdMapper fileIdMapper; + @Mock + private AttachmentFileMapper attachmentFileMapper; + @Mock + private TaskExecutor taskExecutor; + @Mock + private MimetypesFileTypeMap mimetypesFileTypeMap; + + @Nested + class TestGetFile { + + @Mock + private OzgCloudFile ozgCloudFile; + @Mock + private AttachmentFile attachmentFile; + + @BeforeEach + void init() { + when(fileIdMapper.toFileId(any())).thenReturn(OzgCloudFileId.from(FILE_ID)); + when(attachmentFileMapper.fromOzgCloudFile(any())).thenReturn(attachmentFile); + when(ozgCloudFileService.getFile(any())).thenReturn(ozgCloudFile); + } + + @Test + void shouldCallToFileId() { + getFile(); + + verify(fileIdMapper).toFileId(FILE_ID); + } + + @Test + void shouldCallGetFile() { + getFile(); + + verify(ozgCloudFileService).getFile(OzgCloudFileId.from(FILE_ID)); + } + + @Test + void shouldCallFromOzgCloudFile() { + getFile(); + + verify(attachmentFileMapper).fromOzgCloudFile(ozgCloudFile); + } + + @Test + void shouldReturnAttachmentFile() { + var result = getFile(); + + assertThat(result).isEqualTo(attachmentFile); + } + + private AttachmentFile getFile() { + return service.getFile(FileId.from(FILE_ID)); + } + } + + @Nested + class TestGetFileContent { + + @Mock + private PipedInputStream inputStream; + @Mock + private OutputStream outputStream; + + @BeforeEach + void init() { + doReturn(inputStream).when(service).createInputStream(); + doReturn(outputStream).when(service).connectToOutputStream(any()); + doNothing().when(service).writeFileContent(any(), any()); + } + + @Test + void shouldCallCreateInputStream() { + getFileContent(); + + verify(service).createInputStream(); + } + + @Test + void shouldCallConnectToOutputStream() { + getFileContent(); + + verify(service).connectToOutputStream(inputStream); + } + + @Test + void shoulcCallWriteFileContent() { + getFileContent(); + + verify(service).writeFileContent(FILE_ID, outputStream); + } + + @Test + void shouldReturnContentStream() { + var result = getFileContent(); + + assertThat(result).isEqualTo(inputStream); + } + + private InputStream getFileContent() { + return service.getFileContent(FileId.from(FILE_ID)); + } + } + + @Nested + class TestConnectToOutputStream { + + @SneakyThrows + @Test + void shouldReturnOutputStream() { + try (var inputStream = new PipedInputStream()) { + + var result = service.connectToOutputStream(inputStream); + result.write(FILE_CONTENT); + result.close(); + + assertThat(inputStream).hasBinaryContent(FILE_CONTENT); + } + } + } + + @Nested + class TestWriteFileContent { + + private static final OzgCloudFileId OZG_CLOUD_FILE_ID = OzgCloudFileId.from(FILE_ID); + + @Mock + private OutputStream outputStream; + + @Captor + private ArgumentCaptor<Runnable> runnableCaptor; + + @BeforeEach + void init() { + when(fileIdMapper.toFileId(any())).thenReturn(OZG_CLOUD_FILE_ID); + } + + @Test + void shouldCallToFileId() { + writeFileContent(); + + verify(fileIdMapper).toFileId(FILE_ID); + } + + @Test + void shouldCallWriteFileDataToStream() { + writeFileContent(); + + verify(ozgCloudFileService).writeFileDataToStream(OZG_CLOUD_FILE_ID, outputStream); + } + + @Test + void shouldCallCloseOutputStream() { + try (var ioUtilsMock = mockStatic(IOUtils.class)) { + writeFileContent(); + + ioUtilsMock.verify(() -> IOUtils.closeQuietly(eq(outputStream), any(Consumer.class))); + } + } + + private void writeFileContent() { + service.writeFileContent(FILE_ID, outputStream); + verify(taskExecutor).execute(runnableCaptor.capture()); + runnableCaptor.getValue().run(); + } + + } + + @Nested + class TestUploadAttachmentFile { + + private static final AttachmentFile ATTACHMENT_FILE = AttachmentFileTestFactory.create(); + private static final String FILE_CONTENT_STR = new String(FILE_CONTENT); + + @Mock + private AttachmentFile enhancedAttachmentFile; + @Mock + private OzgCloudUploadFile ozgCloudUploadFile; + @Mock + private InputStream contentStream; + @Mock + private Base64.Decoder decoder; + + private MockedStatic<Base64> base64Mock; + + @BeforeEach + void init() { + when(ozgCloudFileService.uploadFile(any(), any())).thenReturn(OzgCloudFileId.from(FILE_ID)); + when(attachmentFileMapper.toOzgCloudUploadFile(any())).thenReturn(ozgCloudUploadFile); + doReturn(enhancedAttachmentFile).when(service).addContentTypeIfMissing(any(), any()); + doReturn(contentStream).when(service).toInputStream(any()); + base64Mock = mockStatic(Base64.class); + base64Mock.when(Base64::getDecoder).thenReturn(decoder); + when(decoder.decode(anyString())).thenReturn(FILE_CONTENT); + } + + @AfterEach + void close() { + base64Mock.close(); + } + + @Test + void shouldCallGetDecoder() { + uploadAttachmentFile(); + + base64Mock.verify(Base64::getDecoder); + } + + @Test + void shouldCallDecode() { + uploadAttachmentFile(); + + verify(decoder).decode(FILE_CONTENT_STR); + } + + @Test + void shouldCallAddContentTypeIfMissing() { + uploadAttachmentFile(); + + verify(service).addContentTypeIfMissing(ATTACHMENT_FILE, FILE_CONTENT); + } + + @Test + void shouldCallToOzgCloudUploadFile() { + uploadAttachmentFile(); + + verify(attachmentFileMapper).toOzgCloudUploadFile(enhancedAttachmentFile); + } + + @Test + void shouldCallToDecodedInputStream() { + uploadAttachmentFile(); + + verify(service).toInputStream(FILE_CONTENT); + } + + @Test + void shouldCallUploadFile() { + uploadAttachmentFile(); + + verify(ozgCloudFileService).uploadFile(ozgCloudUploadFile, contentStream); + } + + @Test + void shouldReturnFileId() { + var result = uploadAttachmentFile(); + + assertThat(result).isEqualTo(FILE_ID); + } + + private String uploadAttachmentFile() { + return service.uploadAttachmentFile(ATTACHMENT_FILE, FILE_CONTENT_STR); + } + } + + @Nested + class TestAddContentTypeIfMissing { + + private AttachmentFile attachmentFile; + + @Nested + class TestNoContentType { + + @BeforeEach + void init() { + attachmentFile = AttachmentFileTestFactory.createBuilder().contentType(null).build(); + doReturn(AttachmentFileTestFactory.CONTENT_TYPE).when(service).getContentType(any(), any()); + } + + @Test + void shouldCallGetContentType() { + addContentTypeIfMissing(); + + verify(service).getContentType(attachmentFile, FILE_CONTENT); + } + + @Test + void shouldReturnAttachmentFile() { + var result = addContentTypeIfMissing(); + + assertThat(result).usingRecursiveComparison().isEqualTo(AttachmentFileTestFactory.create()); + } + } + + @Test + void shouldReturnAttachmentFile() { + attachmentFile = AttachmentFileTestFactory.create(); + + var result = addContentTypeIfMissing(); + + assertThat(result).isSameAs(attachmentFile); + } + + private AttachmentFile addContentTypeIfMissing() { + return service.addContentTypeIfMissing(attachmentFile, FILE_CONTENT); + } + } + + @Nested + class TestGetContentType { + + private static final AttachmentFile ATTACHMENT_FILE = AttachmentFileTestFactory.create(); + + @Nested + class TestGetByFileName { + + @BeforeEach + void init() { + doReturn(Optional.of(AttachmentFileTestFactory.CONTENT_TYPE)).when(service).getTypeByFileName(any()); + } + + @Test + void shouldCallGetTypeByFileName() { + getContentType(); + + verify(service).getTypeByFileName(ATTACHMENT_FILE); + } + + @Test + void shouldReturnContentType() { + var result = getContentType(); + + assertThat(result).isEqualTo(AttachmentFileTestFactory.CONTENT_TYPE); + } + } + + @Nested + class TestTypeByContent { + + @BeforeEach + void init() { + doReturn(Optional.empty()).when(service).getTypeByFileName(any()); + doReturn(Optional.of(AttachmentFileTestFactory.CONTENT_TYPE)).when(service).getTypeByContent(any()); + } + + @Test + void shouldCallGetTypeByContent() { + getContentType(); + + verify(service).getTypeByContent(FILE_CONTENT); + } + + @Test + void shouldReturnContentType() { + var result = getContentType(); + + assertThat(result).isEqualTo(AttachmentFileTestFactory.CONTENT_TYPE); + } + + } + + @Nested + class TestByMimeTypes { + + @BeforeEach + void init() { + doReturn(Optional.empty()).when(service).getTypeByFileName(any()); + doReturn(Optional.empty()).when(service).getTypeByContent(any()); + doReturn(AttachmentFileTestFactory.CONTENT_TYPE).when(service).getByMimeTypes(any()); + } + + @Test + void shouldCallGetByMimeTypes() { + getContentType(); + + verify(service).getByMimeTypes(ATTACHMENT_FILE); + } + + @Test + void shouldReturnContentType() { + var result = getContentType(); + + assertThat(result).isEqualTo(AttachmentFileTestFactory.CONTENT_TYPE); + } + } + + private String getContentType() { + return service.getContentType(ATTACHMENT_FILE, FILE_CONTENT); + } + } + + @Nested + class TestGetTypeByFileName { + + private static final AttachmentFile ATTACHMENT_FILE = AttachmentFileTestFactory.create(); + + @Mock + private FileNameMap fileNameMap; + private MockedStatic<URLConnection> urlConnectionMock; + + @BeforeEach + void init() { + urlConnectionMock = mockStatic(URLConnection.class); + urlConnectionMock.when(URLConnection::getFileNameMap).thenReturn(fileNameMap); + } + + @AfterEach + void close() { + urlConnectionMock.close(); + } + + @Test + void shouldCallGetContentTypeFor() { + getTypeByFileName(); + + verify(fileNameMap).getContentTypeFor(AttachmentFileTestFactory.NAME); + } + + @Test + void shouldReturnContentType() { + when(fileNameMap.getContentTypeFor(any())).thenReturn(AttachmentFileTestFactory.CONTENT_TYPE); + + var result = getTypeByFileName(); + + assertThat(result).contains(AttachmentFileTestFactory.CONTENT_TYPE); + } + + @Test + void shouldReturnEmpty() { + var result = getTypeByFileName(); + + assertThat(result).isEmpty(); + } + + private Optional<String> getTypeByFileName() { + return service.getTypeByFileName(ATTACHMENT_FILE); + } + } + + @Nested + class TestGetTypeByContent { + + @SneakyThrows + @Test + void shouldReturnContentType() { + var content = TestUtils.loadFile("BspQuittung.xml").readAllBytes(); + + var result = service.getTypeByContent(content); + + assertThat(result).contains("application/xml"); + } + + @Test + void shouldReturnEmpty() { + var result = service.getTypeByContent(FILE_CONTENT); + + assertThat(result).isEmpty(); + } + + @Test + void shouldNotThrowException() { + try (var urlConnectionMock = mockStatic(URLConnection.class)) { + urlConnectionMock.when(() -> URLConnection.guessContentTypeFromStream(any())).thenThrow(IOException.class); + + var result = service.getTypeByContent(FILE_CONTENT); + + assertThat(result).isEmpty(); + } + + } + + @Nested + class TestGetByMimeTypes { + + @Test + void shouldCallGetContentType() { + getByMimeTypes(); + + verify(mimetypesFileTypeMap).getContentType(AttachmentFileTestFactory.NAME); + } + + @Test + void shouldReturnContentType() { + when(mimetypesFileTypeMap.getContentType(anyString())).thenReturn(AttachmentFileTestFactory.CONTENT_TYPE); + + var result = getByMimeTypes(); + + assertThat(result).isEqualTo(AttachmentFileTestFactory.CONTENT_TYPE); + } + + private String getByMimeTypes() { + return service.getByMimeTypes(AttachmentFileTestFactory.create()); + } + } + } + + @Nested + class TestToInputStream { + + @Test + void shouldReturnInputStream() { + var result = service.toInputStream(FILE_CONTENT); + + assertThat(result).hasBinaryContent(FILE_CONTENT); + } + } +} \ No newline at end of file diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachmentFile.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileTestFactory.java similarity index 55% rename from nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachmentFile.java rename to nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileTestFactory.java index 0133636869be97afda3d6b5f1475c5773fe5a720..fc2a3ac29ea6a095b8108e882e0ad028293e9b14 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/postfach/AttachmentFile.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/file/AttachmentFileTestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den + * 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 @@ -21,33 +21,26 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.nachrichten.postfach; +package de.ozgcloud.nachrichten.file; -import static java.util.Objects.*; +import com.thedeanda.lorem.LoremIpsum; -import java.io.InputStream; -import java.util.function.Supplier; +import de.ozgcloud.nachrichten.common.vorgang.VorgangTestFactory; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; +public class AttachmentFileTestFactory { -@Builder -@Getter -@ToString -public class AttachmentFile { - private String name; - private String contentType; - @Getter(AccessLevel.NONE) - @ToString.Exclude - private Supplier<InputStream> content; + public static final String NAME = LoremIpsum.getInstance().getWords(1); + public static final String CONTENT_TYPE = LoremIpsum.getInstance().getWords(1); + public static final String VORGANG_ID = VorgangTestFactory.ID; - public InputStream getContent() { - if (isNull(content)) { - return InputStream.nullInputStream(); - } - return content.get(); + public static AttachmentFile create() { + return createBuilder().build(); } + public static AttachmentFile.AttachmentFileBuilder createBuilder() { + return AttachmentFile.builder() + .name(NAME) + .contentType(CONTENT_TYPE) + .vorgangId(VORGANG_ID); + } } diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentServiceTest.java index 5db43d5340835019620df44b1c52465a50dd793d..67be8d6b18244c0926b10e1f7d3bc07955b3dddc 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentServiceTest.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentServiceTest.java @@ -25,26 +25,22 @@ package de.ozgcloud.nachrichten.postfach.bayernid; import static de.ozgcloud.nachrichten.postfach.bayernid.BayernIdAttachmentTestFactory.*; 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.ByteArrayInputStream; import java.io.InputStream; -import org.bson.Document; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import com.mongodb.client.gridfs.model.GridFSFile; - -import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.nachrichten.postfach.BinaryFileService; +import de.ozgcloud.nachrichten.file.AttachmentFile; +import de.ozgcloud.nachrichten.file.AttachmentFileService; +import de.ozgcloud.nachrichten.file.AttachmentFileTestFactory; import de.ozgcloud.nachrichten.postfach.FileId; class BayernIdAttachmentServiceTest { @@ -53,74 +49,54 @@ class BayernIdAttachmentServiceTest { @InjectMocks @Spy - private BayernIdAttachmentService bayernIdAttachmentService; - - @Mock - private BinaryFileService fileService; - - @Mock - private GridFSFile gridFsfile; + private BayernIdAttachmentService service; @Mock - private Document metadata; + private AttachmentFileService attachmentFileService; @Nested - class TestLoadingAttachment { + class TestGetMessageAttachment { + + @Mock + private AttachmentFile attachmentFile; + @Mock + private InputStream fileContent; + @Mock + private BayernIdAttachment bayernIdAttachment; @BeforeEach void init() { - when(gridFsfile.getMetadata()).thenReturn(metadata); - when(fileService.getFile(any())).thenReturn(gridFsfile); + when(attachmentFileService.getFile(any())).thenReturn(attachmentFile); + when(attachmentFileService.getFileContent(any())).thenReturn(fileContent); + doReturn(bayernIdAttachment).when(service).buildBayernIdAttachment(any(), any()); } @Test void shouldCallGetFile() { - when(fileService.getUploadedFileStream(any())).thenReturn(new ByteArrayInputStream(CONTENT)); + service.getMessageAttachment(FILE_ID); - bayernIdAttachmentService.getMessageAttachment(FILE_ID); - - verify(fileService).getFile(FILE_ID); + verify(attachmentFileService).getFile(FILE_ID); } @Test - void shouldCallGetAttachmentContent() { - when(fileService.getUploadedFileStream(any())).thenReturn(new ByteArrayInputStream(CONTENT)); - - bayernIdAttachmentService.getMessageAttachment(FILE_ID); + void shouldCallGetFileContent() { + service.getMessageAttachment(FILE_ID); - verify(bayernIdAttachmentService).getMessageAttachment(FILE_ID); + verify(attachmentFileService).getFileContent(FILE_ID); } @Test void shouldCallBuildBayernIdAttachment() { - var contentStream = getContentStream(); - doReturn(contentStream).when(bayernIdAttachmentService).getAttachmentContentStream(any()); + service.getMessageAttachment(FILE_ID); - bayernIdAttachmentService.getMessageAttachment(FILE_ID); - - verify(bayernIdAttachmentService).buildBayernIdAttachment(metadata, contentStream); + verify(service).buildBayernIdAttachment(attachmentFile, fileContent); } - } - - @Nested - class TestLoadAttachmentError { - @Test - @DisplayName("should throw TechnicalException if attachment not found") - void shouldThrowException() { - when(fileService.getFile(any())).thenReturn(null); + void shouldReturnBayernIdAttachment() { + var attachment = service.getMessageAttachment(FILE_ID); - assertThrows(TechnicalException.class, () -> bayernIdAttachmentService.getMessageAttachment(FILE_ID)); - } - - @Test - @DisplayName("should throw TechnicalException if metadata is null") - void shouldThrowExceptionIfNoMetadata() { - when(fileService.getFile(any())).thenReturn(gridFsfile); - when(gridFsfile.getMetadata()).thenReturn(null); - - assertThrows(TechnicalException.class, () -> bayernIdAttachmentService.getMessageAttachment(FILE_ID)); + assertThat(attachment).isSameAs(bayernIdAttachment); } } @@ -129,8 +105,6 @@ class BayernIdAttachmentServiceTest { @BeforeEach void setup() { - when(metadata.getString(BayernIdAttachmentService.NAME_KEY)).thenReturn(BayernIdAttachmentTestFactory.FILENAME); - when(metadata.getString(BayernIdAttachmentService.CONTENT_TYPE_KEY)).thenReturn(BayernIdAttachmentTestFactory.CONTENT_TYPE); } @Test @@ -155,25 +129,7 @@ class BayernIdAttachmentServiceTest { } private BayernIdAttachment buildBayernIdAttachment() { - return bayernIdAttachmentService.buildBayernIdAttachment(metadata, getContentStream()); - } - } - - @Nested - class TestLoadingAttachmentContent { - - @BeforeEach - void init() { - when(fileService.getUploadedFileStream(any())) - .thenReturn(new ByteArrayInputStream(CONTENT)); + return service.buildBayernIdAttachment(AttachmentFileTestFactory.create(), getContentStream()); } - - @Test - void shouldGetInputStream() { - InputStream input = bayernIdAttachmentService.getAttachmentContentStream(FILE_ID); - - assertThat(input).hasSameContentAs(getContentStream()); - } - } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentTestFactory.java index 2fe959b6e55845a65ad5596239816a23945759b3..0ae8abb39ab76a2dc5548865a0ea84ccf61935a3 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentTestFactory.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/bayernid/BayernIdAttachmentTestFactory.java @@ -26,12 +26,13 @@ package de.ozgcloud.nachrichten.postfach.bayernid; import java.io.ByteArrayInputStream; import java.io.InputStream; +import de.ozgcloud.nachrichten.file.AttachmentFileTestFactory; import de.ozgcloud.nachrichten.postfach.bayernid.BayernIdAttachment.BayernIdAttachmentBuilder; public class BayernIdAttachmentTestFactory { - public final static String FILENAME = "test.txt"; - public final static String CONTENT_TYPE = "text/plain"; + public final static String FILENAME = AttachmentFileTestFactory.NAME; + public final static String CONTENT_TYPE = AttachmentFileTestFactory.CONTENT_TYPE; public static final byte[] CONTENT = "test".getBytes(); public static final long SIZE = 4L; diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentServiceTest.java index dc473e132bf0dff050351527495b0c78b211ff09..3a16696cab0b269da392fc7fc372b6323d913890 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentServiceTest.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentServiceTest.java @@ -28,104 +28,217 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; -import java.sql.Date; -import java.time.Instant; -import org.bson.BsonObjectId; -import org.bson.BsonValue; -import org.bson.Document; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; -import com.mongodb.client.gridfs.model.GridFSFile; +import com.thedeanda.lorem.LoremIpsum; -import de.ozgcloud.nachrichten.postfach.BinaryFileService; +import de.ozgcloud.nachrichten.file.AttachmentFile; +import de.ozgcloud.nachrichten.file.AttachmentFileService; +import de.ozgcloud.nachrichten.file.AttachmentFileTestFactory; import de.ozgcloud.nachrichten.postfach.FileId; class MessageAttachmentServiceTest { - private static final FileId FILE_ID = FileId.from("42"); + private static final FileId FILE_ID = FileId.from(LoremIpsum.getInstance().getWords(1)); + + @Spy @InjectMocks - private MessageAttachmentService messageAttachmentService; + private MessageAttachmentService service; @Mock - private BinaryFileService fileService; - - private GridFSFile gridFsfile; + private AttachmentFileService attachmentFileService; @Nested - class TestLoadingAttachment { + class TestGetMessageAttachment { + + @Mock + private AttachmentFile attachmentFile; + @Mock + private MessageAttachment messageAttachment; + @BeforeEach void init() { - Document metadata = new Document(); - metadata.put("name", MessageAttachmentTestFactory.FILENAME); - BsonValue id = new BsonObjectId(); - gridFsfile = new GridFSFile(id, FILE_ID.toString(), 0, 0, Date.from(Instant.now()), metadata); + when(attachmentFileService.getFile(any())).thenReturn(attachmentFile); + doReturn(MessageAttachmentTestFactory.CONTENT).when(service).getAttachmentContent(any()); + doReturn(messageAttachment).when(service).buildMessageAttachment(any(), any()); + } + + @Test + void shouldCallGetFile() { + getMessageAttachment(); + + verify(attachmentFileService).getFile(FILE_ID); + } - when(fileService.getFile(any())).thenReturn(gridFsfile); - when(fileService.getUploadedFileStream(any())) - .thenReturn(new ByteArrayInputStream(MessageAttachmentTestFactory.DECODED_CONTENT.getBytes())); + @Test + void shouldCallGetAttachmentContent() { + getMessageAttachment(); + + verify(service).getAttachmentContent(FILE_ID); } @Test - void shouldHaveAttachmentWithFileName() { - MessageAttachment attachment = messageAttachmentService.getMessageAttachment(FILE_ID); + void shouldCallBuildMessageAttachment() { + getMessageAttachment(); - assertThat(attachment.getFileName()).isEqualTo(MessageAttachmentTestFactory.FILENAME); + verify(service).buildMessageAttachment(attachmentFile, MessageAttachmentTestFactory.CONTENT); } @Test - void shouldHaveAttachmentContent() { - MessageAttachment attachment = messageAttachmentService.getMessageAttachment(FILE_ID); + void shouldReturnMessageAttachment() { + var result = getMessageAttachment(); - assertThat(attachment.getContent()).isEqualTo(MessageAttachmentTestFactory.CONTENT); + assertThat(result).isSameAs(messageAttachment); } + private MessageAttachment getMessageAttachment() { + return service.getMessageAttachment(FILE_ID); + } } @Nested - class TestLoadingAttachmentContent { + class TestBuildMessageAttachment { + + @Test + void shouldBuildMessageAttachment() { + var attachment = buildMessageAttachment(); + + assertThat(attachment).usingRecursiveComparison().isEqualTo(MessageAttachmentTestFactory.create()); + } + + private MessageAttachment buildMessageAttachment() { + return service.buildMessageAttachment(AttachmentFileTestFactory.create(), MessageAttachmentTestFactory.CONTENT); + } + } + + @Nested + class TestGetAttachmentContent { + + private static final byte[] CONTENT = MessageAttachmentTestFactory.CONTENT.getBytes(); + @BeforeEach void init() { - when(fileService.getUploadedFileStream(any())) - .thenReturn(new ByteArrayInputStream(MessageAttachmentTestFactory.DECODED_CONTENT.getBytes())); + doReturn(CONTENT).when(service).getContent(any(FileId.class)); + doReturn(MessageAttachmentTestFactory.CONTENT).when(service).encodeAttachmentContent(any()); + } + + @Test + void shouldCallGetContent() { + service.getAttachmentContent(FILE_ID); + + verify(service).getContent(FILE_ID); } @Test - void shouldGetInputStream() { - InputStream input = messageAttachmentService.getAttachmentContentStream(FILE_ID); + void shouldCallEncodeAttachmentContent() { + service.getAttachmentContent(FILE_ID); - assertThat(input).isNotNull(); + verify(service).encodeAttachmentContent(CONTENT); } @Test - void shouldGetContent() throws IOException { - String input = messageAttachmentService.getAttachmentContent(FILE_ID); + void shouldReturnContent() { + var result = service.getAttachmentContent(FILE_ID); - assertThat(input).isEqualTo(MessageAttachmentTestFactory.CONTENT); + assertThat(result).isEqualTo(MessageAttachmentTestFactory.CONTENT); } } @Nested - class TestMapAttachmentFile { + class TestGetContent { @Test - void shouldMapFileName() { - var attachmentFile = messageAttachmentService.mapAttachmentFile(MessageAttachmentTestFactory.create()); + void shouldCallGetFileContent() { + when(attachmentFileService.getFileContent(any())).thenReturn(new ByteArrayInputStream(MessageAttachmentTestFactory.CONTENT.getBytes())); + + service.getContent(FILE_ID); + + verify(attachmentFileService).getFileContent(FILE_ID); + } + + @Test + void shouldReturnContent() { + when(attachmentFileService.getFileContent(any())).thenReturn(new ByteArrayInputStream(MessageAttachmentTestFactory.CONTENT.getBytes())); + + var result = service.getContent(FILE_ID); + + assertThat(result).isEqualTo(MessageAttachmentTestFactory.CONTENT.getBytes()); + } + } + + @Nested + class TestEncodeAttachmentContent { + + @Test + void shouldEncodeContent() { + var result = service.encodeAttachmentContent(MessageAttachmentTestFactory.DECODED_CONTENT.getBytes()); + + assertThat(result).isEqualTo(MessageAttachmentTestFactory.CONTENT); + } + } - assertThat(attachmentFile.getName()).isEqualTo(MessageAttachmentTestFactory.FILENAME); + @Nested + class TestPersistAttachment { + + private static final String ATTACHMENT_FILE_ID = LoremIpsum.getInstance().getWords(1); + private static final MessageAttachment ATTACHMENT = MessageAttachmentTestFactory.create(); + + @Mock + private AttachmentFile attachmentFile; + @Mock + private InputStream inputStream; + + @BeforeEach + void init() { + doReturn(attachmentFile).when(service).buildAttachmentFile(any(), any()); + when(attachmentFileService.uploadAttachmentFile(any(), any())).thenReturn(ATTACHMENT_FILE_ID); } @Test - void shouldMapContent() { - var attachmentFile = messageAttachmentService.mapAttachmentFile(MessageAttachmentTestFactory.create()); + void shouldCallBuildAttachmentFile() { + persistAttachment(); + + verify(service).buildAttachmentFile(AttachmentFileTestFactory.VORGANG_ID, ATTACHMENT); + } + + @Test + void shouldCallCreateAttachmentFile() { + persistAttachment(); + + verify(attachmentFileService).uploadAttachmentFile(attachmentFile, MessageAttachmentTestFactory.CONTENT); + } + + @Test + void shouldReturnAttachmentFileId() { + var result = persistAttachment(); + + assertThat(result).isEqualTo(ATTACHMENT_FILE_ID); + } + + private String persistAttachment() { + return service.persistAttachment(AttachmentFileTestFactory.VORGANG_ID, ATTACHMENT); + } + } + + @Nested + class TestBuildAttachmentFile { + + @Test + void shouldMapFileName() { + var attachmentFile = buildAttachmentFile(); + + assertThat(attachmentFile).usingRecursiveComparison().ignoringFields("contentType").isEqualTo(AttachmentFileTestFactory.create()); + } - assertThat(attachmentFile.getContent()).hasContent(MessageAttachmentTestFactory.CONTENT); + private AttachmentFile buildAttachmentFile() { + return service.buildAttachmentFile(AttachmentFileTestFactory.VORGANG_ID, MessageAttachmentTestFactory.create()); } } } diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentTestFactory.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentTestFactory.java index 17181b240ad7645e49afd5c977cefdb925226425..b0ca53c550055aa6c20865dec1745589dd2bb865 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentTestFactory.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/osi/MessageAttachmentTestFactory.java @@ -23,9 +23,11 @@ */ package de.ozgcloud.nachrichten.postfach.osi; +import de.ozgcloud.nachrichten.file.AttachmentFileTestFactory; + public class MessageAttachmentTestFactory { - public final static String FILENAME = "test.txt"; + public final static String FILENAME = AttachmentFileTestFactory.NAME; public static final String CONTENT = "dGVzdA=="; public static final String DECODED_CONTENT = "test"; public static final long SIZE = 4L; diff --git a/pom.xml b/pom.xml index a1265e9ac60cb1fb3c219d3fde293153aae4df77..0e9e340350700284443522feefae9c335268da76 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-parent</artifactId> - <version>4.5.0</version> + <version>4.7.0</version> </parent> <groupId>de.ozgcloud.nachrichten</groupId>