diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java index c03d4e681f51ff7a38fb4741ac21e92e187638e6..42c45db565ca8bb736d62827193e7d9c498bdbb4 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java @@ -44,7 +44,7 @@ public class OsiPostfachRemoteService implements PostfachRemoteService { try { postfachApiFacadeService.deleteMessage(messageId); } catch (RuntimeException e) { - throw new OsiPostfachException("Failed to delete messages", e); + throw new OsiPostfachException("Failed to delete message", e); } } 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 cbd164b320d2d0275be14c42b8044542a9dfa3d6..d2de2f10ab57d1bad01b53bb5ad79f3d30eb7d3d 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 @@ -1,9 +1,12 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; -import java.time.ZoneOffset; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.util.UUID; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; import de.ozgcloud.nachrichten.postfach.PostfachAddress; @@ -15,33 +18,58 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; import lombok.Builder; import lombok.Getter; -@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = { ZoneOffset.class, Osi2HtmlDocument.class }) +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = { Osi2HtmlDocument.class }) public interface Osi2ResponseMapper { String POSTFACH_ADDRESS_VERSION = "2.0"; int POSTFACH_ADDRESS_TYPE = 2; @Mapping(target = "id", ignore = true) - @Mapping(target = "postfachAddress", expression = "java(buildPostfachAddressByPostfachId(message.getMessageBox().toString()))") + @Mapping(target = "vorgangId", source = "sequencenumber") + @Mapping(target = "postfachAddress", source = "messageBox") @Mapping(target = "messageId", source = "guid") - @Mapping(target = "createdAt", expression = "java(java.time.ZonedDateTime.now())") - @Mapping(target = "createdBy", source = "displayName") - @Mapping(target = "sentAt", expression = "java(message.getResponseTime().toZonedDateTime())") + @Mapping(target = "referencedNachricht", ignore = true) + + @Mapping(target = "createdAt", source = "responseTime", qualifiedByName = "mapOffsetDateTimeToZoned") + @Mapping(target = "createdBy", ignore = true) + + @Mapping(target = "sentAt", ignore = true) @Mapping(target = "sentSuccessful", ignore = true) @Mapping(target = "messageCode", ignore = true) + @Mapping(target = "direction", constant = "IN") - @Mapping(target = "vorgangId", source = "sequencenumber") - @Mapping(target = "referencedNachricht", ignore = true) - @Mapping(target = "mailBody", expression = "java( message.getIsHtml() ? Osi2HtmlDocument.renderToPlainText(message.getBody()) : message.getBody() )") + + @Mapping(target = "subject", source = "subject") + @Mapping(target = "mailBody", source = ".", qualifiedByName = "mapMailBody") @Mapping(target = "replyOption", source = "replyAction") @Mapping(target = "attachments", ignore = true) PostfachNachricht toPostfachNachricht(V1ReplyMessage message); - default PostfachAddress buildPostfachAddressByPostfachId(String postfachId) { + default String mapNullToEmpty(String value) { + return value == null ? "" : value; + } + + @Named("mapOffsetDateTimeToZoned") + default ZonedDateTime mapOffsetDateTimeToZoned(OffsetDateTime offsetDateTime) { + return offsetDateTime.toZonedDateTime(); + } + + @Named("mapMailBody") + default String mapMailBody(V1ReplyMessage message) { + // According to the API spec `body` is nullable + var body = mapNullToEmpty(message.getBody()); + return Boolean.TRUE.equals(message.getIsHtml()) + ? Osi2HtmlDocument.renderToPlainText(body) + : body; + } + + default PostfachAddress buildPostfachAddressByPostfachId(UUID messageBox) { return PostfachAddress.builder() .type(POSTFACH_ADDRESS_TYPE) .version(POSTFACH_ADDRESS_VERSION) - .identifier(StringBasedIdentifier.builder().mailboxId(postfachId).build()) + .identifier(StringBasedIdentifier.builder() + .mailboxId(messageBox.toString()) + .build()) .serviceKontoType(OsiPostfachRemoteService.POSTFACH_TYPE_OSI) .build(); 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 3797d54977d7ecb6a0dc197ded47e69a5dddef89..2bb4c78fa25a9d2a80f4b249c9b0e2b57b8dea74 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 @@ -1,6 +1,7 @@ package de.ozgcloud.nachrichten.postfach.osiv2.factory; import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.util.UUID; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1EidasLevel; @@ -9,8 +10,8 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; public class V1ReplyMessageTestFactory { - private static final String SEQUENCE_NUMMER = "OZG-Cloud-VorgangId"; - private static final String SUBJECT = "Das ist das Subject"; + public static final String SEQUENCE_NUMMER = "OZG-Cloud-VorgangId"; + public static final String SUBJECT = "Das ist das Subject"; public static final String HTML_REPLY_BODY = """ Das ist das Multiline&<b>a</b><br><br/> Body"""; @@ -18,28 +19,19 @@ public class V1ReplyMessageTestFactory { Das ist das Multiline&<b>a</b><br><br/> Body"""; public static final String MESSAGE_ID = UUID.randomUUID().toString(); - private static final String DISPLAY_NAME = "Das ist der Absender"; - private static final String ORIGIN_SENDER = "das ist der original Sender"; - private static final String REPLAY_ACTION = "Replypossible"; - private static final String EIDAS_LEVEL = "Low"; - private static final Boolean IS_OBLIGATORY = Boolean.FALSE; - private static final Boolean IS_HTML = Boolean.FALSE; - private static final String MESSAGE_BOX = "Mailbox-Id-Antwortender"; - private static final OffsetDateTime RESPONSE_TIME = OffsetDateTime.now(); + public static final String MESSAGE_BOX_ID = UUID.randomUUID().toString(); + public static final ZonedDateTime RESPONSE_TIME = ZonedDateTime.now(); public static V1ReplyMessage create() { return new V1ReplyMessage() .sequencenumber(SEQUENCE_NUMMER) .subject(SUBJECT) - .body(REPLY_BODY) - .displayName(DISPLAY_NAME) - .originSender(ORIGIN_SENDER) - .replyAction(V1ReplyBehavior.fromValue(REPLAY_ACTION)) - .eidasLevel(V1EidasLevel.fromValue(EIDAS_LEVEL)) - .isObligatory(IS_OBLIGATORY) - .isHtml(IS_HTML) + .replyAction(V1ReplyBehavior.REPLYPOSSIBLE) + .isObligatory(false) + .eidasLevel(V1EidasLevel.LOW) + .isHtml(false) .guid(UUID.fromString(MESSAGE_ID)) - .messageBox(UUID.nameUUIDFromBytes(MESSAGE_BOX.getBytes())) - .responseTime(RESPONSE_TIME); + .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 e692451fc530c988380623f5bc90e826071994d8..cbe2fdff4f392a30782854bece3495f48a474d00 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 @@ -3,13 +3,6 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory.*; import static org.assertj.core.api.Assertions.*; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.UUID; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; @@ -18,11 +11,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mapstruct.factory.Mappers; import org.mockito.InjectMocks; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; class Osi2ResponseMapperTest { @@ -36,64 +31,51 @@ class Osi2ResponseMapperTest { class V1ReplyMessageToPostfachNachricht { @Test - void shouldHaveVorgangId() { + void shouldMapVorgangId() { var result = doMapping(); assertThat(result.getVorgangId()).isEqualTo("OZG-Cloud-VorgangId"); } @Test - void shouldHavePostfachAddress() { + void shouldMapPostfachAddress() { var result = doMapping(); - assertThat(result.getPostfachAddress().getIdentifier().toString()) - .hasToString(UUID.nameUUIDFromBytes("Mailbox-Id-Antwortender".getBytes()).toString()); + assertThat(result.getPostfachAddress().getIdentifier()) + .hasToString(MESSAGE_BOX_ID); } @Test - void shouldHaveCreatedAt() { + void shouldMapCreatedAt() { var result = doMapping(); - assertThat(result.getCreatedAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(5, ChronoUnit.SECONDS)); + assertThat(result.getCreatedAt()).isEqualTo(RESPONSE_TIME); } @Test - void shouldHaveCreatedBy() { - var result = doMapping(); - - assertThat(result.getCreatedBy()).isEqualTo("Das ist der Absender"); - } - - @Test - void shouldHaveSentAt() { - var result = doMapping(); - - assertThat(result.getSentAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(5, ChronoUnit.SECONDS)); - } - - @Test - void shouldHaveDirection() { + void shouldMapDirection() { var result = doMapping(); assertThat(result.getDirection()).isEqualTo(PostfachNachricht.Direction.IN); } @Test - void shouldHaveSubject() { + void shouldMapSubject() { var result = doMapping(); assertThat(result.getSubject()).isEqualTo("Das ist das Subject"); } @Test - void shouldHaveBody() { + void shouldMapNullBodyToEmptyString() { var result = doMapping(); - assertThat(result.getMailBody()).isEqualTo(REPLY_BODY); + assertThat(result.getMailBody()).isEmpty(); } + @DisplayName("should map modified HTML body if HTML message") @Test - void shouldMapHTMLBody() { + void shouldMapModifiedHtmlBodyIfHtmlMessage() { var htmlMessage = V1ReplyMessageTestFactory.create() .body(HTML_REPLY_BODY) .isHtml(true); @@ -103,11 +85,37 @@ class Osi2ResponseMapperTest { assertThat(result.getMailBody()).isEqualTo(REPLY_BODY); } - @Test - void shouldHaveReplyOption() { - var result = doMapping(); + @DisplayName("should map unmodified body if not HTML message") + @ParameterizedTest + @ValueSource(strings = { REPLY_BODY, HTML_REPLY_BODY }) + void shouldMapUnmodifiedBodyIfNotHtmlMessage(String body) { + var htmlMessage = V1ReplyMessageTestFactory.create() + .body(body) + .isHtml(false); + + var result = mapper.toPostfachNachricht(htmlMessage); + + assertThat(result.getMailBody()).isEqualTo(body); + } + + static Stream<Arguments> replyOptionValues() { + return Stream.of( + Arguments.of(V1ReplyBehavior.REPLYPOSSIBLE, PostfachNachricht.ReplyOption.POSSIBLE), + Arguments.of(V1ReplyBehavior.REPLYMANDATORY, PostfachNachricht.ReplyOption.MANDATORY), + Arguments.of(V1ReplyBehavior.REPLYFORBIDDEN, PostfachNachricht.ReplyOption.FORBIDDEN) + ); + } + + @ParameterizedTest + @MethodSource("replyOptionValues") + void shouldMapReplyOption(V1ReplyBehavior replyAction, PostfachNachricht.ReplyOption expected) { + var replyActionMessage = V1ReplyMessageTestFactory.create() + .replyAction(replyAction) + .isHtml(false); + + var result = mapper.toPostfachNachricht(replyActionMessage); - assertThat(result.getReplyOption()).isEqualTo(PostfachNachricht.ReplyOption.POSSIBLE); + assertThat(result.getReplyOption()).isEqualTo(expected); } @DisplayName("should map messageId")