diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java index ed3ed7816b082b0139413eceee38a8196b63e968..ce9d59c38cf6f5ee88d9c8fc40c150001bebbc0a 100644 --- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java +++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java @@ -37,9 +37,9 @@ public class AntragstellerTestFactory { public static final String SUB_VERBOTENE_VEREINIGUNG_KEY = "MitgliedschaftInVerboternerVereinigung"; public static final String SUB_VERBOTENE_VEREINIGUNG_VALUE = "true"; - public static final String VORNAME = "Helge"; - public static final String NACHNAME = "Schneider"; - public static final String GEBURTSNAME = "Schneider"; + public static final String VORNAME = "Theo"; + public static final String NACHNAME = "Test"; + public static final String GEBURTSNAME = "Toast"; public static final String GEBURTSDATUM = "30.8.1955"; public static final String GEBURTSORT = "Mülheim an der Ruhr"; public static final String EMAIL = "schneider@helgeschneider.local"; diff --git a/enterprise-adapter/pom.xml b/enterprise-adapter/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..8d494575614167d982ecd6308ab22a788cb0bd4e --- /dev/null +++ b/enterprise-adapter/pom.xml @@ -0,0 +1,105 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>de.itvsh.kop.eingangsadapter</groupId> + <artifactId>parent</artifactId> + <version>1.13.0-SNAPSHOT</version> + </parent> + <artifactId>enterprise-adapter</artifactId> + <name>EM - Enterprise Interface Adapter</name> + + <properties> + <spring-boot.build-image.imageName> + docker.ozg-sh.de/enterprise-interface-adapter:build-latest + </spring-boot.build-image.imageName> + </properties> + + <dependencies> + <!--ozg-Cloud--> + <dependency> + <groupId>de.itvsh.kop.eingangsadapter</groupId> + <artifactId>common</artifactId> + </dependency> + <dependency> + <groupId>de.itvsh.kop.eingangsadapter</groupId> + <artifactId>semantik-adapter</artifactId> + </dependency> + + + <!--spring--> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + + + + <!--dev tools--> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + </dependency> + + <!--test --> + <dependency> + <groupId>de.itvsh.kop.eingangsadapter</groupId> + <artifactId>common</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + </plugin> + + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>ci-build</id> + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <id>build-image</id> + <phase> + install</phase> + <goals> + <goal>build-image</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> \ No newline at end of file diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryController.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryController.java new file mode 100644 index 0000000000000000000000000000000000000000..47febc1e961826ab9d176bd5c1a89f18ecb8d0c6 --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryController.java @@ -0,0 +1,36 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import de.ozgcloud.eingang.semantik.SemantikAdapter; + +@Controller +@ResponseBody +@RequestMapping("antrag") +public class EntryController { + + @Autowired + private EntryDataMapper mapper; + + @Autowired + private SemantikAdapter semantikAdapter; + + @ResponseStatus(HttpStatus.ACCEPTED) + @PostMapping(consumes = "multipart/form-data", produces = MediaType.APPLICATION_JSON_VALUE) + public void receiveAntrag(@RequestPart Resource formData) throws IOException { + var mapped = mapper.mapEntryData(formData.getInputStream()); + + semantikAdapter.processFormData(mapped); + } +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryData.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryData.java new file mode 100644 index 0000000000000000000000000000000000000000..5fedcbcae2f1aa5fc028dace4634df10794564c6 --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryData.java @@ -0,0 +1,38 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Builder; +import lombok.Getter; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Getter +@Jacksonized +public class EntryData { + + private ControlData control; + private List<EntryFormDataItem> formData; + + @Builder + @Getter + @Jacksonized + public static class ControlData { + private String transactionId; + private String zustaendigeStelle; + private String[] leikaIds; + private ResultEndpoint resultEndpoint; + private String formId; + @JsonProperty("name") + private String formName; + + @Builder + @Getter + @Jacksonized + public static class ResultEndpoint { + private String address; + } + } +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryDataMapper.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryDataMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..1c2c5340499d72c4101fc52bbbd40ddec37c3c2d --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryDataMapper.java @@ -0,0 +1,34 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import java.io.IOException; +import java.io.InputStream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.eingang.common.formdata.FormData; + +@Component +class EntryDataMapper { + + @Autowired + private ObjectMapper objectMapper; + @Autowired + private FormDataMapper formDataMapper; + + public FormData mapEntryData(InputStream request) { + return formDataMapper.mapEntryData(readRequest(request)); + } + + EntryData readRequest(InputStream request) { + try { + return objectMapper.readValue(request, EntryData.class); + } catch (IOException e) { + throw new ReadingRequestException(e); + } + + } + +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataField.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataField.java new file mode 100644 index 0000000000000000000000000000000000000000..7a7623d46aa01306b28979c9791b17c4677e1653 --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataField.java @@ -0,0 +1,48 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import java.time.LocalDate; +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Getter +@Jacksonized +@ToString(onlyExplicitlyIncluded = true) +public class EntryFormDataField implements EntryFormDataItem { + + private String name; + @ToString.Include + private String label; + + private String stringValue; + private Boolean booleanValue; + private Number numberValue; + private LocalDate dateValue; + + @Override + public boolean isFormField() { + return true; + } + + public Object getValue() { + if (StringUtils.isNotBlank(stringValue)) { + return stringValue; + } + if (Objects.nonNull(booleanValue)) { + return booleanValue; + } + if (Objects.nonNull(numberValue)) { + return numberValue; + } + if (Objects.nonNull(dateValue)) { + return dateValue; + } + return null; + } +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataItem.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataItem.java new file mode 100644 index 0000000000000000000000000000000000000000..c2c318fd80dfa8319fa565c56b7a5ee8a0343bf9 --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataItem.java @@ -0,0 +1,21 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +@JsonTypeInfo(use = Id.DEDUCTION) +@JsonSubTypes({ @Type(EntryFormDataField.class), @Type(EntryFormDataSubForm.class) }) +public interface EntryFormDataItem { + String getName(); + String getLabel(); + + default boolean isSubForm() { + return false; + } + + default boolean isFormField() { + return false; + } +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataSubForm.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataSubForm.java new file mode 100644 index 0000000000000000000000000000000000000000..402f2bc1bdbc8e5af6ae3fc1b4cfa9836c4e5725 --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataSubForm.java @@ -0,0 +1,28 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import java.util.List; + +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Getter +@Jacksonized +@ToString(onlyExplicitlyIncluded = true) +public class EntryFormDataSubForm implements EntryFormDataItem { + + private String name; + @ToString.Include + private String label; + + @Singular + private List<EntryFormDataItem> formItems; + + @Override + public boolean isSubForm() { + return true; + } +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/FormDataMapper.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/FormDataMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..287a9e4c71a00c916f70b4c180860111e55047f5 --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/FormDataMapper.java @@ -0,0 +1,60 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import de.ozgcloud.eingang.common.formdata.FormData; +import de.ozgcloud.eingang.common.formdata.FormHeader; +import de.ozgcloud.eingang.common.formdata.ZustaendigeStelle; + +@Mapper +public interface FormDataMapper { + + public static final String VALUE_KEY = "value"; + public static final String LABEL_KEY = "label"; + + @Mapping(target = "antragsteller", ignore = true) + @Mapping(target = "attachment", ignore = true) + @Mapping(target = "attachments", ignore = true) + @Mapping(target = "numberOfAttachments", ignore = true) + @Mapping(target = "representation", ignore = true) + @Mapping(target = "representations", ignore = true) + @Mapping(target = "numberOfRepresentations", ignore = true) + + @Mapping(target = "id", ignore = true) + @Mapping(target = "header", source = "control") + + @Mapping(target = "zustaendigeStelle", source = "control.zustaendigeStelle") + FormData mapEntryData(EntryData entryData); + + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "formEngineName", constant = "EnterpriseSoftware") + @Mapping(target = "requestId", source = "transactionId") + @Mapping(target = "sender", ignore = true) // TODO fill from authentication + @Mapping(target = "serviceKonto", ignore = true) + FormHeader mapHeader(EntryData.ControlData controlData); + + default ZustaendigeStelle fromId(String organisationsEinheitenId) { + return ZustaendigeStelle.builder().organisationseinheitenId(organisationsEinheitenId).build(); + } + + default Map<String, Object> mapFormItems(List<EntryFormDataItem> items) { + return items.stream().map(item -> Pair.of(item.getName(), + item.isFormField() ? mapFormField((EntryFormDataField) item) : mapSubForm((EntryFormDataSubForm) item))) + .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + } + + default Map<String, Object> mapFormField(EntryFormDataField field) { + return Map.of(LABEL_KEY, field.getLabel(), VALUE_KEY, field.getValue()); + } + + default Map<String, Object> mapSubForm(EntryFormDataSubForm subForm) { + return Map.of(LABEL_KEY, subForm.getLabel(), VALUE_KEY, mapFormItems(subForm.getFormItems())); + } + +} diff --git a/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/ReadingRequestException.java b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/ReadingRequestException.java new file mode 100644 index 0000000000000000000000000000000000000000..d1d4e3f9c4467aac99cf52838c164f3f1e26f49c --- /dev/null +++ b/enterprise-adapter/src/main/java/de/ozgcloud/eingang/enterprise/entry/ReadingRequestException.java @@ -0,0 +1,14 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +public class ReadingRequestException extends TechnicalException { + + private static final String MESSAGE = "Error reading Request."; + + public ReadingRequestException(Exception cause) { + super(MESSAGE, cause); + + } + +} diff --git a/enterprise-adapter/src/main/resources/application-local.yml b/enterprise-adapter/src/main/resources/application-local.yml new file mode 100644 index 0000000000000000000000000000000000000000..59827f1222c5e412f7c779ebb5c94f95da019fd2 --- /dev/null +++ b/enterprise-adapter/src/main/resources/application-local.yml @@ -0,0 +1,18 @@ +logging: + config: classpath:log4j2-local.xml + +server: + port: 9294 + error: + include-stacktrace: always + +kop: + adapter: + targetPlutoName: local + fallbackStrategy: DENY + +grpc: + client: + pluto-local: + address: static://127.0.0.1:9090 + negotiationType: PLAINTEXT \ No newline at end of file diff --git a/enterprise-adapter/src/main/resources/application.yml b/enterprise-adapter/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..6ebdfd473fe13324482e8be56c40248f82d2ae7e --- /dev/null +++ b/enterprise-adapter/src/main/resources/application.yml @@ -0,0 +1,44 @@ +logging: + level: + ROOT: WARN + '[de.itvsh]': INFO + +spring: + servlet: + multipart: + max-file-size: 124MB + max-request-size: 256MB + file-size-threshold: 10MB + +server: + http2: + enabled: true + error: + include-stacktrace: never + +management: + server: + port: 8081 + health: + livenessState: + enabled: true + readinessState: + enabled: true + endpoint: + health: + group: + exploratory: + include: livenessState,readinessState,ping + show-details: always + probes: + enabled: true + prometheus: + enabled: true + endpoints: + web: + exposure: + include: health,prometheus + +kop: + adapter: + routingStrategy: SINGLE \ No newline at end of file diff --git a/enterprise-adapter/src/main/resources/log4j2-local.xml b/enterprise-adapter/src/main/resources/log4j2-local.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d7001e1f9186d197a2d301d3910c9d73ed05d15 --- /dev/null +++ b/enterprise-adapter/src/main/resources/log4j2-local.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <Appenders> + <Console name="CONSOLE" target="SYSTEM_OUT"> + <PatternLayout pattern="[%-5level] %c{1.} %msg%n"/> + </Console> + </Appenders> + + <Loggers> + <Root level="WARN"> + <appender-ref ref="CONSOLE" /> + </Root> + </Loggers> +</configuration> \ No newline at end of file diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/ControlDataTestFactory.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/ControlDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9f26f3e2c8262ba57cbc3eb69385636f737c5e3a --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/ControlDataTestFactory.java @@ -0,0 +1,30 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import de.ozgcloud.eingang.enterprise.entry.EntryData.ControlData; +import de.ozgcloud.eingang.enterprise.entry.EntryData.ControlData.ResultEndpoint; + +public class ControlDataTestFactory { + + public static final String TRANSACTION_ID = "4e7a6ae7-4d0f-444d-8971-7cfc051c9924"; + public static final String ZUSTAENDIGE_STELLE = "248240886"; + public static final String[] LEIKA_IDS = new String[] { "99108011000000", "99108011153000" }; + + public static final String RESULT_ENDPOIN_ADDRESS = "https://idalabs.de/backend/api"; + + public static final String FORM_ID = "KFAS_LIVE_KI_10_Haltverbot_befristet"; + public static final String NAME = "Anmeldung zur Einrichtung einer zeitlich befristeten Haltverbotszone gem. § 45 Abs. 1 Straßenverkehrsordnung (StVO)"; + + public static ControlData create() { + return createBuilder().build(); + } + + public static ControlData.ControlDataBuilder createBuilder() { + return ControlData.builder() + .transactionId(TRANSACTION_ID) + .zustaendigeStelle(ZUSTAENDIGE_STELLE) + .leikaIds(LEIKA_IDS) + .resultEndpoint(ResultEndpoint.builder().address(RESULT_ENDPOIN_ADDRESS).build()) + .formId(FORM_ID) + .formName(NAME); + } +} diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EnterpriseEntryITCase.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EnterpriseEntryITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..8ed385a33028c9356addd631066f9027347a2ca3 --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EnterpriseEntryITCase.java @@ -0,0 +1,45 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.kop.common.test.TestUtils; +import de.ozgcloud.eingang.Application; +import de.ozgcloud.eingang.router.VorgangService; +import lombok.SneakyThrows; + +@SpringBootTest(classes = Application.class) +@AutoConfigureMockMvc +class EnterpriseEntryITCase { + + @MockBean + private VorgangService vorgangService; + + @Autowired + private MockMvc mockMvc; + + @Test + void shouldCallVorgangService() { + doPostRequest(); + + verify(vorgangService).createVorgang(any()); + } + + @SneakyThrows + private ResultActions doPostRequest() { + return mockMvc.perform(multipart("/antrag") + .file(new MockMultipartFile("formData", TestUtils.loadTextFile("request/simple.json").getBytes()))) + .andExpect(status().is2xxSuccessful()); + } +} diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryControllerTest.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..19708556d8ba45f7106e46064ba59c22ebd6a7ef --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryControllerTest.java @@ -0,0 +1,89 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.kop.common.test.TestUtils; +import de.ozgcloud.eingang.common.formdata.FormData; +import de.ozgcloud.eingang.common.formdata.FormDataTestFactory; +import de.ozgcloud.eingang.semantik.SemantikAdapter; +import lombok.SneakyThrows; + +class EntryControllerTest { + + @InjectMocks + private EntryController controller; + + @Mock + private EntryDataMapper mapper; + @Mock + private SemantikAdapter semantikAdapter; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class ReceiveAntrag { + + @Captor + private ArgumentCaptor<InputStream> streamCaptor; + + @Test + @SneakyThrows + void shouldReturnAccepted() { + doPostRequest().andExpect(status().isAccepted()); + } + + @Test + @SneakyThrows + void shouldCallMapper() { + var request = TestUtils.loadTextFile("request/simple.json"); + + doPostRequest(); + + verify(mapper).mapEntryData(streamCaptor.capture()); + var inputBytes = IOUtils.readFully(streamCaptor.getValue(), request.getBytes().length); + assertThat(inputBytes).hasSameSizeAs(request.getBytes()).isEqualTo(request.getBytes()); + } + + @Test + void shouldCallSemantikAdapter() { + FormData formData = FormDataTestFactory.create(); + when(mapper.mapEntryData(any())).thenReturn(formData); + + doPostRequest(); + + verify(semantikAdapter).processFormData(formData); + } + + @SneakyThrows + private ResultActions doPostRequest() { + return mockMvc.perform(multipart("/antrag") + .file(new MockMultipartFile("formData", TestUtils.loadTextFile("request/simple.json").getBytes()))) + .andExpect(status().is2xxSuccessful()); + } + } + +} diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryDataMapperTest.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryDataMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..238ce95c1997b70c0a76bdc323c0078eaff7fb67 --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryDataMapperTest.java @@ -0,0 +1,79 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.InputStream; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.itvsh.kop.common.test.TestUtils; +import de.ozgcloud.eingang.common.formdata.FormData; +import de.ozgcloud.eingang.common.formdata.FormDataTestFactory; +import lombok.SneakyThrows; + +class EntryDataMapperTest { + + @Spy + @InjectMocks + private EntryDataMapper mapper; + @Mock + private FormDataMapper formDataMapper; + + @Spy + private ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); + + @Nested + class TestMappingEntryData { + + private InputStream jsonInput = TestUtils.loadFile("request/simple.json"); + + @Nested + class TestReadRequest { + @Test + void shouldReadJson() { + var read = mapper.readRequest(jsonInput); + + assertThat(read).usingRecursiveComparison().isEqualTo(EntryDataTestFactory.create()); + } + } + + @Test + void shouldReadRequest() { + mapper.mapEntryData(jsonInput); + + verify(mapper).readRequest(jsonInput); + } + + @Test + @SneakyThrows + void shouldCallFormDataMapper() { + var entryData = EntryDataTestFactory.create(); + doReturn(entryData).when(objectMapper).readValue(any(InputStream.class), Mockito.<Class<EntryData>>any()); + + mapper.mapEntryData(jsonInput); + + verify(formDataMapper).mapEntryData(entryData); + } + + @Test + void shouldReturnMappedResult() { + FormData formData = FormDataTestFactory.create(); + when(formDataMapper.mapEntryData(any())).thenReturn(formData); + + var result = mapper.mapEntryData(jsonInput); + + assertThat(result).isSameAs(formData); + } + } + +} diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryDataTestFactory.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..855fa290a29a92945a32767c9f90452b907a9e4c --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryDataTestFactory.java @@ -0,0 +1,14 @@ +package de.ozgcloud.eingang.enterprise.entry; + +public class EntryDataTestFactory { + + public static EntryData create() { + return createBuilder().build(); + } + + public static EntryData.EntryDataBuilder createBuilder() { + return EntryData.builder() + .control(ControlDataTestFactory.create()) + .formData(EntryFormDataTestFactory.create()); + } +} diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataTestFactory.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..6e6bf6802c041b764da3a5b53f0bad168c954deb --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/EntryFormDataTestFactory.java @@ -0,0 +1,40 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import static de.ozgcloud.eingang.enterprise.entry.FormDataMapper.*; + +import java.util.List; +import java.util.Map; + +public class EntryFormDataTestFactory { + + public static final String FORM_FIELD_NAME = "field_name"; + public static final String FORM_FIELD_VALUE = "field_string_value"; + public static final String FORM_FIELD_LABEL = "field_label"; + + public static final String SUB_FORM_NAME = "antragsteller"; + public static final String SUB_FORM_LABEL = "Antragstellende Person"; + + public static final String SUB_FORM_FIELD_NAME = "lastname"; + public static final String SUB_FORM_FIELD_LABEL = "Nachname"; + public static final String SUB_FORM_FIELD_VALUE = "Täst"; + + public static List<EntryFormDataItem> create() { + return List.of( + EntryFormDataField.builder().name(FORM_FIELD_NAME).label(FORM_FIELD_LABEL).stringValue(FORM_FIELD_VALUE).build(), + EntryFormDataSubForm.builder().name(SUB_FORM_NAME).label(SUB_FORM_LABEL) + .formItem( + EntryFormDataField.builder() + .name(SUB_FORM_FIELD_NAME) + .label(SUB_FORM_FIELD_LABEL) + .stringValue(SUB_FORM_FIELD_VALUE) + .build()) + .build()); + } + + public static Map<String, Object> createAsFormDataMap() { + return Map.of( + FORM_FIELD_NAME, Map.of(LABEL_KEY, FORM_FIELD_LABEL, VALUE_KEY, FORM_FIELD_VALUE), + SUB_FORM_NAME, Map.of(LABEL_KEY, SUB_FORM_LABEL, VALUE_KEY, Map.of( + SUB_FORM_FIELD_NAME, Map.of(LABEL_KEY, SUB_FORM_FIELD_LABEL, VALUE_KEY, SUB_FORM_FIELD_VALUE)))); + } +} diff --git a/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/FormDataMapperTest.java b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/FormDataMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f725885390ee66c6da08232950680b787884562c --- /dev/null +++ b/enterprise-adapter/src/test/java/de/ozgcloud/eingang/enterprise/entry/FormDataMapperTest.java @@ -0,0 +1,26 @@ +package de.ozgcloud.eingang.enterprise.entry; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; + +class FormDataMapperTest { + + @InjectMocks + private FormDataMapper mapper = Mappers.getMapper(FormDataMapper.class); + + @Nested + class TestMapFormItems { + + @Test + void shouldMapFormItems() { + var mapped = mapper.mapFormItems(EntryFormDataTestFactory.create()); + + assertThat(mapped).usingRecursiveComparison().isEqualTo(EntryFormDataTestFactory.createAsFormDataMap()); + } + } + +} diff --git a/enterprise-adapter/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/enterprise-adapter/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 0000000000000000000000000000000000000000..79b126e6cdb86bec1f4f08c205de8961bde1934a --- /dev/null +++ b/enterprise-adapter/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +org.mockito.junit.jupiter.MockitoExtension \ No newline at end of file diff --git a/enterprise-adapter/src/test/resources/junit-platform.properties b/enterprise-adapter/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000000000000000000000000000000000..1cebb76d5a58ac034b2627d12411d82d1e85821e --- /dev/null +++ b/enterprise-adapter/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.extensions.autodetection.enabled = true \ No newline at end of file diff --git a/enterprise-adapter/src/test/resources/request/simple.json b/enterprise-adapter/src/test/resources/request/simple.json new file mode 100644 index 0000000000000000000000000000000000000000..cb55a7580d5924f204ccd0950f68d9a6d6a72dea --- /dev/null +++ b/enterprise-adapter/src/test/resources/request/simple.json @@ -0,0 +1,33 @@ +{ + "control": { + "transactionId": "4e7a6ae7-4d0f-444d-8971-7cfc051c9924", + "zustaendigeStelle": "248240886", + "leikaIds": [ + "99108011000000", + "99108011153000" + ], + "resultEndpoint": { + "address": "https://idalabs.de/backend/api" + }, + "formId": "KFAS_LIVE_KI_10_Haltverbot_befristet", + "name": "Anmeldung zur Einrichtung einer zeitlich befristeten Haltverbotszone gem. § 45 Abs. 1 Straßenverkehrsordnung (StVO)" + }, + "formData": [ + { + "name": "field_name", + "label": "field_label", + "stringValue": "field_string_value" + }, + { + "name": "antragsteller", + "label": "Antragstellende Person", + "formItems": [ + { + "name": "lastname", + "label": "Nachname", + "stringValue": "Täst" + } + ] + } + ] +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 159a629dfb724cef66d31bd051cf94e8a3cb4026..163fadcc95d6404bd5adc2458afa062ddd751736 100644 --- a/pom.xml +++ b/pom.xml @@ -24,17 +24,19 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - + <parent> <groupId>de.itvsh.kop.common</groupId> <artifactId>kop-common-parent</artifactId> <version>2.3.2-SNAPSHOT</version> - <relativePath/> <!-- lookup parent from repository --> + <relativePath /> <!-- lookup parent from repository --> </parent> - + <groupId>de.itvsh.kop.eingangsadapter</groupId> <artifactId>parent</artifactId> <version>1.13.0-SNAPSHOT</version> @@ -50,6 +52,7 @@ <module>semantik-adapter</module> <module>formcycle-adapter</module> <module>xta-adapter</module> + <module>enterprise-adapter</module> </modules> <properties> @@ -154,9 +157,9 @@ </plugin> <plugin> <groupId>com.evolvedbinary.maven.jvnet</groupId> - <artifactId>jaxb30-maven-plugin</artifactId> - <version>${jaxb3-plugin.version}</version> - <executions> + <artifactId>jaxb30-maven-plugin</artifactId> + <version>${jaxb3-plugin.version}</version> + <executions> <execution> <goals> <goal>generate</goal> @@ -206,9 +209,9 @@ </plugin> </plugins> </pluginManagement> - + <plugins> - + </plugins> </build>