From a8018ff40c84f53a5e2f9c623e9e5b3eab2d9146 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Fri, 31 Jan 2025 13:09:43 +0100 Subject: [PATCH 1/5] OZG-4095 Render html as plaintext with jsoup --- pom.xml | 6 ++ .../osiv2/transfer/Osi2HtmlDocument.java | 62 +++++++++++++ .../osiv2/transfer/Osi2ResponseMapper.java | 5 +- .../factory/V1ReplyMessageTestFactory.java | 6 +- .../osiv2/transfer/Osi2HtmlDocumentTest.java | 90 +++++++++++++++++++ .../transfer/Osi2ResponseMapperTest.java | 11 ++- 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java create mode 100644 src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java diff --git a/pom.xml b/pom.xml index 6da03db..30a0179 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ <openapi-generator.version>7.10.0</openapi-generator.version> <swagger-parser.version>2.1.23</swagger-parser.version> <wiremock-spring-boot.version>3.6.0</wiremock-spring-boot.version> + <jsoup.version>1.18.3</jsoup.version> </properties> <dependencies> <!-- OZG-Cloud --> @@ -59,6 +60,11 @@ <artifactId>swagger-parser</artifactId> <version>${swagger-parser.version}</version> </dependency> + <dependency> + <groupId>org.jsoup</groupId> + <artifactId>jsoup</artifactId> + <version>${jsoup.version}</version> + </dependency> <!-- test --> <dependency> diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java new file mode 100644 index 0000000..074c226 --- /dev/null +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java @@ -0,0 +1,62 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.transfer; + +import java.util.List; +import java.util.function.Function; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +public record Osi2HtmlDocument(String html) { + + private record SpanRendering(String tag, Function<Element, String> markdownReplacement) { + static SpanRendering of(String tag, Function<Element, String> markdownReplacement) { + return new SpanRendering(tag, markdownReplacement); + } + + void replaceElementsWithMarkdownSpansIn(Document document) { + document.body() + .select(tag) + .replaceAll(anchor -> new Element("span") + .text(markdownReplacement.apply(anchor)) + ); + } + } + + static final List<SpanRendering> SPAN_RENDERINGS = List.of( + SpanRendering.of("h1", heading -> "# " + heading.text()), + SpanRendering.of("h2", heading -> "## " + heading.text()), + SpanRendering.of("h3", heading -> "### " + heading.text()), + SpanRendering.of("i", italic -> "*" + italic.text() + "*"), + SpanRendering.of("em", italic -> "*" + italic.text() + "*"), + SpanRendering.of("b", bold -> "**" + bold.text() + "**"), + SpanRendering.of("strong", bold -> "**" + bold.text() + "**"), + SpanRendering.of("img", image -> " + ")"), + SpanRendering.of("a", anchor -> "[" + anchor.text() + "](" + anchor.attr("href") + ")"), + SpanRendering.of("blockquote", bold -> "> " + bold.wholeText()) + ); + + public String renderHTMLToPlainText() { + var document = Jsoup.parse(html); + addNewlinesForSubsequentParagraphs(document); + replaceElementsWithMarkdownSpansIn(document); + return document.wholeText(); + } + + private void addNewlinesForSubsequentParagraphs(Document document) { + document.body() + .select("p + p") + .prepend("\n"); + } + + private void replaceElementsWithMarkdownSpansIn(Document document) { + for (var entry : SPAN_RENDERINGS) { + entry.replaceElementsWithMarkdownSpansIn(document); + } + } + + public static String renderPlainText(String html) { + return new Osi2HtmlDocument(html).renderHTMLToPlainText(); + } + +} 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 f5acb80..ceb9579 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 @@ -2,6 +2,7 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import java.time.ZoneOffset; +import org.jsoup.Jsoup; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; @@ -15,7 +16,7 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; import lombok.Builder; import lombok.Getter; -@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = ZoneOffset.class) +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = { ZoneOffset.class, Osi2HtmlDocument.class }) public interface Osi2ResponseMapper { String POSTFACH_ADDRESS_VERSION = "2.0"; @@ -32,7 +33,7 @@ public interface Osi2ResponseMapper { @Mapping(target = "direction", constant = "IN") @Mapping(target = "vorgangId", source = "sequencenumber") @Mapping(target = "referencedNachricht", ignore = true) - @Mapping(target = "mailBody", source = "body") + @Mapping(target = "mailBody", expression = "java( message.getIsHtml() ? Osi2HtmlDocument.renderPlainText(message.getBody()) : message.getBody() )") @Mapping(target = "replyOption", source = "replyAction") @Mapping(target = "attachments", ignore = true) PostfachNachricht toPostfachNachricht(V1ReplyMessage message); 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 244a643..6675805 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 @@ -12,7 +12,11 @@ public class V1ReplyMessageTestFactory { private static final String SEQUENCE_NUMMER = "OZG-Cloud-VorgangId"; private static final String SUBJECT = "Das ist das Subject"; private static final String BODY = """ - Das ist das Multiline + Das ist das Multiline&<br><br/> + Body"""; + private static final String CLEAN_BODY = """ + Das ist das Multiline& + Body"""; private static final String DISPLAY_NAME = "Das ist der Absender"; private static final String ORIGIN_SENDER = "das ist der original Sender"; diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java new file mode 100644 index 0000000..5970838 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java @@ -0,0 +1,90 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.transfer; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class Osi2HtmlDocumentTest { + + @DisplayName("should render anchor link") + @Test + void shouldRenderAnchorLink() { + var plainText = renderPlainText("<a href=\"https://www.example.com\">Example</a>"); + + assertThat("[Example](https://www.example.com)").isEqualTo(plainText); + } + + @DisplayName("should render h1 heading") + @Test + void shouldRenderH1Heading() { + var plainText = renderPlainText("<h1>Example</h1>"); + + assertThat(plainText).isEqualTo("# Example"); + } + + @DisplayName("should render h2 heading") + @Test + void shouldRenderH2Heading() { + var plainText = renderPlainText("<h2>Example</h2>"); + + assertThat(plainText).isEqualTo("## Example"); + } + + @DisplayName("should render h3 heading") + @Test + void shouldRenderH3Heading() { + var plainText = renderPlainText("<h3>Example</h3>"); + + assertThat(plainText ).isEqualTo("### Example"); + } + + + @DisplayName("should render italic") + @ParameterizedTest + @ValueSource(strings = { "<i>Example</i>", "<em>Example</em>" }) + void shouldRenderItalic(String italicExample) { + var plainText = renderPlainText(italicExample); + + assertThat(plainText).isEqualTo("*Example*"); + } + + @DisplayName("should render bold") + @ParameterizedTest + @ValueSource(strings = { "<b>Example</b>", "<strong>Example</strong>" }) + void shouldRenderBold(String boldExample) { + var plainText = renderPlainText(boldExample); + + assertThat(plainText).isEqualTo("**Example**"); + } + + @DisplayName("should render image") + @Test + void shouldRenderImage() { + var plainText = renderPlainText("<img src=\"https://www.example.com/image.jpg\" alt=\"Example\">"); + + assertThat(plainText).isEqualTo(""); + } + + @DisplayName("should render blockquote") + @Test + void shouldRenderBlockquote() { + var plainText = renderPlainText("<blockquote><p>Example</p><p>Example2</p></blockquote>"); + + assertThat(plainText).isEqualTo("> Example\nExample2"); + } + + @DisplayName("should render html") + @Test + void shouldRenderHtml() { + var plainText = renderPlainText("<p>Example😶</p><br/><p>Example2</p><b>"); + + assertThat(plainText).isEqualTo("Example\uD83D\uDE36\nExample2****"); + } + + private String renderPlainText(String html) { + return new Osi2HtmlDocument(html).renderHTMLToPlainText(); + } +} \ No newline at end of file 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 54586de..af8bf0b 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 @@ -20,7 +20,7 @@ class Osi2ResponseMapperTest { @InjectMocks private Osi2ResponseMapper mapper = Mappers.getMapper(Osi2ResponseMapper.class); - private V1ReplyMessage message = V1ReplyMessageTestFactory.create(); + private final V1ReplyMessage message = V1ReplyMessageTestFactory.create(); @DisplayName("map V1ReplyMessage to PostfachNachricht") @Nested @@ -91,6 +91,15 @@ class Osi2ResponseMapperTest { Body"""); } + @Test + void shouldMapHTMLBody(){ + var postfachNachricht = mapper.toPostfachNachricht(message.isHtml(true)); + + assertThat(postfachNachricht.getMailBody()).isEqualTo(""" + Das ist das Multiline + Body"""); + } + @Test void shouldHaveReplyOption(){ var postfachNachricht = mapper.toPostfachNachricht(message); -- GitLab From 7d4c7f43291c5b049dd5d9f8d6bd42cb35c6754b Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Fri, 31 Jan 2025 14:33:29 +0100 Subject: [PATCH 2/5] OZG-4095 Simplify replacement --- .../osiv2/transfer/Osi2HtmlDocument.java | 31 +++++++------------ .../transfer/PostfachApiFacadeService.java | 1 - .../osiv2/transfer/Osi2HtmlDocumentTest.java | 6 ++-- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java index 074c226..5273da3 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java @@ -24,39 +24,32 @@ public record Osi2HtmlDocument(String html) { } static final List<SpanRendering> SPAN_RENDERINGS = List.of( + SpanRendering.of("a", anchor -> "[" + anchor.text() + "](" + anchor.attr("href") + ")"), + SpanRendering.of("img", image -> " + ")"), + SpanRendering.of("i", italic -> "_" + italic.text() + "_"), + SpanRendering.of("em", italic -> "_" + italic.text() + "_"), + SpanRendering.of("b", bold -> "__" + bold.text() + "__"), + SpanRendering.of("strong", bold -> "__" + bold.text() + "__"), SpanRendering.of("h1", heading -> "# " + heading.text()), SpanRendering.of("h2", heading -> "## " + heading.text()), SpanRendering.of("h3", heading -> "### " + heading.text()), - SpanRendering.of("i", italic -> "*" + italic.text() + "*"), - SpanRendering.of("em", italic -> "*" + italic.text() + "*"), - SpanRendering.of("b", bold -> "**" + bold.text() + "**"), - SpanRendering.of("strong", bold -> "**" + bold.text() + "**"), - SpanRendering.of("img", image -> " + ")"), - SpanRendering.of("a", anchor -> "[" + anchor.text() + "](" + anchor.attr("href") + ")"), - SpanRendering.of("blockquote", bold -> "> " + bold.wholeText()) + SpanRendering.of("blockquote", bold -> "> " + bold.text()) ); - public String renderHTMLToPlainText() { + public String renderToPlainText() { var document = Jsoup.parse(html); - addNewlinesForSubsequentParagraphs(document); - replaceElementsWithMarkdownSpansIn(document); + applySpanRenderings(document); return document.wholeText(); } - private void addNewlinesForSubsequentParagraphs(Document document) { - document.body() - .select("p + p") - .prepend("\n"); - } - - private void replaceElementsWithMarkdownSpansIn(Document document) { + private void applySpanRenderings(Document document) { for (var entry : SPAN_RENDERINGS) { entry.replaceElementsWithMarkdownSpansIn(document); } } - public static String renderPlainText(String html) { - return new Osi2HtmlDocument(html).renderHTMLToPlainText(); + public static String renderToPlainText(String html) { + return new Osi2HtmlDocument(html).renderToPlainText(); } } 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 d11c556..b4012fd 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 @@ -49,6 +49,5 @@ public class PostfachApiFacadeService { public void deleteMessage(final String messageId) { messageExchangeApi.deleteMessage(UUID.fromString(messageId)); - } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java index 5970838..fd8dc9e 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java @@ -71,9 +71,9 @@ class Osi2HtmlDocumentTest { @DisplayName("should render blockquote") @Test void shouldRenderBlockquote() { - var plainText = renderPlainText("<blockquote><p>Example</p><p>Example2</p></blockquote>"); + var plainText = renderPlainText("<blockquote><p>Example</p>\n<p>Example2</p></blockquote>"); - assertThat(plainText).isEqualTo("> Example\nExample2"); + assertThat(plainText).isEqualTo("> Example Example2"); } @DisplayName("should render html") @@ -85,6 +85,6 @@ class Osi2HtmlDocumentTest { } private String renderPlainText(String html) { - return new Osi2HtmlDocument(html).renderHTMLToPlainText(); + return new Osi2HtmlDocument(html).renderToPlainText(); } } \ No newline at end of file -- GitLab From 5e72cd448d52b882b9414f18648c600660b0f099 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Fri, 31 Jan 2025 15:18:31 +0100 Subject: [PATCH 3/5] OZG-4095 receive: Use html rendering in response mapper --- .../osiv2/transfer/Osi2ResponseMapper.java | 2 +- .../factory/V1ReplyMessageTestFactory.java | 11 +++--- .../osiv2/transfer/Osi2HtmlDocumentTest.java | 6 +-- .../transfer/Osi2ResponseMapperTest.java | 37 +++++++++---------- 4 files changed, 28 insertions(+), 28 deletions(-) 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 ceb9579..aef50b4 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 @@ -33,7 +33,7 @@ public interface Osi2ResponseMapper { @Mapping(target = "direction", constant = "IN") @Mapping(target = "vorgangId", source = "sequencenumber") @Mapping(target = "referencedNachricht", ignore = true) - @Mapping(target = "mailBody", expression = "java( message.getIsHtml() ? Osi2HtmlDocument.renderPlainText(message.getBody()) : message.getBody() )") + @Mapping(target = "mailBody", expression = "java( message.getIsHtml() ? Osi2HtmlDocument.renderToPlainText(message.getBody()) : message.getBody() )") @Mapping(target = "replyOption", source = "replyAction") @Mapping(target = "attachments", ignore = true) PostfachNachricht toPostfachNachricht(V1ReplyMessage message); 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 6675805..e51f5ef 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 @@ -11,11 +11,12 @@ public class V1ReplyMessageTestFactory { private static final String SEQUENCE_NUMMER = "OZG-Cloud-VorgangId"; private static final String SUBJECT = "Das ist das Subject"; - private static final String BODY = """ - Das ist das Multiline&<br><br/> + public static final String HTML_REPLY_BODY = """ + Das ist das Multiline&<b>a</b><br><br/> Body"""; - private static final String CLEAN_BODY = """ - Das ist das Multiline& + public static final String REPLY_BODY = """ + Das ist das Multiline&<b>a</b> + Body"""; private static final String DISPLAY_NAME = "Das ist der Absender"; @@ -32,7 +33,7 @@ public class V1ReplyMessageTestFactory { return new V1ReplyMessage() .sequencenumber(SEQUENCE_NUMMER) .subject(SUBJECT) - .body(BODY) + .body(REPLY_BODY) .displayName(DISPLAY_NAME) .originSender(ORIGIN_SENDER) .replyAction(V1ReplyBehavior.fromValue(REPLAY_ACTION)) diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java index fd8dc9e..c4a928a 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java @@ -48,7 +48,7 @@ class Osi2HtmlDocumentTest { void shouldRenderItalic(String italicExample) { var plainText = renderPlainText(italicExample); - assertThat(plainText).isEqualTo("*Example*"); + assertThat(plainText).isEqualTo("_Example_"); } @DisplayName("should render bold") @@ -57,7 +57,7 @@ class Osi2HtmlDocumentTest { void shouldRenderBold(String boldExample) { var plainText = renderPlainText(boldExample); - assertThat(plainText).isEqualTo("**Example**"); + assertThat(plainText).isEqualTo("__Example__"); } @DisplayName("should render image") @@ -81,7 +81,7 @@ class Osi2HtmlDocumentTest { void shouldRenderHtml() { var plainText = renderPlainText("<p>Example😶</p><br/><p>Example2</p><b>"); - assertThat(plainText).isEqualTo("Example\uD83D\uDE36\nExample2****"); + assertThat(plainText).isEqualTo("Example\uD83D\uDE36\nExample2____"); } private String renderPlainText(String html) { 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 af8bf0b..1c417e5 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,5 +1,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.time.ZonedDateTime; @@ -26,21 +27,21 @@ class Osi2ResponseMapperTest { @Nested class V1ReplyMessageToPostfachNachricht { @Test - void shouldHaveId(){ + void shouldHaveId() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getId()).isEqualTo(UUID.nameUUIDFromBytes("123-guid-456".getBytes()).toString()); } @Test - void shouldHaveVorgangId(){ + void shouldHaveVorgangId() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getVorgangId()).isEqualTo("OZG-Cloud-VorgangId"); } @Test - void shouldHavePostfachAddress(){ + void shouldHavePostfachAddress() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getPostfachAddress().getIdentifier().toString()) @@ -48,65 +49,63 @@ class Osi2ResponseMapperTest { } @Test - void shouldHaveCreatedAt(){ + void shouldHaveCreatedAt() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getCreatedAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(5, ChronoUnit.SECONDS)); } @Test - void shouldHaveCreatedBy(){ + void shouldHaveCreatedBy() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getCreatedBy()).isEqualTo("Das ist der Absender"); } @Test - void shouldHaveSentAt(){ + void shouldHaveSentAt() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getSentAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(5, ChronoUnit.SECONDS)); } @Test - void shouldHaveDirection(){ + void shouldHaveDirection() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getDirection()).isEqualTo(PostfachNachricht.Direction.IN); } @Test - void shouldHaveSubject(){ + void shouldHaveSubject() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getSubject()).isEqualTo("Das ist das Subject"); } @Test - void shouldHaveBody(){ + void shouldHaveBody() { var postfachNachricht = mapper.toPostfachNachricht(message); - assertThat(postfachNachricht.getMailBody()).isEqualTo(""" - Das ist das Multiline - Body"""); + assertThat(postfachNachricht.getMailBody()).isEqualTo(REPLY_BODY); } @Test - void shouldMapHTMLBody(){ - var postfachNachricht = mapper.toPostfachNachricht(message.isHtml(true)); + void shouldMapHTMLBody() { + var postfachNachricht = mapper.toPostfachNachricht(message + .body(HTML_REPLY_BODY) + .isHtml(true)); - assertThat(postfachNachricht.getMailBody()).isEqualTo(""" - Das ist das Multiline - Body"""); + assertThat(postfachNachricht.getMailBody()).isEqualTo(REPLY_BODY); } @Test - void shouldHaveReplyOption(){ + void shouldHaveReplyOption() { var postfachNachricht = mapper.toPostfachNachricht(message); assertThat(postfachNachricht.getReplyOption()).isEqualTo(PostfachNachricht.ReplyOption.POSSIBLE); } -// TODO:prüfen das Attachments in der PostfachNachricht enthalten sind + // TODO:prüfen das Attachments in der PostfachNachricht enthalten sind } } -- GitLab From 135699b7f867fe83495686ce7c44b1fd7b85d4a0 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Mon, 3 Feb 2025 11:07:07 +0100 Subject: [PATCH 4/5] OZG-4095 html: Reduce html rendering to only replace newlines --- pom.xml | 6 -- .../osiv2/transfer/Osi2HtmlDocument.java | 44 +---------- .../osiv2/transfer/Osi2ResponseMapper.java | 1 - .../factory/V1ReplyMessageTestFactory.java | 2 +- .../osiv2/transfer/Osi2HtmlDocumentTest.java | 73 +------------------ 5 files changed, 4 insertions(+), 122 deletions(-) diff --git a/pom.xml b/pom.xml index 30a0179..6da03db 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,6 @@ <openapi-generator.version>7.10.0</openapi-generator.version> <swagger-parser.version>2.1.23</swagger-parser.version> <wiremock-spring-boot.version>3.6.0</wiremock-spring-boot.version> - <jsoup.version>1.18.3</jsoup.version> </properties> <dependencies> <!-- OZG-Cloud --> @@ -60,11 +59,6 @@ <artifactId>swagger-parser</artifactId> <version>${swagger-parser.version}</version> </dependency> - <dependency> - <groupId>org.jsoup</groupId> - <artifactId>jsoup</artifactId> - <version>${jsoup.version}</version> - </dependency> <!-- test --> <dependency> diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java index 5273da3..b6a5497 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java @@ -1,51 +1,9 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; -import java.util.List; -import java.util.function.Function; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; - public record Osi2HtmlDocument(String html) { - private record SpanRendering(String tag, Function<Element, String> markdownReplacement) { - static SpanRendering of(String tag, Function<Element, String> markdownReplacement) { - return new SpanRendering(tag, markdownReplacement); - } - - void replaceElementsWithMarkdownSpansIn(Document document) { - document.body() - .select(tag) - .replaceAll(anchor -> new Element("span") - .text(markdownReplacement.apply(anchor)) - ); - } - } - - static final List<SpanRendering> SPAN_RENDERINGS = List.of( - SpanRendering.of("a", anchor -> "[" + anchor.text() + "](" + anchor.attr("href") + ")"), - SpanRendering.of("img", image -> " + ")"), - SpanRendering.of("i", italic -> "_" + italic.text() + "_"), - SpanRendering.of("em", italic -> "_" + italic.text() + "_"), - SpanRendering.of("b", bold -> "__" + bold.text() + "__"), - SpanRendering.of("strong", bold -> "__" + bold.text() + "__"), - SpanRendering.of("h1", heading -> "# " + heading.text()), - SpanRendering.of("h2", heading -> "## " + heading.text()), - SpanRendering.of("h3", heading -> "### " + heading.text()), - SpanRendering.of("blockquote", bold -> "> " + bold.text()) - ); - public String renderToPlainText() { - var document = Jsoup.parse(html); - applySpanRenderings(document); - return document.wholeText(); - } - - private void applySpanRenderings(Document document) { - for (var entry : SPAN_RENDERINGS) { - entry.replaceElementsWithMarkdownSpansIn(document); - } + return html.replaceAll("<br/?>", "\n"); } public static String renderToPlainText(String html) { 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 aef50b4..0b20678 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 @@ -2,7 +2,6 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import java.time.ZoneOffset; -import org.jsoup.Jsoup; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; 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 e51f5ef..4df9e39 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 @@ -15,7 +15,7 @@ public class V1ReplyMessageTestFactory { Das ist das Multiline&<b>a</b><br><br/> Body"""; public static final String REPLY_BODY = """ - Das ist das Multiline&<b>a</b> + Das ist das Multiline&<b>a</b> Body"""; diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java index c4a928a..205145c 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java @@ -4,84 +4,15 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; class Osi2HtmlDocumentTest { @DisplayName("should render anchor link") @Test void shouldRenderAnchorLink() { - var plainText = renderPlainText("<a href=\"https://www.example.com\">Example</a>"); + var plainText = renderPlainText("<a href=\"https://www.example.com\">Example<br></a><br/>text"); - assertThat("[Example](https://www.example.com)").isEqualTo(plainText); - } - - @DisplayName("should render h1 heading") - @Test - void shouldRenderH1Heading() { - var plainText = renderPlainText("<h1>Example</h1>"); - - assertThat(plainText).isEqualTo("# Example"); - } - - @DisplayName("should render h2 heading") - @Test - void shouldRenderH2Heading() { - var plainText = renderPlainText("<h2>Example</h2>"); - - assertThat(plainText).isEqualTo("## Example"); - } - - @DisplayName("should render h3 heading") - @Test - void shouldRenderH3Heading() { - var plainText = renderPlainText("<h3>Example</h3>"); - - assertThat(plainText ).isEqualTo("### Example"); - } - - - @DisplayName("should render italic") - @ParameterizedTest - @ValueSource(strings = { "<i>Example</i>", "<em>Example</em>" }) - void shouldRenderItalic(String italicExample) { - var plainText = renderPlainText(italicExample); - - assertThat(plainText).isEqualTo("_Example_"); - } - - @DisplayName("should render bold") - @ParameterizedTest - @ValueSource(strings = { "<b>Example</b>", "<strong>Example</strong>" }) - void shouldRenderBold(String boldExample) { - var plainText = renderPlainText(boldExample); - - assertThat(plainText).isEqualTo("__Example__"); - } - - @DisplayName("should render image") - @Test - void shouldRenderImage() { - var plainText = renderPlainText("<img src=\"https://www.example.com/image.jpg\" alt=\"Example\">"); - - assertThat(plainText).isEqualTo(""); - } - - @DisplayName("should render blockquote") - @Test - void shouldRenderBlockquote() { - var plainText = renderPlainText("<blockquote><p>Example</p>\n<p>Example2</p></blockquote>"); - - assertThat(plainText).isEqualTo("> Example Example2"); - } - - @DisplayName("should render html") - @Test - void shouldRenderHtml() { - var plainText = renderPlainText("<p>Example😶</p><br/><p>Example2</p><b>"); - - assertThat(plainText).isEqualTo("Example\uD83D\uDE36\nExample2____"); + assertThat("<a href=\"https://www.example.com\">Example\n</a>\ntext").isEqualTo(plainText); } private String renderPlainText(String html) { -- GitLab From 0fe39d50a2e08531c85c10eca93e9daa5055f75c Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Mon, 3 Feb 2025 11:29:55 +0100 Subject: [PATCH 5/5] OZG-4095 html: Replace ampersands, only --- .../nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java | 2 +- .../postfach/osiv2/factory/V1ReplyMessageTestFactory.java | 4 +--- .../postfach/osiv2/transfer/Osi2HtmlDocumentTest.java | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java index b6a5497..b1fc132 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocument.java @@ -3,7 +3,7 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; public record Osi2HtmlDocument(String html) { public String renderToPlainText() { - return html.replaceAll("<br/?>", "\n"); + return html.replace("&", "&"); } public static String renderToPlainText(String html) { 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 4df9e39..84fec5a 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 @@ -15,9 +15,7 @@ public class V1ReplyMessageTestFactory { Das ist das Multiline&<b>a</b><br><br/> Body"""; public static final String REPLY_BODY = """ - Das ist das Multiline&<b>a</b> - - + Das ist das Multiline&<b>a</b><br><br/> Body"""; private static final String DISPLAY_NAME = "Das ist der Absender"; private static final String ORIGIN_SENDER = "das ist der original Sender"; diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java index 205145c..2e0a7b6 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2HtmlDocumentTest.java @@ -10,9 +10,9 @@ class Osi2HtmlDocumentTest { @DisplayName("should render anchor link") @Test void shouldRenderAnchorLink() { - var plainText = renderPlainText("<a href=\"https://www.example.com\">Example<br></a><br/>text"); + var plainText = renderPlainText("<a href=\"https://www.example.com\">&Example<br></a><br/>text"); - assertThat("<a href=\"https://www.example.com\">Example\n</a>\ntext").isEqualTo(plainText); + assertThat("<a href=\"https://www.example.com\">&Example<br></a><br/>text").isEqualTo(plainText); } private String renderPlainText(String html) { -- GitLab