Skip to content
Snippets Groups Projects
Commit a8762fe5 authored by OZGCloud's avatar OZGCloud
Browse files

Merge branch 'refs/heads/master' into OZG-5987-SubCommands-Revoke-on-fail

# Conflicts:
#	vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java
#	vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java
#	vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceITCase.java
#	vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java
parents 84d23ca3 5e5a373c
Branches
Tags
No related merge requests found
Showing
with 839 additions and 122 deletions
......@@ -12,7 +12,7 @@
<groupId>de.ozgcloud.bescheid</groupId>
<artifactId>bescheid-manager</artifactId>
<name>OZG-Cloud Bescheid Manager</name>
<version>1.14.0-SNAPSHOT</version>
<version>1.15.0-SNAPSHOT</version>
<properties>
<vorgang-manager.version>2.9.0</vorgang-manager.version>
......
......@@ -20,6 +20,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.w3c.dom.Document;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
......@@ -55,6 +56,9 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService {
private static final String FILE_TYPE_PDF = "PDF";
private static final String FILE_TYPE_XML = "XML";
static final String NACHRICHTEN_TEXT_EXPRESSION = "/root/SmartDocument/Fields/NachrichtenText/text()"; // NOSONAR
static final String FIELD_TEMPLATE_EXPRESSION = "/root/SmartDocument/Fields/Field[@ID=\"Template.56E7AA0956C7486292E9A02114CB231C\"]/text()"; //NOSONAR
@SuppressWarnings("deprecation") // SD requires forced UTF-8 encoding
private static final MediaType JSON_MEDIA_TYPE_FOR_SD = MediaType.APPLICATION_JSON_UTF8;
......@@ -116,31 +120,48 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService {
}
Optional<String> getNachrichtText(SmartDocumentsResponse response) {
return getXMLFile(response).flatMap(this::extractTextFormXmlFile);
return getXMLFile(response).flatMap(this::extractTextFromXmlFile);
}
Optional<String> extractTextFormXmlFile(File xmlFile) {
Optional<String> extractTextFromXmlFile(File xmlFile) {
try {
return doExtractText(xmlFile);
} catch (XPathExpressionException | SAXException | IOException | ParserConfigurationException e) {
LOG.warn("XML-Parsing error on extracting Nachricht-Text: {}", e.getMessage(), e);
} catch (ClassCastException e) {
LOG.warn("Error on extraction Nachricht-Text. XPath return unexpected Type.", e);
var document = parseXml(xmlFile);
if (document.isPresent()) {
return document.flatMap(this::getNachrichtenText).or(() -> getFieldTemplateText(document.get())).map(Text::getTextContent);
}
} catch (RuntimeException e) {
LOG.warn("Unexpected Error on extracting NachrichtText: {}", e.getMessage(), e);
}
return Optional.empty();
}
Optional<String> doExtractText(File xmlFile)
throws SAXException, IOException, ParserConfigurationException, XPathExpressionException {
var xPath = XPathFactory.newInstance().newXPath();
Optional<Document> parseXml(File xmlFile) {
try {
return Optional.of(DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(xmlFile));
} catch (SAXException | IOException | ParserConfigurationException e) {
LOG.warn("XML-Parsing error on extracting Nachricht-Text: {}", e.getMessage(), e);
}
return Optional.empty();
}
Optional<Text> getNachrichtenText(Document document) {
return evaluateXPath(document, NACHRICHTEN_TEXT_EXPRESSION);
}
var document = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(xmlFile);
var expr = xPath.compile("/root/SmartDocument/Fields/NachrichtenText/text()");
Optional<Text> getFieldTemplateText(Document document) {
return evaluateXPath(document, FIELD_TEMPLATE_EXPRESSION);
}
return Optional.ofNullable((Text) expr.evaluate(document, XPathConstants.NODE))
.map(Text::getTextContent);
Optional<Text> evaluateXPath(Document document, String xpathExpression) {
try {
var expr = XPathFactory.newInstance().newXPath().compile(xpathExpression);
return Optional.ofNullable(expr.evaluate(document, XPathConstants.NODE)).map(Text.class::cast);
} catch (ClassCastException e) {
LOG.warn("Error on extraction Nachricht-Text. XPath return unexpected Type.", e);
} catch (XPathExpressionException e) {
LOG.warn("Cannot evaluate XPath: {}", xpathExpression, e);
}
return Optional.empty();
}
Optional<File> getXMLFile(SmartDocumentsResponse response) {
......
package de.ozgcloud.bescheid;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.authentication.AuthenticationTrustResolver;
......@@ -10,6 +12,9 @@ import org.springframework.security.authentication.AuthenticationTrustResolverIm
@ComponentScan({ "de.ozgcloud.*" })
public class BescheidTestApplication {
@MockBean
private BuildProperties buildProperties;
@Bean
AuthenticationTrustResolver trustResolver() {
return new AuthenticationTrustResolverImpl();
......
......@@ -6,7 +6,6 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.test.mock.mockito.MockBean;
import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
......@@ -21,8 +20,6 @@ class TemplateHandlerITCase {
@Autowired
private TemplateHandler handler;
@MockBean
private BuildProperties properties;
@MockBean
private OzgCloudCommandService commandService;
@MockBean
private CommandMapper commandMapper;
......
......@@ -6,7 +6,6 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.test.mock.mockito.MockBean;
import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
......@@ -23,8 +22,6 @@ class NachrichtServiceITCase {
private OzgCloudCommandService commandService;
@MockBean
private CommandMapper commandMapper;
@MockBean
private BuildProperties buildProperties;
@DisplayName("Build message")
@Nested
......
......@@ -2,7 +2,10 @@ package de.ozgcloud.bescheid.smartdocuments;
import static org.assertj.core.api.Assertions.*;
import java.io.File;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
......@@ -14,9 +17,10 @@ import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
import de.ozgcloud.bescheid.BescheidRequestTestFactory;
import de.ozgcloud.bescheid.BescheidTestApplication;
import de.ozgcloud.bescheid.vorgang.VorgangTestFactory;
import de.ozgcloud.common.binaryfile.TempFileUtils;
import de.ozgcloud.common.test.ITCase;
import de.ozgcloud.common.test.TestUtils;
@Disabled
@SpringBootTest(classes = BescheidTestApplication.class)
@ITCase
@ActiveProfiles({ "itcase", "local" })
......@@ -29,6 +33,7 @@ class SmartDocumentsBescheidRemoteServiceITCase {
@MockBean
private CommandMapper commandMapper;
@Disabled("This test request SmartDocuments service")
@Test
void createBescheid() {
var bescheid = remoteService.create(BescheidRequestTestFactory.create(), VorgangTestFactory.create());
......@@ -38,4 +43,37 @@ class SmartDocumentsBescheidRemoteServiceITCase {
assertThat(bescheid.getBescheidFileName()).isNotEmpty();
}
@Nested
class TestGetNachrichtenText {
private static final String NACHRICHTEN_TEXT = "Nachrichtentext";
private static final String FIELD_TEMPLATE_TEXT = "Nachrichten Field Text";
@Test
void shouldGetNachrichtenText() {
var nachrichtenText = remoteService.extractTextFromXmlFile(loadFile("SD_answer.xml"));
assertThat(nachrichtenText).contains(NACHRICHTEN_TEXT);
}
@Test
void shouldGetFieldTemplateText() {
var fieldTemplateText = remoteService.extractTextFromXmlFile(loadFile("SD_answer_field_template.xml"));
assertThat(fieldTemplateText).contains(FIELD_TEMPLATE_TEXT);
}
@Test
void shouldExpectMissingTextNode() {
File xmlFileWithoutText = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer_without_text.xml"));
var text = remoteService.extractTextFromXmlFile(xmlFileWithoutText);
assertThat(text).isEmpty();
}
private File loadFile(String fileName) {
return TempFileUtils.writeTmpFile(TestUtils.loadFile(fileName));
}
}
}
......@@ -7,19 +7,20 @@ import static org.mockito.Mockito.*;
import java.io.File;
import java.util.Optional;
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 org.w3c.dom.Document;
import org.w3c.dom.Text;
import de.ozgcloud.bescheid.BescheidRequestTestFactory;
import de.ozgcloud.bescheid.BescheidTestFactory;
import de.ozgcloud.bescheid.common.user.UserProfileTestFactory;
import de.ozgcloud.bescheid.smartdocuments.SmartDocumentsRequest.CustomerData.UserData;
import de.ozgcloud.bescheid.vorgang.VorgangTestFactory;
import de.ozgcloud.common.binaryfile.TempFileUtils;
import de.ozgcloud.common.test.TestUtils;
import lombok.SneakyThrows;
class SmartDocumentsBescheidRemoteServiceTest {
......@@ -30,6 +31,10 @@ class SmartDocumentsBescheidRemoteServiceTest {
@Mock
private SmartDocumentsProperties properties;
@Mock
private Text xmlText;
@Mock
private File xmlFile;
@Nested
class TestBuildBescheid {
......@@ -57,7 +62,15 @@ class SmartDocumentsBescheidRemoteServiceTest {
@Nested
class TestGetNachrichtText {
private final File xmlFile = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer.xml"));
@Test
void shouldCallGetXMLFile() {
var response = SmartDocumentsResponseTestFactory.create();
service.getNachrichtText(response);
verify(service).getXMLFile(response);
}
@Test
void shouldCallExtractText() {
......@@ -65,22 +78,94 @@ class SmartDocumentsBescheidRemoteServiceTest {
service.getNachrichtText(SmartDocumentsResponseTestFactory.create());
verify(service).extractTextFormXmlFile(notNull());
verify(service).extractTextFromXmlFile(xmlFile);
}
}
@Nested
class ExtractingText {
class TestExtractTextFromXmlFileSuccessfully {
private static final String NACHRICHT_TEXT = "NachrichtText";
@Mock
private Document document;
@SneakyThrows
@BeforeEach
void init() {
doReturn(Optional.of(document)).when(service).parseXml(any());
}
@SneakyThrows
@Test
void shouldReturnText() {
var text = service.extractTextFormXmlFile(xmlFile);
void shouldCallParseXml() {
service.extractTextFromXmlFile(xmlFile);
verify(service).parseXml(xmlFile);
}
@Test
void shouldCallGetNachrichtenText() {
doReturn(Optional.of(xmlText)).when(service).getNachrichtenText(any());
service.extractTextFromXmlFile(xmlFile);
assertThat(text).isPresent().get().isNotNull();
verify(service).getNachrichtenText(document);
}
@Test
void shouldReturnNachrichtenText() {
doReturn(Optional.of(xmlText)).when(service).getNachrichtenText(any());
when(xmlText.getTextContent()).thenReturn(NACHRICHT_TEXT);
var text = service.extractTextFromXmlFile(xmlFile);
assertThat(text).contains(NACHRICHT_TEXT);
}
@Test
void shouldNotCallGetFieldTemplateText() {
doReturn(Optional.of(xmlText)).when(service).getNachrichtenText(any());
service.extractTextFromXmlFile(xmlFile);
verify(service, never()).getFieldTemplateText(document);
}
@Test
void shouldCallGetFieldTemplateText() {
doReturn(Optional.empty()).when(service).getNachrichtenText(any());
doReturn(Optional.of(xmlText)).when(service).getFieldTemplateText(any());
service.extractTextFromXmlFile(xmlFile);
verify(service).getFieldTemplateText(document);
}
@Test
void shouldReturnFieldTemplateText() {
doReturn(Optional.empty()).when(service).getNachrichtenText(any());
doReturn(Optional.of(xmlText)).when(service).getFieldTemplateText(any());
when(xmlText.getTextContent()).thenReturn(NACHRICHT_TEXT);
var text = service.extractTextFromXmlFile(xmlFile);
assertThat(text).contains(NACHRICHT_TEXT);
}
}
@Nested
class TestExtractTextFormXmlFileFails {
@Mock
private Document document;
@SneakyThrows
@Test
void shouldHandleError() {
var text = service.extractTextFormXmlFile(null);
void shouldHandleMissingXmlDocument() {
doReturn(Optional.empty()).when(service).parseXml(any());
var text = service.extractTextFromXmlFile(xmlFile);
assertThat(text).isEmpty();
}
......@@ -88,13 +173,76 @@ class SmartDocumentsBescheidRemoteServiceTest {
@Test
@SneakyThrows
void shouldExpectMissingTextNode() {
File xmlFileWithoutText = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer_without_text.xml"));
doReturn(Optional.of(document)).when(service).parseXml(any());
doReturn(Optional.empty()).when(service).getFieldTemplateText(any());
var text = service.doExtractText(xmlFileWithoutText);
var text = service.extractTextFromXmlFile(xmlFile);
assertThat(text).isEmpty();
}
}
@Nested
class TestGetNachrichtenText {
@Mock
private Text text;
@Mock
private Document document;
@BeforeEach
void init() {
doReturn(Optional.of(text)).when(service).evaluateXPath(any(), any());
}
@Test
void shouldCallEvaluatePath() {
getNachrichtenText();
verify(service).evaluateXPath(document, SmartDocumentsBescheidRemoteService.NACHRICHTEN_TEXT_EXPRESSION);
}
@Test
void shouldReturnText() {
var result = getNachrichtenText();
assertThat(result).contains(text);
}
private Optional<Text> getNachrichtenText() {
return service.getNachrichtenText(document);
}
}
@Nested
class TestGetFieldTemplateText {
@Mock
private Text text;
@Mock
private Document document;
@BeforeEach
void init() {
doReturn(Optional.of(text)).when(service).evaluateXPath(any(), any());
}
@Test
void shouldCallEvaluatePath() {
getFieldTemplateText();
verify(service).evaluateXPath(document, SmartDocumentsBescheidRemoteService.FIELD_TEMPLATE_EXPRESSION);
}
@Test
void shouldReturnText() {
var result = getFieldTemplateText();
assertThat(result).contains(text);
}
private Optional<Text> getFieldTemplateText() {
return service.getFieldTemplateText(document);
}
}
}
......@@ -258,13 +258,7 @@
<Field ID="FEC37A441C9D46189533DBD9CFD0E0E8"><![CDATA[STEUBER]]></Field>
<Field ID="FECCC52ABB614390BAE2577AD8C44262"><![CDATA[24103]]></Field>
<Field ID="root/CustomerData/Dateiname"><![CDATA[Fleet_9-Verk-AO_01-08_06.12.2023-06.12.2023]]></Field>
<NachrichtenText>Sehr geehrte/r Antragstellende,
anliegend erhalten Sie die verkehrsrechtliche Anordnung für die Aufstellung der von Ihnen beantragten Beschilderung. Die Prüfung der Straßenverkehrsbehörde bezieht sich nur auf die Vollständigkeit des Antrages und die Plausibilität der Angaben. Die richtige Auswahl der aufzustellenden Verkehrszeichen oder Überschneidung mit anderen Anträgen wurde dabei nicht geprüft.
BITTE DRINGEND BEACHTEN
Der Antrag einer mobilen Haltverbotszone ist MINDESTENS 1 Woche vor Aufstellung der Beschilderung zu stellen. Des Weiteren gilt die Beschilderung erst als angeordnet, wenn die Antragstellenden das von der Straßenverkehrsbehörde unterschriebene Anmeldeformular zurückerhalten haben (Fax, E-Mail, Post). Vorab sind keine Schilder aufzustellen!!!
</NachrichtenText>
<NachrichtenText>Nachrichtentext</NachrichtenText>
</Fields>
<DocumentProperties>
<Guid>7ACEA5AE7C3642978ACCC48182EBCD6E</Guid>
......
<?xml version='1.0' encoding='UTF-8'?><!--
~ 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.
-->
<root>
<SmartDocument Version="2.0">
<Selection>
<TypistID>072D2DBF07C049858DFB716BDBAAE83A</TypistID>
<UserGroupID>
DA9D309EF1374D8A920CC9878DD3D629
</UserGroupID>
<AuthorID>072D2DBF07C049858DFB716BDBAAE83A
</AuthorID>
<TemplateGroupID>94A1C1208DF0479CB58E3CFB0A6057BC</TemplateGroupID>
<Template>
KFAS_LIVE_KI_10_Haltverbot_befristet
</Template>
<TemplateID VersionID="87731D1F4AB74952979B58497D95EA86">
7ACEA5AE7C3642978ACCC48182EBCD6E
</TemplateID>
<Blocks>
<Block VersionID="15DCCB4DFE1A414796EBA44352C7AAFF">
CC2A9C0E14164B9A967CEE8DCD41B3B5
</Block>
<Placeholders>
<Placeholder ID="125E766AF0424D4992F8F04C806F75EB">
CC2A9C0E14164B9A967CEE8DCD41B3B5
</Placeholder>
</Placeholders>
</Blocks>
<FixedValues/>
</Selection>
<Variables/>
<QuestionAnswers>
<Question ID="07BE1279C0504852AEFBB264D165E139" Description="Tagesdatum" OriginalValue="2024-06-25">
25.06.2024
</Question>
<Question ID="23BB945F7BA44321B09A0A984D541DD1" Description="Tage Halteverbot"
ContentId="23BB945F7BA44321B09A0A984D541DD1" IsFormatted="true" ContentType="XML">
<Records>
<Record>
<Field Name="71F5F35C4FD94982A1F8321A73E9B593">
<Value>nein</Value>
<QuestionAnswers>
<Question ID="58F6E456AE1F4E13907D78E158BF4847" Description="Freitag">
<Answer ID="313DC774D89E45D8864FC33983C95923" Description="Nein" IsFormatted="true"
ContentId="313DC774D89E45D8864FC33983C95923"/>
</Question>
</QuestionAnswers>
</Field>
<Field Name="B66C9D21D31A4293852EEDCAB57C593B">
<Value>ja</Value>
<QuestionAnswers>
<Question ID="EF33891FF01A42B0BEFB1E78E3FC023D" Description="Samstag">
<Answer ID="F22F6FE074784CCD950430166F42B33F" Description="Ja" IsFormatted="true"
ContentId="F22F6FE074784CCD950430166F42B33F"/>
</Question>
</QuestionAnswers>
</Field>
<Field Name="67082AAB675E457180A7014531C46CB3">
<Value>ja</Value>
<QuestionAnswers>
<Question ID="60C6CD1F29084A26BEC493A862583AAB" Description="Montag">
<Answer ID="8EAE05F9D89C46AE8CCB08A24104CC17" Description="Ja" IsFormatted="true"
ContentId="8EAE05F9D89C46AE8CCB08A24104CC17"/>
</Question>
</QuestionAnswers>
</Field>
<Field Name="3FD23F8F98EF459A95B50448DEDFF20E">
<Value>ja</Value>
<QuestionAnswers>
<Question ID="C8BC6BFFE8BE4E6BBDC4CBCD34377DB2" Description="Mittwoch">
<Answer ID="97B676CAC9B74D00B574F319B2D20BB5" Description="Ja" IsFormatted="true"
ContentId="97B676CAC9B74D00B574F319B2D20BB5"/>
</Question>
</QuestionAnswers>
</Field>
<Field Name="09658B4DA4BB4295822A42C0AA8FC2EC">
<Value>nein</Value>
<QuestionAnswers>
<Question ID="39BF39565D4043078ABEEC9601A091E7" Description="Donnerstag">
<Answer ID="8D4A19B4D3AF448FB720E9160E7A40D2" Description="Nein" IsFormatted="true"
ContentId="8D4A19B4D3AF448FB720E9160E7A40D2"/>
</Question>
</QuestionAnswers>
</Field>
<Field Name="975867EBE97547ACA6B8D85F1D8D39C8">
<Value>nein</Value>
<QuestionAnswers>
<Question ID="03906ABB93494C6EB087D87C4C70217C" Description="Dienstag">
<Answer ID="B9ACD0E74C6B41BEA700C9EB8FCFFC25" Description="Nein" IsFormatted="true"
ContentId="B9ACD0E74C6B41BEA700C9EB8FCFFC25"/>
</Question>
</QuestionAnswers>
</Field>
<Field Name="AF89873144744EDC9A1B78776F3D25F5">
<Value>nein</Value>
<QuestionAnswers>
<Question ID="06349259221C4DEFBB4FE9FD33D76403" Description="Sonntag">
<Answer ID="853ED74B536C4BD8AF95193450F18201" Description="Nein" IsFormatted="true"
ContentId="853ED74B536C4BD8AF95193450F18201"/>
</Question>
</QuestionAnswers>
</Field>
</Record>
</Records>
</Question>
<Question ID="28CB094DAD1A4604B08CE7C5D2177C03" Description="Beschilderung">
<Answer ID="605F18E32DE04FF2B058BD66F2DE9C06" Description="Nein" IsFormatted="true"
ContentId="605F18E32DE04FF2B058BD66F2DE9C06"/>
</Question>
<Question ID="736372FDB0A2407CBBFB15DE71FD1450" Description="Geschlecht Antragssteller">
<Answer ID="CCFD78217BC84D339F582F4B5703A7B6" Description="keine Angabe" IsFormatted="true"
ContentId="CCFD78217BC84D339F582F4B5703A7B6"/>
</Question>
<Question ID="97E309BDBDB548F48D162D289056B313" Description="Uhrzeit Halteverbot">
<Answer ID="E45BE1342D5F43D59E21986C377DA1B0" Description="ganztägig" IsFormatted="true"
ContentId="E45BE1342D5F43D59E21986C377DA1B0"/>
</Question>
<Question ID="992F241A1DF4497A8B4568E69B9C567C" Description="Vor Ort geltende Parkregelung">
<Answer ID="9875A84F77164A9C88426FD6A01D784F" Description="Parken am Fahrbahnrand" IsFormatted="true"
ContentId="9875A84F77164A9C88426FD6A01D784F"/>
</Question>
<Question ID="9BD81FB0E5FF4235A094BE3D21544615" Description="Name Ansprechperson / Firma"
AlternativeParentID="736372FDB0A2407CBBFB15DE71FD1450">
<Answer ID="FC9E396BAA894B52A475BCE1184230D9" Description="keine Angabe" IsFormatted="true"
ContentId="FC9E396BAA894B52A475BCE1184230D9"/>
</Question>
<Question ID="9CE081D9122E43B1B778863AE32AB29F" Description="Protokoll">
<Answer ID="8490AF83642D4D26A408A2C431F52183" Description="Nein" IsFormatted="true"
ContentId="8490AF83642D4D26A408A2C431F52183"/>
</Question>
<Question ID="B651AC699A4F4B768DC34DE87BBD578C" Description="Gründe für Halteverbot"
ContentId="B651AC699A4F4B768DC34DE87BBD578C" IsFormatted="true" ContentType="XML">
<Records/>
</Question>
<Question ID="C33A6D64E1B542CB97520C1F32D18C35" Description="Unterschrift">
<Answer ID="68A02377AE9D473C9F6BAFF4EF150DE5" Description="Reusche" IsFormatted="true"
ContentId="68A02377AE9D473C9F6BAFF4EF150DE5"/>
</Question>
<Question ID="C7C9CCBF9633499EAB08D1BBB84BFD98" Description="Richtigkeit">
<Answer ID="6DF6B7C6A88D4F2282B8366027B0032D" Description="Nein" IsFormatted="true"
ContentId="6DF6B7C6A88D4F2282B8366027B0032D"/>
</Question>
<Question ID="C7D1A20B17CD4C96BF55A2738B8A4631" Description="Kenntnisnahme">
<Answer ID="50BBC8449BEB4AFEB774AC706F1D66B9" Description="Nein" IsFormatted="true"
ContentId="50BBC8449BEB4AFEB774AC706F1D66B9"/>
</Question>
</QuestionAnswers>
<Fields>
<Field ID="3C5C7920FB444A9DB2F5239339599BD7"/>
<Field ID="41237CC02584421A8D65990E7F78F833"/>
<Field ID="423CB6F4AEB54A44BC67F97626ADC06C"/>
<Field ID="442D48B946C64853B78DB99D9341AAC8">16:23</Field>
<Field ID="4D4178B6F808409391990233D33BF54C"/>
<Field ID="5AE5217E97F845B48D4274894BC2F043">03:25</Field>
<Field ID="635CEB0B41DE44EFB2207122CCB8EE1D"/>
<Field ID="6B06CD242FC64865BFC5B770BB03EAA4"/>
<Field ID="769E3AC926154741AABE870EB802E2AD"/>
<Field ID="7AF097E4E741455685CD543ABB7CE337"/>
<Field ID="8CA72E11F95F4352AE6FB8001BE79508">25.05.2023</Field>
<Field ID="8D38662ADBF2408B8C8DDB5294A5A5A9"/>
<Field ID="CA36A227CFEC4B89A40CA8B0CF4460C0">23</Field>
<Field ID="CC330D642D7D465FA611D029F6A06344">KFAS_LIVE_KI_10_Haltverbot_befristet-JhQDdq2F</Field>
<Field ID="D316B8AE41A3496495C058CCB3766F3A"/>
<Field ID="DB270EC247D5476A9D8BFA008E872D7D">Kiel</Field>
<Field ID="DE7F7E00C8C246AEA9FFDF7F89E2FB03">05.05.2023</Field>
<Field ID="F1826BE2F3D04E508F6401E8AA1D08C1"/>
<Field ID="FEC37A441C9D46189533DBD9CFD0E0E8"/>
<Field ID="FECCC52ABB614390BAE2577AD8C44262">24114</Field>
<Field ID="Template.56E7AA0956C7486292E9A02114CB231C">Nachrichten Field Text</Field>
<Field ID="root/CustomerData/Dateiname">_23-Verk-AO_05-23_05.05.2023-25.05.2023</Field>
</Fields>
<DocumentProperties>
<Guid>7ACEA5AE7C3642978ACCC48182EBCD6E</Guid>
<BuiltIn>
<creator>MGM</creator>
</BuiltIn>
<Extended/>
<Custom>
<Author name="Author" type="lpwstr" property="true" variable="false" xml="false">MGM</Author>
<Typist name="Typist" type="lpwstr" property="true" variable="false" xml="false">MGM</Typist>
<TemplateId name="TemplateId" type="lpwstr" property="true" variable="false" xml="false">
7ACEA5AE7C3642978ACCC48182EBCD6E
</TemplateId>
<Template name="Template" type="lpwstr" property="true" variable="false" xml="false">
KFAS_LIVE_KI_10_Haltverbot_befristet
</Template>
</Custom>
</DocumentProperties>
</SmartDocument>
<CustomerData>
<bescheid>
<bescheidVom>2023-06-26</bescheidVom>
<genehmigt>true</genehmigt>
</bescheid>
<userData>
<firstName>Theo</firstName>
<lastName>Test</lastName>
</userData>
<vorgang>
<vorgangName>KFAS_LIVE_KI_10_Haltverbot_befristet</vorgangName>
<eingang>
<zustaendigeStelle>
<organisationseinheitenId>10363455</organisationseinheitenId>
<email/>
</zustaendigeStelle>
<antragsteller/>
<formData>
<Antrag_abschlie_en>
<Ich_best_tige__dass_die_Beschilderung_entsprechend_den_wichtigen_Hinweisen_durchgef_hrt_wird>
ja
</Ich_best_tige__dass_die_Beschilderung_entsprechend_den_wichtigen_Hinweisen_durchgef_hrt_wird>
<Ich_best_tige_hiermit__dass_ein_Protokoll_entsprechend_den_wichtigen_Hinweisen_erstellt_wird>
ja
</Ich_best_tige_hiermit__dass_ein_Protokoll_entsprechend_den_wichtigen_Hinweisen_erstellt_wird>
<Hiermit_best_tige_ich_die_Kenntnisnahme_des_Vorgehens_bei_parkenden_KFZ>ja
</Hiermit_best_tige_ich_die_Kenntnisnahme_des_Vorgehens_bei_parkenden_KFZ>
<Hiermit_best_tige_ich_die_Richtigkeit_der_gemachten_Angaben>ja
</Hiermit_best_tige_ich_die_Richtigkeit_der_gemachten_Angaben>
</Antrag_abschlie_en>
<Stammdaten__Antragstellende_Person_>
<Adresse_Kiel_vorbef_llt_mit_Kontaktdaten>
<AS_E-Mail>Bjoern.Reusche@kiel.de</AS_E-Mail>
<Postleitzahl>24114</Postleitzahl>
<AS_Telefon>015142536149</AS_Telefon>
<Wohnort>Kiel</Wohnort>
<Stra_e>Je?stra?e</Stra_e>
<Hausnummer>23</Hausnummer>
</Adresse_Kiel_vorbef_llt_mit_Kontaktdaten>
<Nachname>Reusche</Nachname>
<Angabe_zur_Person__Firma>m?nnlich</Angabe_zur_Person__Firma>
<Verantwortliche_Person_f_r_die_Aufstellung_der_Beschilderung/>
<Vorname>Bj?rn</Vorname>
</Stammdaten__Antragstellende_Person_>
<Angaben_zur_Parkregelung_und_zur_Beschilderung>
<Objektgruppe_Hinweis_kein_Abschleppen_m_glich/>
<Ort_des_Haltverbotes>
<Postleitzahl>24114</Postleitzahl>
<Wohnort>Kiel</Wohnort>
<Stra_e>Je?stra?e</Stra_e>
<Hausnummer>23</Hausnummer>
</Ort_des_Haltverbotes>
<L_nge_der_Fl_che_in_Metern>25</L_nge_der_Fl_che_in_Metern>
<Gr_nde>
<Container>nein</Container>
<Baufahrzeug>nein</Baufahrzeug>
<Materiallagerung>nein</Materiallagerung>
<Umzug>ja</Umzug>
<Hubsteiger__Kran>nein</Hubsteiger__Kran>
<Sonstiges>ja</Sonstiges>
</Gr_nde>
<Beginn_des_Haltverbotes>05.05.2023</Beginn_des_Haltverbotes>
<Bitte_erl_utern_Sie_Ihren_Grund>Verl?ngerung bisher ab 01.04.2023
</Bitte_erl_utern_Sie_Ihren_Grund>
<Uhrzeit>
<Ende_des_Haltverbotes__Uhrzeit_>16:23</Ende_des_Haltverbotes__Uhrzeit_>
<Beginn_des_Haltverbotes__Uhrzeit_>03:25</Beginn_des_Haltverbotes__Uhrzeit_>
</Uhrzeit>
<Vor_Ort_geltende_Parkregelung>Parken am Fahrbahnrand</Vor_Ort_geltende_Parkregelung>
<Tage_Haltverbot>
<Freitag>nein</Freitag>
<Samstag>ja</Samstag>
<Montag>ja</Montag>
<Mittwoch>ja</Mittwoch>
<Donnerstag>nein</Donnerstag>
<Dienstag>nein</Dienstag>
<Sonntag>nein</Sonntag>
</Tage_Haltverbot>
<Ende_des_Haltverbotes>25.05.2023</Ende_des_Haltverbotes>
</Angaben_zur_Parkregelung_und_zur_Beschilderung>
<Informationen_und_Hinweise_zu_erforderlichen_Unterlagen/>
</formData>
</eingang>
<vorgangNummer>KFAS_LIVE_KI_10_Haltverbot_befristet-JhQDdq2F</vorgangNummer>
<aktenzeichen>KFAS_LIVE_KI_10_Haltverbot_befristet-JhQDdq2F</aktenzeichen>
</vorgang>
<Dateiname>_23-Verk-AO_05-23_05.05.2023-25.05.2023</Dateiname>
</CustomerData>
</root>
......@@ -29,7 +29,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager</artifactId>
<version>2.10.0-SNAPSHOT</version>
<version>2.11.0-SNAPSHOT</version>
<name>OZG-Cloud Vorgang Manager</name>
<packaging>pom</packaging>
......
......@@ -109,6 +109,18 @@ spec:
podSelector:
matchLabels:
component: info-manager
{{- end }}
{{- if (.Values.zufiManager).enabled }}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: {{ required "zufiManager.namespace must be set if zufiManager server is enabled" (.Values.zufiManager).namespace }}
podSelector:
matchLabels:
component: zufi-server
ports:
- port: 9090
protocol: TCP
{{- end }}
- to:
- namespaceSelector:
......
......@@ -27,21 +27,28 @@ release:
namespace: by-helm-test
templates:
- templates/network_policy.yaml
tests:
- it: should match apiVersion
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
tests:
- it: should match apiVersion
asserts:
- isAPIVersion:
of: networking.k8s.io/v1
- it: should match kind
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- isKind:
of: NetworkPolicy
- it: validate metadata
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- equal:
path: metadata
......@@ -50,6 +57,9 @@ tests:
namespace: by-helm-test
- it: should set policy target matchLabel
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- equal:
path: spec.podSelector
......@@ -59,18 +69,27 @@ tests:
- it: should add policyType Egress
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.policyTypes
content: Egress
- it: should add policyType Ingress
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.policyTypes
content: Ingress
- it: should add ingress rule for eingangsmanager and alfa
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.ingress
......@@ -90,6 +109,8 @@ tests:
- it: should add ingress rule for antragraum if antragraum is enabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
antragraum:
enabled: true
......@@ -109,6 +130,8 @@ tests:
- it: should not add ingress rule for antragraum if antragraum is disabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
antragraum:
enabled: false
......@@ -126,6 +149,8 @@ tests:
- it: should throw error if antragraum is enabled but antragraum namespace is not set
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
antragraum:
enabled: true
......@@ -135,6 +160,9 @@ tests:
- it: should add egress rule to elasticsearch
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.egress
......@@ -151,6 +179,9 @@ tests:
protocol: TCP
- it: should add egress rule to mongodb
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.egress
......@@ -164,6 +195,9 @@ tests:
protocol: TCP
- it: should add egress rule to user-manager
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.egress
......@@ -178,6 +212,8 @@ tests:
- it: should add egress rule to nachrichten-bayernid-proxy if bayernid is enabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
bayernid:
enabled: true
......@@ -200,6 +236,8 @@ tests:
- it: should not add egress rule to bayernid-proxy if bayernid is disabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
bayernid:
enabled: false
......@@ -220,6 +258,8 @@ tests:
- it: should throw error if bayernid-proxy is enabled but bayernid namespace is not set
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
bayernid:
enabled: true
......@@ -229,6 +269,8 @@ tests:
- it: should add egress rule to info-manager if antragraum is enabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
antragraum:
enabled: true
......@@ -247,6 +289,8 @@ tests:
- it: should not add egress rule to info-manager if antragraum is disabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ozgcloud:
antragraum:
enabled: false
......@@ -262,8 +306,83 @@ tests:
matchLabels:
component: info-manager
- it: should add egress rule to zufi server if zufi is enabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
zufiManager:
enabled: true
namespace: zufi
asserts:
- contains:
path: spec.egress
content:
to:
- podSelector:
matchLabels:
component: zufi-server
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: zufi
ports:
- port: 9090
protocol: TCP
- it: should not add egress rule to zufi server if zufi is disabled
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
zufiManager:
enabled: false
namespace: zufi
asserts:
- notContains:
path: spec.egress
content:
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: zufi
podSelector:
matchLabels:
component: zufi-server
any: true
- it: should throw error if zufi is enabled but zufi namespace is not set
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
zufiManager:
enabled: true
asserts:
- failedTemplate:
errorMessage: zufiManager.namespace must be set if zufiManager server is enabled
- it: should not enable zufi netpol by default
set:
zufiManager:
namespace: zufi
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- notContains:
path: spec.egress
content:
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: zufi
podSelector:
matchLabels:
component: zufi-server
any: true
- it: should add egress rule to dns service
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
asserts:
- contains:
path: spec.egress
......@@ -285,6 +404,7 @@ tests:
- it: add ingress rule local by values
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ssoPublicIp: 51.89.117.53/32
additionalIngressConfigGlobal:
- from:
......@@ -302,6 +422,7 @@ tests:
- it: add ingress rule global by values
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
ssoPublicIp: 51.89.117.53/32
additionalIngressConfigLocal:
- from:
......@@ -320,6 +441,7 @@ tests:
- it: add egress rules local by values
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
additionalEgressConfigGlobal:
- to:
- ipBlock:
......@@ -351,6 +473,7 @@ tests:
- it: add egress rules global by values
set:
networkPolicy:
dnsServerNamespace: test-dns-namespace
additionalEgressConfigLocal:
- to:
- ipBlock:
......@@ -380,6 +503,22 @@ tests:
set:
networkPolicy:
disabled: false
dnsServerNamespace: test-dns-namespace
asserts:
- hasDocuments:
count: 1
- it: test network policy dnsServerNamespace must be set message
set:
networkPolicy:
disabled: false
asserts:
- failedTemplate:
errorMessage: networkPolicy.dnsServerNamespace must be set
- it: test network policy should be enabled by default
set:
networkPolicy:
dnsServerNamespace: test-dns-server-namespace
asserts:
- hasDocuments:
count: 1
\ No newline at end of file
......@@ -12,7 +12,7 @@
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-base</artifactId>
<version>2.10.0-SNAPSHOT</version>
<version>2.11.0-SNAPSHOT</version>
<name>OZG-Cloud Vorgang Manager Base</name>
......
......@@ -10,7 +10,7 @@
<groupId>de.ozgcloud.command</groupId>
<artifactId>command-manager</artifactId>
<version>2.10.0-SNAPSHOT</version>
<version>2.11.0-SNAPSHOT</version>
<name>OZG-Cloud Command Manager</name>
<properties>
......
......@@ -36,7 +36,7 @@
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-interface</artifactId>
<version>2.10.0-SNAPSHOT</version>
<version>2.11.0-SNAPSHOT</version>
<name>OZG-Cloud Vorgang Manager gRPC Interface</name>
<description>Interface (gRPC) for Vorgang Manager Server</description>
......
......@@ -38,7 +38,7 @@
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-server</artifactId>
<version>2.10.0-SNAPSHOT</version>
<version>2.11.0-SNAPSHOT</version>
<name>OZG-Cloud Vorgang Manager Server</name>
<description>Server Implementierung des VorgangManagers</description>
......@@ -50,10 +50,10 @@
<spring-boot.build-image.imageName>docker.ozg-sh.de/vorgang-manager:build-latest</spring-boot.build-image.imageName>
<ozgcloud.license.version>1.3.0</ozgcloud.license.version>
<zufi-manager-interface.version>1.0.0-SNAPSHOT</zufi-manager-interface.version>
<zufi-manager-interface.version>1.0.0</zufi-manager-interface.version>
<user-manager-interface.version>2.1.0</user-manager-interface.version>
<bescheid-manager.version>1.14.0-SNAPSHOT</bescheid-manager.version>
<bescheid-manager.version>1.14.1</bescheid-manager.version>
<processor-manager.version>0.4.1</processor-manager.version>
<nachrichten-manager.version>2.9.0</nachrichten-manager.version>
<ozgcloud-starter.version>0.10.0</ozgcloud-starter.version>
......
......@@ -78,16 +78,16 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI
private Update buildUpdateForItemAndVersion(Map<String, Object> propertyMap) {
var item = new Document();
item.putAll(propertyMap);
return new Update().set(VorgangAttachedItem.FIELDNAME_ITEM, item);
return new Update().set(VorgangAttachedItem.FIELDNAME_ITEM, item).inc(VorgangAttachedItem.FIELDNAME_VERSION, 1);
}
@Override
public void patch(String itemId, long version, Map<String, Object> propertyMap) {
mongoOperations.updateFirst(
var patchedResult = mongoOperations.updateFirst(
query(byIdAndVersion(itemId, version)),
createPatchUpdateForItemAndVersion(propertyMap),
VorgangAttachedItem.class);
collisionVerifier.verify(patchedResult, itemId);
}
@Override
......@@ -123,7 +123,7 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI
}
private Document buildItemPatchDocument(Map<String, Object> propertiesMap) {
Map<String, Object> resultMap = new HashMap<>(propertiesMap.size());
var resultMap = new HashMap<String, Object>(propertiesMap.size());
propertiesMap.forEach((k, v) -> resultMap.put("%s.%s".formatted(VorgangAttachedItem.FIELDNAME_ITEM, k), v));
return new Document(resultMap);
}
......@@ -140,5 +140,4 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI
Update.update(VorgangAttachedItem.FIELDNAME_IS_DELETED, deleted), VorgangAttachedItem.COLLECTION_NAME);
collisionVerifier.verify(updateResult, itemId);
}
}
\ No newline at end of file
......@@ -23,28 +23,32 @@
*/
package de.ozgcloud.vorgang.attached_item;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import jakarta.validation.ValidationException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import jakarta.validation.ValidationException;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandCreatedEvent;
import de.ozgcloud.command.CommandFailedEvent;
import de.ozgcloud.command.CommandRevokeFailedEvent;
import de.ozgcloud.command.CommandRevokedEvent;
import de.ozgcloud.command.RevokeCommandEvent;
import de.ozgcloud.vorgang.command.CommandService;
import de.ozgcloud.vorgang.command.Order;
import de.ozgcloud.vorgang.command.PersistedCommand;
import de.ozgcloud.vorgang.vorgang.VorgangDeletedEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Component
@RequiredArgsConstructor
@Log4j2
class VorgangAttachedItemEventListener {
......@@ -58,42 +62,48 @@ class VorgangAttachedItemEventListener {
public static final Predicate<Command> IS_PATCH_ITEM_ORDER = command -> Order.PATCH_ATTACHED_ITEM.isMeant(command.getOrder());
public static final Predicate<Command> IS_DELETE_ITEM_ORDER = command -> Order.DELETE_ATTACHED_ITEM.isMeant(command.getOrder());
@Autowired
private VorgangAttachedItemService service;
@Autowired
private VorgangAttachedItemMapper mapper;
@Autowired
private ApplicationEventPublisher publisher;
private final VorgangAttachedItemService service;
private final VorgangAttachedItemMapper mapper;
private final ApplicationEventPublisher publisher;
private final CommandService commandService;
@EventListener(condition = IS_CREATE_ITEM_ORDER_CONDITION)
public void createItem(CommandCreatedEvent event) {
var command = event.getSource();
validateCreateCommand(command);
var item = mapper.fill(command.getBodyObject());
service.create(command.getId(), item.toBuilder().id(null).version(0).build());
var item = mapper.fill(command.getBodyObject()).toBuilder().id(null).version(0).build();
service.create(command.getId(), item);
}
private void validateCreateCommand(Command command) {
void validateCreateCommand(Command command) {
if (!StringUtils.equals(command.getRelationId(), command.getVorgangId())) {
throw new ValidationException("Command invalid creation command: vorgangId and relationId must be equals");
}
}
@EventListener(condition = IS_UPDATE_ITEM_ORDER_CONDITION)
public void updateItem(CommandCreatedEvent event) {
var command = event.getSource();
var itemBuilder = mapper.fill(command.getBodyObject())
.toBuilder().id(command.getRelationId());
var filledItem = mapper.fill(command.getBodyObject()).toBuilder().id(command.getRelationId()).build();
setCommandPreviousState(command.getId(), getVorgangAttachedItem(command.getRelationId()).getItem());
doUpdate(command, filledItem);
}
private void doUpdate(Command command, VorgangAttachedItem filledItem) {
if (Objects.nonNull(command.getRelationVersion())) {
itemBuilder.version(command.getRelationVersion());
service.update(command.getId(), itemBuilder.build());
service.update(command.getId(), filledItem.toBuilder().version(command.getRelationVersion()).build());
} else {
service.forceUpdate(command.getId(), itemBuilder.build());
service.forceUpdate(command.getId(), filledItem.getId(), filledItem.getItem());
}
}
VorgangAttachedItem getVorgangAttachedItem(String id) {
return service.getById(id);
}
@EventListener(condition = IS_PATCH_ITEM_ORDER_CONDITION)
......@@ -101,9 +111,20 @@ class VorgangAttachedItemEventListener {
var command = event.getSource();
var item = mapper.fill(command.getBodyObject());
setCommandPreviousState(command.getId(), getExistingItemMapEntries(command.getRelationId(), item.getItem().keySet()));
service.patch(command, item.getItem());
}
Map<String, Object> getExistingItemMapEntries(String id, Set<String> keys) {
return getVorgangAttachedItem(id).getItem().entrySet().stream().filter(entry -> keys.contains(entry.getKey()))
.collect(HashMap::new, (itemMap, entry) -> itemMap.put(entry.getKey(), entry.getValue()), HashMap::putAll);
}
void setCommandPreviousState(String commandId, Map<String, Object> itemMap) {
commandService.setPreviousState(commandId, itemMap);
}
@EventListener(condition = IS_DELETE_ITEM_ORDER_CONDITION)
public void deleteItem(CommandCreatedEvent event) {
var command = event.getSource();
......@@ -129,16 +150,31 @@ class VorgangAttachedItemEventListener {
}
}
@EventListener(condition = IS_CREATE_ITEM_ORDER_CONDITION)
public void onRevokeCreateItem(RevokeCommandEvent event) {
service.revokeCreate(event.getSource());
}
@EventListener(condition = IS_DELETE_ITEM_ORDER_CONDITION)
public void onRevokeDeleteItem(RevokeCommandEvent event) {
service.revokeDelete(event.getSource());
}
@EventListener(condition = IS_UPDATE_ITEM_ORDER_CONDITION)
public void onRevokeUpdateItem(RevokeCommandEvent event) {
var command = event.getSource();
try {
service.unmarkAsDeleteByIdAndVersion(command.getRelationId(), command.getRelationVersion());
publisher.publishEvent(new CommandRevokedEvent(command));
} catch (RuntimeException e) {
LOG.error("Revoke of delete item command failed", e);
publisher.publishEvent(new CommandRevokeFailedEvent(command.getId(), e.getMessage()));
var persistedCommand = getPersistedCommand(command.getId());
service.revokeUpdate(command, persistedCommand.getPreviousState());
}
@EventListener(condition = IS_PATCH_ITEM_ORDER_CONDITION)
public void onRevokePatchItem(RevokeCommandEvent event) {
var command = event.getSource();
var persistedCommand = getPersistedCommand(command.getId());
service.revokePatch(command, persistedCommand.getPreviousState());
}
private PersistedCommand getPersistedCommand(String commandId) {
return (PersistedCommand) commandService.getById(commandId);
}
}
......@@ -24,7 +24,6 @@
package de.ozgcloud.vorgang.attached_item;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.collections.MapUtils;
import org.mapstruct.Mapper;
......@@ -41,25 +40,18 @@ public interface VorgangAttachedItemMapper {
public static final String PROPERTY_ITEM_NAME = "itemName";
public static final String PROPERTY_ITEM = "item";
@SuppressWarnings("unchecked")
default VorgangAttachedItem fill(Map<String, Object> propertyMap) {
return VorgangAttachedItem.builder()
.id(extractIdFromItem(propertyMap))
.client(MapUtils.getString(propertyMap, PROPERTY_CLIENT))
.vorgangId(MapUtils.getString(propertyMap, PROPERTY_VORGANG_ID))
.itemName(MapUtils.getString(propertyMap, PROPERTY_ITEM_NAME))
.item((Map<String, Object>) propertyMap.get(PROPERTY_ITEM))
.item(getPropertyItem(propertyMap))
.version(MapUtils.getLong(propertyMap, PROPERTY_VERSION, 0L))
.build();
}
// TODO remove mapping of id from Map
@SuppressWarnings("unchecked")
default String extractIdFromItem(Map<String, Object> propertyMap) {
return (String) Optional.ofNullable(propertyMap.get(PROPERTY_ID))
.orElseGet(() -> ((Map<String, Object>) propertyMap.get(PROPERTY_ITEM)).get(PROPERTY_ID));
default Map<String, Object> getPropertyItem(Map<String, Object> propertyMap) {
return (Map<String, Object>) propertyMap.get(PROPERTY_ITEM);
}
}
......@@ -31,25 +31,28 @@ import java.util.stream.Stream;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandRevokeFailedEvent;
import de.ozgcloud.command.CommandRevokedEvent;
import de.ozgcloud.vorgang.common.errorhandling.NotFoundException;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Service
@Log4j2
@Validated
@RequiredArgsConstructor
@Service
public class VorgangAttachedItemService {
static final boolean NOT_DELETED = false;
@Autowired
private VorgangAttachedItemRepository repository;
@Autowired
private ApplicationEventPublisher publisher;
private final VorgangAttachedItemRepository repository;
private final ApplicationEventPublisher publisher;
public VorgangAttachedItem create(@NonNull String commandId, @Valid VorgangAttachedItem item) {
var saved = repository.save(prepareForCreation(item));
......@@ -74,13 +77,21 @@ public class VorgangAttachedItemService {
}
public void update(String commandId, VorgangAttachedItem item) {
repository.update(item.getId(), item.getVersion(), item.getItem());
doUpdate(item.getId(), item.getVersion(), item.getItem());
publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(commandId));
}
public void forceUpdate(String commandId, VorgangAttachedItem item) {
repository.forceUpdate(item.getId(), item.getItem());
public void revokeUpdate(Command command, Map<String, Object> itemMap) {
handleRevoke(command, () -> doUpdate(command.getRelationId(), command.getRelationVersion() + 1, itemMap));
}
void doUpdate(String id, long version, Map<String, Object> body) {
repository.update(id, version, body);
}
public void forceUpdate(String commandId, String itemId, Map<String, Object> itemMap) {
repository.forceUpdate(itemId, itemMap);
publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(commandId));
}
......@@ -115,11 +126,19 @@ public class VorgangAttachedItemService {
}
public void patch(Command command, Map<String, Object> body) {
repository.patch(command.getRelationId(), command.getRelationVersion(), body);
doPatch(command, command.getRelationVersion(), body);
publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(command.getId()));
}
public void revokePatch(Command command, Map<String, Object> itemMap) {
handleRevoke(command, () -> doPatch(command, command.getRelationVersion() + 1, itemMap));
}
void doPatch(Command command, Long relationVersion, Map<String, Object> body) {
repository.patch(command.getRelationId(), relationVersion, body);
}
public VorgangAttachedItem getById(String id) {
var attachedItem = repository.findByIdAndDeleted(id, NOT_DELETED).orElseThrow(() -> new NotFoundException(VorgangAttachedItem.class, id));
......@@ -136,12 +155,33 @@ public class VorgangAttachedItemService {
repository.updateDeleted(true, itemId, version);
}
public void unmarkAsDeleteByIdAndVersion(String itemId, long version) {
repository.updateDeleted(false, itemId, version);
}
public void deleteByVorgangId(String vorgangId) {
repository.deleteByVorgangId(vorgangId);
}
public void revokeCreate(Command command) {
handleRevoke(command, () -> delete(command.getRelationId(), command.getRelationVersion()));
}
void delete(String id, long version) {
repository.delete(id, version);
}
public void revokeDelete(Command command) {
handleRevoke(command, () -> unmarkAsDeleteByIdAndVersion(command.getRelationId(), command.getRelationVersion()));
}
void unmarkAsDeleteByIdAndVersion(String itemId, long version) {
repository.updateDeleted(false, itemId, version);
}
void handleRevoke(Command command, Runnable runnable) {
try {
runnable.run();
publisher.publishEvent(new CommandRevokedEvent(command));
} catch (RuntimeException e) {
LOG.error("Revoke item command failed(order: {})", command.getOrder(), e);
publisher.publishEvent(new CommandRevokeFailedEvent(command.getId(), e.getMessage()));
}
}
}
\ 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