From 512e40f93adb137e397cf5f3c346eb5413b3375a Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Wed, 19 Feb 2025 14:58:26 +0100 Subject: [PATCH] OZG-4097 receive-attachment: Map to empty list of attachments if null --- .../osiv2/transfer/Osi2ResponseMapper.java | 10 +- .../transfer/PostfachApiFacadeService.java | 4 +- .../osiv2/OsiPostfachRemoteServiceITCase.java | 39 ++++++-- .../AttachmentExampleUploadUtil.java | 2 +- .../factory/V1ReplyFilesTestFactory.java | 25 +++++ .../factory/V1ReplyMessageTestFactory.java | 9 +- .../transfer/Osi2ResponseMapperTest.java | 98 +++++++++++++++++-- .../PostfachApiFacadeServiceTest.java | 19 ++-- 8 files changed, 171 insertions(+), 35 deletions(-) create mode 100644 src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyFilesTestFactory.java diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java index 43a65de..dbbdf6a 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java @@ -38,14 +38,14 @@ public interface Osi2ResponseMapper { String POSTFACH_ADDRESS_VERSION = "2.0"; int POSTFACH_ADDRESS_TYPE = 2; - @Mapping(target = "vorgangId", source = "message.sequencenumber") - @Mapping(target = "postfachAddress", source = "message.messageBox") - @Mapping(target = "messageId", source = "message.guid") - @Mapping(target = "createdAt", source = "message.responseTime", qualifiedByName = "mapOffsetDateTimeToZoned") + @Mapping(target = "vorgangId", source = "sequencenumber") + @Mapping(target = "postfachAddress", source = "messageBox") + @Mapping(target = "messageId", source = "guid") + @Mapping(target = "createdAt", source = "responseTime", qualifiedByName = "mapOffsetDateTimeToZoned") @Mapping(target = "subject", source = "subject") @Mapping(target = "mailBody", source = ".", qualifiedByName = "mapMailBody") @Mapping(target = "replyOption", source = "replyAction") - @Mapping(target = "attachments", source = "files") + @Mapping(target = "attachments", source = "files", defaultExpression = "java(List.of())") Osi2Message toMessage(V1ReplyMessage message); @Mapping(target = "contentType", source = "mimeType") diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java index 0c1aed4..5f29ced 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java @@ -73,7 +73,7 @@ public class PostfachApiFacadeService { quarantineApi.deleteUpload(UUID.fromString(fileUploadId)); } - public Resource downloadAttachment(String messageId, String attachmentGuid) { - return messageExchangeApi.getMessageAttachment(UUID.fromString(messageId), UUID.fromString(attachmentGuid)); + public Resource downloadAttachment(String messageGuid, String attachmentGuid) { + return messageExchangeApi.getMessageAttachment(UUID.fromString(messageGuid), UUID.fromString(attachmentGuid)); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java index d2f0700..79c86ce 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java @@ -5,7 +5,6 @@ import static de.ozgcloud.nachrichten.NachrichtenManagerConfiguration.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.JwtFactory.*; import static org.assertj.core.api.Assertions.*; -import java.time.OffsetDateTime; import java.util.List; import java.util.UUID; import java.util.function.Function; @@ -25,6 +24,7 @@ import org.springframework.test.context.TestPropertySource; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.thedeanda.lorem.LoremIpsum; import de.ozgcloud.nachrichten.postfach.PostfachMessageCode; import de.ozgcloud.nachrichten.postfach.osiv2.attachment.Osi2AttachmentFileService; @@ -37,6 +37,7 @@ import de.ozgcloud.nachrichten.postfach.osiv2.factory.JsonUtil; import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeSendMessageResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyFilesTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineFileResult; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineStatus; @@ -227,6 +228,12 @@ class OsiPostfachRemoteServiceITCase { var messageList = osiPostfachRemoteService.getAllMessages().toList(); assertThat(messageList).hasSize(1); + var firstMessage = messageList.getFirst(); + assertThat(firstMessage.getAttachments()).hasSize(1); + var fileMetadata = osi2AttachmentFileService.getFileMetadataOfIds(firstMessage.getAttachments()); + var firstAttachment = fileMetadata.getFirst(); + assertThat(firstAttachment.getName()).isNotBlank(); + assertThat(firstAttachment.getContentType()).isEqualTo("text/plain"); } @DisplayName("should receive two messages") @@ -240,23 +247,37 @@ class OsiPostfachRemoteServiceITCase { assertThat(messageList).hasSize(2); } - private void mockPostfachMessageAndResponse(final String... uuids) { + private void mockPostfachMessageAndResponse(final String... messageGuids) { // Stub message listing response (MessageExchangeApi::receiveMessages) postfachFacadeMockServer.stubFor(get(urlPathEqualTo("/MessageExchange/v1/Receive")) .withQueryParam("take", equalTo("100")) .withQueryParam("skip", equalTo("0")) - .willReturn(okJsonObj(MessageExchangeReceiveMessagesResponseTestFactory.create(uuids))) + .willReturn(okJsonObj(MessageExchangeReceiveMessagesResponseTestFactory.create(messageGuids))) ); - for (String uuid : uuids) { + for (String messageGuid : messageGuids) { + + var attachmentText = LoremIpsum.getInstance().getParagraphs(10, 20); + var attachment = V1ReplyFilesTestFactory.createBuilder() + .guid(UUID.randomUUID()) + .name(LoremIpsum.getInstance().getName() + ".txt") + .build(); // Stub individual response for message (MessageExchangeApi::getMessage) postfachFacadeMockServer.stubFor(get(urlPathTemplate("/MessageExchange/v1/Receive/{messageId}")) - .withPathParam("messageId", equalTo(uuid)) - .willReturn(okJsonObj( - V1ReplyMessageTestFactory.create() - .messageBox(UUID.fromString(uuid)) - .responseTime(OffsetDateTime.now()) + .withPathParam("messageId", equalTo(messageGuid)) + .willReturn(okJsonObj(V1ReplyMessageTestFactory.create() + .guid(UUID.fromString(messageGuid)) + .files(List.of(attachment)) )) ); + // Stub attachment response (MessageExchangeApi::getMessageAttachment) + postfachFacadeMockServer.stubFor(get(urlPathTemplate("/MessageExchange/v1/Receive/{messageId}/Attachment/{attachmentId}")) + .withPathParam("messageId", equalTo(messageGuid)) + .withPathParam("attachmentId", equalTo(attachment.getGuid().toString())) + .willReturn(ok() + .withHeader("Content-Type", "application/octet-stream") + .withBody(attachmentText.getBytes()) + ) + ); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java index d8cb98e..b8cea08 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java @@ -17,7 +17,7 @@ public class AttachmentExampleUploadUtil { public static String uploadTextFile(Osi2AttachmentFileService remoteService) { return remoteService.uploadFileAndReturnId(AttachmentFile.builder() - .contentType("test/plain") + .contentType("text/plain") .name("test.txt") .vorgangId(UUID.randomUUID().toString()) .build(), new ByteArrayInputStream(EXAMPLE_TEXT_DATA)); diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyFilesTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyFilesTestFactory.java new file mode 100644 index 0000000..1974823 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyFilesTestFactory.java @@ -0,0 +1,25 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.factory; + +import java.util.UUID; + +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyFiles; + +public class V1ReplyFilesTestFactory { + + public static final String ATTACHMENT_GUID = UUID.randomUUID().toString(); + public static final String ATTACHMENT_NAME = "test.txt"; + public static final String ATTACHMENT_CONTENT_TYPE = "text/plain"; + public static final Long ATTACHMENT_SIZE = 1000L; + + public static V1ReplyFiles create() { + return createBuilder().build(); + } + + public static V1ReplyFiles.Builder createBuilder() { + return V1ReplyFiles.builder() + .guid(UUID.fromString(ATTACHMENT_GUID)) + .name(ATTACHMENT_NAME) + .mimeType(ATTACHMENT_CONTENT_TYPE) + .size(ATTACHMENT_SIZE); + } +} diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java index 2bb4c78..eb41e78 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java @@ -2,6 +2,7 @@ package de.ozgcloud.nachrichten.postfach.osiv2.factory; import java.time.OffsetDateTime; import java.time.ZonedDateTime; +import java.util.List; import java.util.UUID; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1EidasLevel; @@ -23,14 +24,20 @@ public class V1ReplyMessageTestFactory { public static final ZonedDateTime RESPONSE_TIME = ZonedDateTime.now(); public static V1ReplyMessage create() { - return new V1ReplyMessage() + return createBuilder().build(); + } + + public static V1ReplyMessage.Builder createBuilder() { + return V1ReplyMessage.builder() .sequencenumber(SEQUENCE_NUMMER) .subject(SUBJECT) .replyAction(V1ReplyBehavior.REPLYPOSSIBLE) .isObligatory(false) .eidasLevel(V1EidasLevel.LOW) .isHtml(false) + .body(REPLY_BODY) .guid(UUID.fromString(MESSAGE_ID)) + .files(List.of(V1ReplyFilesTestFactory.create())) .messageBox(UUID.fromString(MESSAGE_BOX_ID)) .responseTime(OffsetDateTime.of(RESPONSE_TIME.toLocalDateTime(), RESPONSE_TIME.getOffset())); } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java index cfd7d23..ff5a336 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java @@ -1,6 +1,7 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.QuarantineFileResultTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyFilesTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory.*; import static org.assertj.core.api.Assertions.*; @@ -21,10 +22,13 @@ import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2RuntimeException; import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2UploadException; import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.QuarantineFileResultTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyFilesTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineStatus; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyFiles; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; +import de.ozgcloud.nachrichten.postfach.osiv2.model.AttachmentInfo; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; import lombok.SneakyThrows; @@ -34,9 +38,9 @@ class Osi2ResponseMapperTest { private Osi2ResponseMapper mapper = Mappers.getMapper(Osi2ResponseMapper.class); private final V1ReplyMessage message = V1ReplyMessageTestFactory.create(); - @DisplayName("map V1ReplyMessage to Osi2Message") + @DisplayName("toMessage") @Nested - class V1ReplyOsi2MessageToOsi2Message { + class TestToMessage { @Test void shouldMapVorgangId() { @@ -69,7 +73,11 @@ class Osi2ResponseMapperTest { @Test void shouldMapNullBodyToEmptyString() { - var result = doMapping(); + var noBodyMessage = V1ReplyMessageTestFactory.createBuilder() + .body(null) + .build(); + + var result = mapper.toMessage(noBodyMessage); assertThat(result.mailBody()).isEmpty(); } @@ -77,9 +85,10 @@ class Osi2ResponseMapperTest { @DisplayName("should map modified HTML body if HTML message") @Test void shouldMapModifiedHtmlBodyIfHtmlMessage() { - var htmlMessage = V1ReplyMessageTestFactory.create() + var htmlMessage = V1ReplyMessageTestFactory.createBuilder() .body(HTML_REPLY_BODY) - .isHtml(true); + .isHtml(true) + .build(); var result = mapper.toMessage(htmlMessage); @@ -90,9 +99,10 @@ class Osi2ResponseMapperTest { @ParameterizedTest @ValueSource(strings = { REPLY_BODY, HTML_REPLY_BODY }) void shouldMapUnmodifiedBodyIfNotHtmlMessage(String body) { - var htmlMessage = V1ReplyMessageTestFactory.create() + var htmlMessage = V1ReplyMessageTestFactory.createBuilder() .body(body) - .isHtml(false); + .isHtml(false) + .build(); var result = mapper.toMessage(htmlMessage); @@ -111,9 +121,10 @@ class Osi2ResponseMapperTest { @ParameterizedTest @MethodSource("replyOptionValues") void shouldMapReplyOption(V1ReplyBehavior replyAction, PostfachNachricht.ReplyOption expected) { - var replyActionMessage = V1ReplyMessageTestFactory.create() + var replyActionMessage = V1ReplyMessageTestFactory.createBuilder() .replyAction(replyAction) - .isHtml(false); + .isHtml(false) + .build(); var result = mapper.toMessage(replyActionMessage); @@ -128,6 +139,32 @@ class Osi2ResponseMapperTest { assertThat(result.messageId()).isEqualTo(MESSAGE_ID); } + @DisplayName("should map attachments") + @Test + void shouldMapAttachments() { + assert message.getFiles() != null; + var expectedAttachments = message.getFiles() + .stream() + .map(file -> mapper.toAttachmentInfo(file)) + .toList(); + + var result = doMapping(); + + assertThat(result.attachments()).usingRecursiveComparison().isEqualTo(expectedAttachments); + } + + @DisplayName("should map null attachments to empty list") + @Test + void shouldMapNullAttachmentsToEmptyList() { + var messageWithoutAttachments = V1ReplyMessageTestFactory.createBuilder() + .files(null) + .build(); + + var result = mapper.toMessage(messageWithoutAttachments); + + assertThat(result.attachments()).isEmpty(); + } + @DisplayName("should not fail if all fields are null") @Test void shouldNotFailIfAllFieldsAreNull() { @@ -152,6 +189,49 @@ class Osi2ResponseMapperTest { } } + @DisplayName("to AttachmentInfo") + @Nested + class TestToAttachmentInfo { + + private final V1ReplyFiles file = V1ReplyFilesTestFactory.create(); + + @DisplayName("should map guid") + @Test + void shouldMapGuid() { + var result = doMapping(); + + assertThat(result.guid()).isEqualTo(ATTACHMENT_GUID); + } + + @DisplayName("should map name") + @Test + void shouldMapName() { + var result = doMapping(); + + assertThat(result.name()).isEqualTo(ATTACHMENT_NAME); + } + + @DisplayName("should map size") + @Test + void shouldMapSize() { + var result = doMapping(); + + assertThat(result.size()).isEqualTo(ATTACHMENT_SIZE); + } + + @DisplayName("should map content type") + @Test + void shouldMapContentType() { + var result = doMapping(); + + assertThat(result.contentType()).isEqualTo(ATTACHMENT_CONTENT_TYPE); + } + + private AttachmentInfo doMapping() { + return mapper.toAttachmentInfo(file); + } + } + @DisplayName("is safe") @Nested class TestIsSafe { diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java index 15c944a..0e6e97f 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java @@ -25,6 +25,7 @@ import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMess import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.DomainChunkMetaData; @@ -36,6 +37,7 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineStatus; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; import de.ozgcloud.nachrichten.postfach.osiv2.model.FileChunkInfo; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; import lombok.SneakyThrows; class PostfachApiFacadeServiceTest { @@ -179,8 +181,8 @@ class PostfachApiFacadeServiceTest { @Nested class TestFetchMessageById { - @Mock - V1ReplyMessage replyMessage; + private final V1ReplyMessage replyMessage = V1ReplyMessageTestFactory.create(); + private final Osi2Message message = Osi2MessageTestFactory.create(); @Test void shouldCallGetMessage() { @@ -188,7 +190,7 @@ class PostfachApiFacadeServiceTest { service.fetchMessageById(MESSAGE_ID_1); - verify(messageExchangeApi).getMessage(any()); + verify(messageExchangeApi).getMessage(UUID.fromString(MESSAGE_ID_1)); } @Test @@ -198,17 +200,18 @@ class PostfachApiFacadeServiceTest { service.fetchMessageById(MESSAGE_ID_1); - verify(osi2ResponseMapper).toMessage(any()); + verify(osi2ResponseMapper).toMessage(replyMessage); } + @DisplayName("should return") @Test - void shouldReturnPostfachNachricht() { + void shouldReturn() { when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage); - when(osi2ResponseMapper.toMessage(any())).thenReturn(Osi2MessageTestFactory.create()); + when(osi2ResponseMapper.toMessage(any())).thenReturn(message); - var message = service.fetchMessageById(MESSAGE_ID_1); + var result = service.fetchMessageById(MESSAGE_ID_1); - assertThat(message).isEqualTo(Osi2MessageTestFactory.create()); + assertThat(result).isEqualTo(message); } } -- GitLab