Skip to content
Snippets Groups Projects
Commit 13bab259 authored by OZG-Cloud Team's avatar OZG-Cloud Team
Browse files

Merge pull request 'OZG-4367 Umlaute-in-Feldnamen-aus-FC-uebertragen' (#100)...

Merge pull request 'OZG-4367 Umlaute-in-Feldnamen-aus-FC-uebertragen' (#100) from OZG-4367-Umlaute-in-Feldnamen-aus-FC-uebertragen into master

Reviewed-on: https://git.ozg-sh.de/ozgcloud-app/eingang-manager/pulls/100
parents 0e0c72c5 da41288b
No related branches found
No related tags found
No related merge requests found
......@@ -38,19 +38,19 @@
<properties>
<formcycle-interface.version>${project.version}</formcycle-interface.version>
<jsoup.version>1.17.2</jsoup.version>
</properties>
<dependencies>
<!--own project-->
<dependency>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-utils</artifactId>
<groupId>de.ozgcloud.eingang</groupId>
<artifactId>formcycle-adapter-interface</artifactId>
<version>${formcycle-interface.version}</version>
</dependency>
<dependency>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-utils</artifactId>
<type>test-jar</type>
<scope>test</scope>
<groupId>de.ozgcloud.eingang</groupId>
<artifactId>semantik-adapter</artifactId>
</dependency>
<dependency>
<groupId>de.ozgcloud.eingang</groupId>
......@@ -58,6 +58,16 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-utils</artifactId>
</dependency>
<dependency>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-utils</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<!--spring-->
<dependency>
......@@ -69,15 +79,10 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.ozgcloud.eingang</groupId>
<artifactId>formcycle-adapter-interface</artifactId>
<version>${formcycle-interface.version}</version>
</dependency>
<dependency>
<groupId>de.ozgcloud.eingang</groupId>
<artifactId>semantik-adapter</artifactId>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
</dependencies>
......
......@@ -65,6 +65,7 @@ class FormDataController {
private final FormCycleFormDataMapper mapper;
private final SemantikAdapter semantikAdapter;
private final VorgangNummerSupplier vorgangNummerSupplier;
private final FormDataHtmlCleaner formDataHtmlCleaner;
@PostMapping(consumes = "multipart/form-data", produces = HTTP_TYPE_PROTOBUF)
public FormCycleConfirmationResponse receiveFormData(@RequestPart FormCycleFormData formData,
......@@ -72,6 +73,7 @@ class FormDataController {
@RequestPart(required = false) Optional<Collection<MultipartFile>> attachments) {
FormData mappedFormData = mapper.toFormData(formData);
mappedFormData = formDataHtmlCleaner.clean(mappedFormData);
mappedFormData = addRepresentations(representations, mappedFormData);
mappedFormData = addFiles(formData, attachments, mappedFormData);
mappedFormData = addServiceKonto(formData, mappedFormData);
......
/*
* Copyright (C) 2023 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.eingang.formcycle;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Safelist;
import org.springframework.stereotype.Component;
import de.ozgcloud.eingang.common.formdata.FormData;
@Component
public class FormDataHtmlCleaner {
public FormData clean(FormData formData) {
return formData.toBuilder().formData(cleanFormData(formData.getFormData())).build();
}
Map<String, Object> cleanFormData(Map<String, Object> formData) {
return formData.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> cleanValue(entry.getValue())));
}
@SuppressWarnings("unchecked")
Object cleanValue(Object value) {
if (value instanceof Map) {
return cleanFormData((Map<String, Object>) value);
} else if (value instanceof Collection<?> values) {
return values.stream().map(this::cleanValue).toList();
} else if (value instanceof String valueString) {
return parseHtml(valueString);
}
return value;
}
Object parseHtml(String html) {
var jsoupDocument = Jsoup.parse(html);
var outputSettings = new Document.OutputSettings(); // keep new lines
outputSettings.prettyPrint(false);
jsoupDocument.outputSettings(outputSettings);
var innerHtml = jsoupDocument.html().replace("\\\\n", "\n");
return Jsoup.clean(innerHtml, "", Safelist.none(), outputSettings);
}
}
......@@ -71,6 +71,8 @@ class FormDataControllerTest {
private SemantikAdapter semantikAdapter;
@Mock
private VorgangNummerSupplier vorgangNummerSupplier;
@Mock
private FormDataHtmlCleaner htmlCleaner;
private MockMvc mockMvc;
......@@ -93,6 +95,7 @@ class FormDataControllerTest {
@BeforeEach
void init() {
when(htmlCleaner.clean(any())).thenReturn(mappedFormData);
when(mapper.toFormData(any())).thenReturn(mappedFormData);
when(vorgangNummerSupplier.get()).thenReturn(VORGANG_NUMMER);
}
......@@ -111,7 +114,7 @@ class FormDataControllerTest {
@Test
@SneakyThrows
void shouldRespondeWithVorgangNummer() {
void shouldResponseWithVorgangNummer() {
var confirmation = FormCycleConfirmationResponse.parseFrom(
doPostRequest().andReturn().getResponse().getContentAsByteArray());
......@@ -127,6 +130,13 @@ class FormDataControllerTest {
assertThat(formDataCaptor.getValue().getHeader().getRequestId()).isEqualTo(VORGANG_NUMMER);
}
@Test
void shouldCallHtmlCleaner() {
doPostRequest();
verify(htmlCleaner).clean(any());
}
@Test
void shouldCallMapper() {
doPostRequest();
......@@ -157,8 +167,9 @@ class FormDataControllerTest {
@BeforeEach
void init() {
when(mapper.toFormData(any()))
.thenReturn(FormDataTestFactory.createBuilder().clearRepresentations().numberOfRepresentations(0).build());
var formData = FormDataTestFactory.createBuilder().clearRepresentations().numberOfRepresentations(0).build();
when(mapper.toFormData(any())).thenReturn(formData);
when(htmlCleaner.clean(any())).thenReturn(formData);
}
@Test
......@@ -195,8 +206,9 @@ class FormDataControllerTest {
class Attachments {
@BeforeEach
void init() {
when(mapper.toFormData(any()))
.thenReturn(FormDataTestFactory.createBuilder().clearAttachments().numberOfAttachments(0).build());
var formData = FormDataTestFactory.createBuilder().clearAttachments().numberOfAttachments(0).build();
when(mapper.toFormData(any())).thenReturn(formData);
when(htmlCleaner.clean(any())).thenReturn(formData);
}
@Test
......
/*
* Copyright (C) 2023 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.eingang.formcycle;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Map;
import org.assertj.core.data.MapEntry;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import de.ozgcloud.eingang.common.formdata.FormData;
class FormDataHtmlCleanerTest {
private static final String KEY = "key";
private static final Object VALUE = "value";
@Spy
@InjectMocks
private FormDataHtmlCleaner cleaner;
@Nested
class TestClean {
private final static Map<String, Object> FORM_DATA_MAP = Map.of(KEY, VALUE);
private final static FormData FORM_DATA = FormData.builder().formData(FORM_DATA_MAP).build();
@Test
void shouldCallCleanFormData() {
cleaner.clean(FORM_DATA);
verify(cleaner).cleanFormData(FORM_DATA_MAP);
}
@Test
void shouldSetCleanedFormData() {
var cleanedFormData = Map.of(KEY, VALUE);
doReturn(cleanedFormData).when(cleaner).cleanFormData(anyMap());
var result = cleaner.clean(FORM_DATA);
assertThat(result.getFormData()).isSameAs(cleanedFormData);
}
}
@Nested
class TestCleanFormData {
@Test
void shouldCallCleanValue() {
cleaner.cleanFormData(Map.of(KEY, VALUE));
verify(cleaner).cleanValue(VALUE);
}
@Test
void shouldReturnCleanedMap() {
var cleanedValue = "noHtml";
doReturn(cleanedValue).when(cleaner).cleanValue(any());
var result = cleaner.cleanFormData(Map.of(KEY, VALUE));
assertThat(result).containsOnly(MapEntry.entry(KEY, cleanedValue));
}
}
@Nested
class TestCleanValue {
@Nested
class TestCleanMap {
@Test
void shouldCallCleanFormData() {
var expectedMap = Map.of(KEY, VALUE);
cleaner.cleanValue(expectedMap);
verify(cleaner).cleanFormData(expectedMap);
}
@Test
void shouldReturnValue() {
var expectedMap = Map.of(KEY, VALUE);
doReturn(expectedMap).when(cleaner).cleanFormData(anyMap());
var result = cleaner.cleanValue(Map.of("a", "b"));
assertThat(result).isSameAs(expectedMap);
}
}
@Nested
class TestCleanCollection {
@Test
void shouldCallCleanValue() {
cleaner.cleanValue(List.of(VALUE));
verify(cleaner).cleanValue(VALUE);
}
@Test
void shouldReturnValue() {
doReturn(List.of(VALUE)).when(cleaner).cleanValue(any());
var result = cleaner.cleanValue(List.of("a"));
assertThat(result).isInstanceOf(List.class).asList().containsExactly(VALUE);
}
}
@Nested
class TestCleanString {
@Test
void shouldCallParseHtml() {
var stringValue = VALUE.toString();
cleaner.cleanValue(VALUE);
verify(cleaner).parseHtml(stringValue);
}
@Test
void shouldReturnValue() {
var cleanedValue = "noHtml";
doReturn(cleanedValue).when(cleaner).parseHtml(anyString());
var result = cleaner.cleanValue(VALUE);
assertThat(result).isEqualTo(cleanedValue);
}
}
@Test
void shouldReturnUnmodifiedValue() {
var value = 1;
var result = cleaner.cleanValue(value);
assertThat(result).isEqualTo(1);
verify(cleaner, never()).parseHtml(any());
}
}
@Nested
class TestHtmlCleaner {
static final String KEY_LABEL = "label";
static final String KEY_VALUE = "value";
static final Map<String, Object> FORM_DATA_MAP = Map.of("tf1", Map.of(
KEY_LABEL, "<p><em>&Auml;</em></p>",
KEY_VALUE, "Ä - Wert"),
"tf2", Map.of(
KEY_LABEL, "<p><strong>&Ouml;</strong></p>",
KEY_VALUE, "Ö - Wert"),
"fs1", Map.of(
KEY_LABEL, "Ü",
KEY_VALUE, Map.of(
"tf3", Map.of(
KEY_LABEL, " <p><s>Label mit</s> &szlig;</p>",
KEY_VALUE, "ein Text mit ß und <html><body><h1>Hello</h1><body><html>")),
"tf4", Map.of(
KEY_LABEL, "<p><span style=\"background-color:#1abc9c;\">&auml;</span></p>",
KEY_VALUE, "Text"),
"ed1", Map.of(
KEY_LABEL,
"<ol>\n\t<li><em><strong><u>&ouml;</u></strong></em></li>\n\t<li><span style=\"color:#e74c3c;\">&uuml;</span></li>\n</ol>",
KEY_VALUE, "TExt\nmit\n Leerzeichen\nund\n Umbrüchen"
)));
static final Map<String, Object> EXPECTED_MAP = Map.of("tf1", Map.of(
KEY_LABEL, "Ä",
KEY_VALUE, "Ä - Wert"),
"tf2", Map.of(
KEY_LABEL, "Ö",
KEY_VALUE, "Ö - Wert"),
"fs1", Map.of(
KEY_LABEL, "Ü",
KEY_VALUE, Map.of(
"tf3", Map.of(
KEY_LABEL, "Label mit ß",
KEY_VALUE, "ein Text mit ß und Hello")),
"tf4", Map.of(
KEY_LABEL, "ä",
KEY_VALUE, "Text"),
"ed1", Map.of(
KEY_LABEL,
"\n\tö\n\tü\n",
KEY_VALUE, "TExt\nmit\n Leerzeichen\nund\n Umbrüchen"
)));
@Test
void shouldCleanHtml() {
var result = cleaner.clean(FormData.builder().formData(FORM_DATA_MAP).build());
assertThat(result.getFormData()).isEqualTo(EXPECTED_MAP);
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment