diff --git a/src/main/java/de/ozgcloud/eingang/intelliform/DepositDataMapper.java b/src/main/java/de/ozgcloud/eingang/intelliform/DepositDataMapper.java index 93b4e696a4c40b0f72b3a7fbea700fffb164161e..15e7ca3ceba67458acad3162e305cb2dd4d08ce8 100644 --- a/src/main/java/de/ozgcloud/eingang/intelliform/DepositDataMapper.java +++ b/src/main/java/de/ozgcloud/eingang/intelliform/DepositDataMapper.java @@ -23,95 +23,51 @@ */ package de.ozgcloud.eingang.intelliform; -import static java.util.stream.Collectors.*; - -import java.io.IOException; import java.time.ZonedDateTime; -import java.util.Collection; import java.util.GregorianCalendar; import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import org.springframework.stereotype.Component; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; import de.ozgcloud.common.binaryfile.TempFileUtils; -import de.ozgcloud.eingang.common.errorhandling.TechnicalException; import de.ozgcloud.eingang.common.formdata.FormData; import de.ozgcloud.eingang.common.formdata.FormData.FormDataControl; import de.ozgcloud.eingang.common.formdata.IncomingFile; -import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; import lombok.RequiredArgsConstructor; @Component @RequiredArgsConstructor class DepositDataMapper { - // TODO Resolve code duplication (xta-adapter: - // de.ozgcloud.eingang.xdomea.XMLHelper) - private static final DocumentBuilder DOCUMENT_BUILDER = createDocumentBuilder(); - - private static DocumentBuilder createDocumentBuilder() { - var documentBuilderFactory = DocumentBuilderFactory.newInstance(); - try { - documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - return documentBuilderFactory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - throw new TechnicalException("Failed to configure document builder", e); - } - } - public FormData mapToFormData(DepositData depositData) { - var incomingFileMap = mapDepositAttachmentsToSortedIncomingFiles(depositData); - var document = parsePrimaryXmlRepresentation(depositData, incomingFileMap); - var attachmentGroups = findAttachmentGroups(document); - - var formData = mapToFormDataWithRepresentationsAndAttachments( - getRepresentations(incomingFileMap, getAttachmentFileIds(attachmentGroups)), - getAttachmentFileGroups(attachmentGroups, incomingFileMap)); + var classification = mapDepositIncomingFiles(depositData) + .classifyFiles(); - return formData.toBuilder() - .control(FormDataControl.builder().metaData(Optional.of(buildMetaData(depositData))).build()) - .build(); - } - - private List<String> getAttachmentFileIds(Map<String, List<String>> attachmentGroups) { - return attachmentGroups.values().stream() - .flatMap(Collection::stream) - .toList(); - } + var attachments = classification.getAttachmentIncomingFileGroups(); + var representations = classification.getRepresentationIncomingFiles(); - private FormData mapToFormDataWithRepresentationsAndAttachments( - List<IncomingFile> representations, - List<IncomingFileGroup> attachments) { return FormData.builder() .attachments(attachments) .numberOfAttachments(attachments.size()) .representations(representations) .numberOfRepresentations(representations.size()) + .control(FormDataControl.builder() + .metaData(Optional.of(buildMetaData(depositData))) + .representations(Optional.of(buildControlRepresentations(classification))) + .build()) .build(); } - Map<String, IncomingFile> mapDepositAttachmentsToSortedIncomingFiles(DepositData depositData) { - var incomingFilesMap = mapDepositAttachmentsToIncomingFiles(depositData); - var primaryId = depositData.getPrimaryDataAttachmentId(); - incomingFilesMap.putFirst(primaryId, getIncomingFileById(primaryId, incomingFilesMap)); - return incomingFilesMap; + DepositIncomingFiles mapDepositIncomingFiles(DepositData depositData) { + return DepositIncomingFiles.builder() + .primaryFormDataFileVendorId(depositData.getPrimaryDataAttachmentId()) + .incomingFileMap(mapDepositAttachmentsToIncomingFiles(depositData)) + .build(); } private LinkedHashMap<String, IncomingFile> mapDepositAttachmentsToIncomingFiles(DepositData depositData) { @@ -136,62 +92,6 @@ class DepositDataMapper { .build(); } - private Document parsePrimaryXmlRepresentation(DepositData depositData, Map<String, IncomingFile> incomingFileMap) { - // Expect that the <primaryDataAttachmentId> refers to the XML file - return parseDocument( - getIncomingFileById(depositData.getPrimaryDataAttachmentId(), incomingFileMap)); - } - - private static Document parseDocument(IncomingFile incomingFile) { - try (var inputStream = incomingFile.getContentStream()) { - return DOCUMENT_BUILDER.parse(inputStream); - } catch (SAXException | IOException e) { - throw new TechnicalException("Failed to parse xml document!", e); - } - } - - Map<String, List<String>> findAttachmentGroups(Document document) { - return streamElements(document.getElementsByTagName("file")) - .collect(groupingBy( - element -> element.getParentNode().getNodeName(), - mapping( - element -> element.getAttribute("id"), - toList()))); - } - - private List<IncomingFileGroup> getAttachmentFileGroups(Map<String, List<String>> attachmentGroups, Map<String, IncomingFile> incomingFileMap) { - return attachmentGroups.entrySet().stream() - .map(entry -> IncomingFileGroup.builder() - .name(entry.getKey()) - .files(entry.getValue().stream().map(id -> getIncomingFileById(id, incomingFileMap)).toList()) - .build()) - .toList(); - } - - private List<IncomingFile> getRepresentations(Map<String, IncomingFile> incomingFileMap, List<String> attachmentFileIds) { - return getNamesWithout(incomingFileMap.keySet(), attachmentFileIds).stream() - .map(id -> getIncomingFileById(id, incomingFileMap)) - .toList(); - } - - private IncomingFile getIncomingFileById(String id, Map<String, IncomingFile> incomingFileMap) { - if (!incomingFileMap.containsKey(id)) { - throw new TechnicalException("Failed to find <file> attachment ID '%s' in deposit data!".formatted(id)); - } - return incomingFileMap.get(id); - } - - private List<String> getNamesWithout(Collection<String> names, Collection<String> excludedStrings) { - var excludedStringsSet = Set.copyOf(excludedStrings); - return names.stream().filter(name -> !excludedStringsSet.contains(name)).toList(); - } - - private Stream<Element> streamElements(NodeList nodeList) { - return IntStream.range(0, nodeList.getLength()) - .mapToObj(nodeList::item) - .map(Element.class::cast); - } - IntelliFormMetaData buildMetaData(DepositData depositData) { var builder = IntelliFormMetaData.builder(); getOrigin(depositData).ifPresent(builder::origin); @@ -203,7 +103,16 @@ class DepositDataMapper { } Optional<ZonedDateTime> getOrigin(DepositData depositData) { - return Optional.ofNullable(depositData.getTimestamp()).map(XMLGregorianCalendar::toGregorianCalendar).map(GregorianCalendar::toZonedDateTime); + return Optional.ofNullable(depositData.getTimestamp()) + .map(XMLGregorianCalendar::toGregorianCalendar) + .map(GregorianCalendar::toZonedDateTime); + } + + FormData.Representations buildControlRepresentations(DepositIncomingFilesClassification classification) { + return FormData.Representations.builder() + .primaryFormDataRepresentation(classification.primaryFormDataFileName()) + .primaryFormDataPdfRepresentation(classification.primaryFormDataPdfFileName()) + .build(); } } diff --git a/src/main/java/de/ozgcloud/eingang/intelliform/DepositIncomingFiles.java b/src/main/java/de/ozgcloud/eingang/intelliform/DepositIncomingFiles.java new file mode 100644 index 0000000000000000000000000000000000000000..1fdf110de85d807be5e2d069615c9f519d7afb8f --- /dev/null +++ b/src/main/java/de/ozgcloud/eingang/intelliform/DepositIncomingFiles.java @@ -0,0 +1,84 @@ +package de.ozgcloud.eingang.intelliform; + +import static java.util.stream.Collectors.*; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import de.ozgcloud.eingang.common.errorhandling.TechnicalException; +import de.ozgcloud.eingang.common.formdata.IncomingFile; +import lombok.Builder; + +@Builder +record DepositIncomingFiles( + String primaryFormDataFileVendorId, + LinkedHashMap<String, IncomingFile> incomingFileMap +) { + + private static final DocumentBuilder DOCUMENT_BUILDER = createDocumentBuilder(); + + public IncomingFile getIncomingFileByVendorId(String vendorId) { + if (!incomingFileMap.containsKey(vendorId)) { + throw new TechnicalException("Failed to find <file> attachment ID '%s' in deposit data!".formatted(vendorId)); + } + return incomingFileMap.get(vendorId); + } + + public DepositIncomingFilesClassification classifyFiles() { + return DepositIncomingFilesClassification.builder() + .attachmentGroups(findAttachmentGroups(parsePrimaryXmlRepresentation())) + .incomingFiles(this) + .build(); + } + + private Map<String, List<String>> findAttachmentGroups(Document document) { + return streamElements(document.getElementsByTagName("file")) + .collect(groupingBy( + element -> element.getParentNode().getNodeName(), + mapping( + element -> element.getAttribute("id"), + toList()))); + } + + private Stream<Element> streamElements(NodeList nodeList) { + return IntStream.range(0, nodeList.getLength()) + .mapToObj(nodeList::item) + .map(Element.class::cast); + } + + private Document parsePrimaryXmlRepresentation() { + return parseDocument(getIncomingFileByVendorId(primaryFormDataFileVendorId)); + } + + private static DocumentBuilder createDocumentBuilder() { + var documentBuilderFactory = DocumentBuilderFactory.newInstance(); + try { + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + return documentBuilderFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new TechnicalException("Failed to configure document builder", e); + } + } + + private static Document parseDocument(IncomingFile incomingFile) { + try (var inputStream = incomingFile.getContentStream()) { + return DOCUMENT_BUILDER.parse(inputStream); + } catch (SAXException | IOException e) { + throw new TechnicalException("Failed to parse xml document!", e); + } + } + +} diff --git a/src/main/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesClassification.java b/src/main/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesClassification.java new file mode 100644 index 0000000000000000000000000000000000000000..2fce49929a87818ca3aeba321e6460501dd62a9c --- /dev/null +++ b/src/main/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesClassification.java @@ -0,0 +1,60 @@ +package de.ozgcloud.eingang.intelliform; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import de.ozgcloud.eingang.common.formdata.IncomingFile; +import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; +import lombok.Builder; + +@Builder +record DepositIncomingFilesClassification( + Map<String, List<String>> attachmentGroups, + DepositIncomingFiles incomingFiles +) { + + public String primaryFormDataPdfFileName() { + return getRepresentationIncomingFiles().stream() + .map(IncomingFile::getName) + .filter(name -> name.endsWith(".pdf")) + .findFirst() + .orElse(""); + } + + public String primaryFormDataFileName() { + return incomingFiles.getIncomingFileByVendorId(incomingFiles.primaryFormDataFileVendorId()).getName(); + } + + public List<IncomingFileGroup> getAttachmentIncomingFileGroups() { + return attachmentGroups.entrySet().stream() + .map(entry -> IncomingFileGroup.builder() + .name(entry.getKey()) + .files(entry.getValue().stream() + .map(incomingFiles::getIncomingFileByVendorId) + .toList()) + .build()) + .toList(); + } + + public List<IncomingFile> getRepresentationIncomingFiles() { + return getNamesWithout(incomingFiles.incomingFileMap().keySet(), getAttachmentFileIds()).stream() + .map(incomingFiles::getIncomingFileByVendorId) + .toList(); + } + + private List<String> getAttachmentFileIds() { + return attachmentGroups.values().stream() + .flatMap(Collection::stream) + .toList(); + } + + private List<String> getNamesWithout(Collection<String> names, Collection<String> excludedStrings) { + var excludedStringsSet = Set.copyOf(excludedStrings); + return names.stream() + .filter(name -> !excludedStringsSet.contains(name)) + .toList(); + } + +} diff --git a/src/test/java/de/ozgcloud/eingang/intelliform/DepositDataMapperTest.java b/src/test/java/de/ozgcloud/eingang/intelliform/DepositDataMapperTest.java index 608c69ca8c1b4e5b6e8fd8c3c7d2a73ed23b2bb1..573e5a1906d41121d0a8c9bb1710b84af98ba8ee 100644 --- a/src/test/java/de/ozgcloud/eingang/intelliform/DepositDataMapperTest.java +++ b/src/test/java/de/ozgcloud/eingang/intelliform/DepositDataMapperTest.java @@ -25,21 +25,15 @@ package de.ozgcloud.eingang.intelliform; import static de.ozgcloud.eingang.intelliform.AttachmentTestFactory.*; import static de.ozgcloud.eingang.intelliform.DepositDataTestFactory.*; -import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.io.IOException; -import java.io.StringReader; import java.nio.charset.Charset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.List; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -47,16 +41,12 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.Mock; import org.mockito.Spy; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import de.ozgcloud.eingang.common.errorhandling.TechnicalException; import de.ozgcloud.eingang.common.formdata.FormData; import de.ozgcloud.eingang.common.formdata.IncomingFile; import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; -import de.ozgcloud.eingang.intelliform.AttachmentTestFactory.MetaAttachment; import lombok.SneakyThrows; class DepositDataMapperTest { @@ -68,201 +58,114 @@ class DepositDataMapperTest { @Nested class TestMapToFormData { - @Captor - private ArgumentCaptor<Attachment> attachmentArgumentCaptor; + private final DepositData depositData = DepositDataTestFactory.create(); + + @Mock + private IntelliFormMetaData intelliFormMetaData; + + @Mock + private FormData.Representations controlRepresentations; + + @Mock + private DepositIncomingFiles depositIncomingFiles; + + @Mock + private DepositIncomingFilesClassification classification; + + @Mock + private IncomingFileGroup attachmentIncomingFileGroup; + + @Mock + private IncomingFile represenationIncomingFile; + + @BeforeEach + void mock() { + doReturn(depositIncomingFiles).when(mapper).mapDepositIncomingFiles(any()); + when(depositIncomingFiles.classifyFiles()).thenReturn(classification); + when(classification.getAttachmentIncomingFileGroups()).thenReturn(List.of(attachmentIncomingFileGroup)); + when(classification.getRepresentationIncomingFiles()).thenReturn(List.of(represenationIncomingFile)); + doReturn(intelliFormMetaData).when(mapper).buildMetaData(any()); + doReturn(controlRepresentations).when(mapper).buildControlRepresentations(any()); + } + + @DisplayName("should call mapDepositIncomingFiles") + @Test + void shouldCallMapDepositIncomingFiles() { + doMapping(); + + verify(mapper).mapDepositIncomingFiles(depositData); + } + + @DisplayName("should call classifyFiles") + @Test + void shouldCallClassifyFiles() { + doMapping(); + + verify(depositIncomingFiles).classifyFiles(); + } + + @DisplayName("should call getAttachmentIncomingFileGroups") + @Test + void shouldCallGetAttachmentIncomingFileGroups() { + doMapping(); - private DepositData depositData; - - @DisplayName("with normal attachments") - @Nested - class TestWithNormalAttachments { - @BeforeEach - void mock() { - depositData = DepositDataTestFactory.create(ATTACHMENTS); - } - - @DisplayName("should throw technical exception if primary xml link is incorrect") - @Test - void shouldThrowTechnicalExceptionIfPrimaryXmlLinkIsIncorrect() { - depositData.setPrimaryDataAttachmentId("incorrect"); - - assertThatThrownBy(TestMapToFormData.this::doMapping) - .isInstanceOf(TechnicalException.class); - } - - @DisplayName("should use map to incoming file method") - @Test - void shouldUseMapToIncomingFileMethod() { - doMapping(); - verify(mapper, times(ATTACHMENTS.size())).mapAttachmentToIncomingFile(attachmentArgumentCaptor.capture()); - - assertThat(attachmentArgumentCaptor.getAllValues()).isEqualTo(ATTACHMENTS); - } - - @DisplayName("should return with representations") - @Test - void shouldReturnWithRepresentations() { - var formData = doMapping(); - - var incomingFileIds = formData.getRepresentations().stream().map(IncomingFile::getVendorId).toList(); - assertThat(incomingFileIds).containsExactly(XML_ATTACHMENT_ID, PDF_ATTACHMENT_ID); - } - - @DisplayName("should return with one attachment") - @Test - void shouldReturnWithOneAttachment() { - var formData = doMapping(); - - var incomingFileIds = formData.getAttachments().stream() - .flatMap(group -> group.getFiles().stream()) - .map(IncomingFile::getVendorId) - .toList(); - assertThat(incomingFileIds).containsExactly(PNG_ATTACHMENT_ID); - } - - @DisplayName("should return with attachment group name") - @Test - void shouldReturnWithAttachmentGroupName() { - var formData = doMapping(); - - var incomingFileIds = formData.getAttachments().stream() - .map(IncomingFileGroup::getName) - .toList(); - assertThat(incomingFileIds).containsExactly("Upload1"); - } - - @DisplayName("should return with number of representations") - @Test - void shouldReturnWithNumberOfRepresentations() { - var formData = doMapping(); - - assertThat(formData.getNumberOfRepresentations()).isEqualTo(2); - } - } - - @DisplayName("with duplicate keys") - @Nested - class TestWithDuplicateKeys { - @BeforeEach - void mock() { - depositData = DepositDataTestFactory.create(List.of( - withEmptyName(createXmlDaten()), - createXmlDaten(), - withEmptyName(createPdf()), - createPdf(), - withEmptyName(createPng()), - createPng())); - } - - private Attachment withEmptyName(Attachment attachment) { - attachment.setName(""); - return attachment; - } - - @DisplayName("should keep last entry for representations") - @Test - void shouldKeepLastEntryForRepresentations() { - var formData = doMapping(); - - var representationFiles = formData.getRepresentations(); - assertThat(getAttachmentVendorIds(representationFiles)).containsExactly(XML_ATTACHMENT_ID, PDF_ATTACHMENT_ID); - assertThat(getAttachmentFileNames(representationFiles)).containsExactly(XML_NAME, PDF_ATTACHMENT_NAME); - } - - @DisplayName("should keep last entry for attachments") - @Test - void shouldKeepLastEntryForAttachments() { - var formData = doMapping(); - - var attachmentFiles = formData.getAttachments().stream() - .map(IncomingFileGroup::getFiles) - .flatMap(List::stream) - .toList(); - assertThat(getAttachmentVendorIds(attachmentFiles)).containsExactly(PNG_ATTACHMENT_ID); - assertThat(getAttachmentFileNames(attachmentFiles)).containsExactly(PNG_ATTACHMENT_NAME); - } - - private List<String> getAttachmentFileNames(List<IncomingFile> incomingFileList) { - return incomingFileList.stream() - .map(IncomingFile::getName) - .toList(); - } - - private List<String> getAttachmentVendorIds(List<IncomingFile> incomingFileList) { - return incomingFileList.stream() - .map(IncomingFile::getVendorId) - .toList(); - } - } - - @DisplayName("with many attachments") - @Nested - class TestWithManyAttachments { - @BeforeEach - void mock() { - depositData = DepositDataTestFactory.create(MANY_ATTACHMENTS); - } - - @DisplayName("should return with representations") - @Test - void shouldReturnWithRepresentations() { - var formData = doMapping(); - - var incomingFileIds = formData.getRepresentations().stream() - .map(IncomingFile::getVendorId) - .toList(); - assertThat(incomingFileIds).containsExactly( - XML_ATTACHMENT_ID, - XML_ROHFORM_ATTACHMENT_ID, - XML_ORIGINALFORM_ATTACHMENT_ID); - } - - @DisplayName("should return with attachment groups") - @Test - void shouldReturnWithAttachmentGroups() { - var formData = doMapping(); - - var incomingFileIds = formData.getAttachments().stream() - .flatMap(group -> group.getFiles().stream()) - .map(IncomingFile::getVendorId) - .toList(); - assertThat(incomingFileIds).containsExactlyInAnyOrder( - DOCX1_ATTACHMENT_ID, - PDF_ATTACHMENT_ID, - DOCX2_ATTACHMENT_ID, - DOCX3_ATTACHMENT_ID, - DOCX4_ATTACHMENT_ID, - DOCX5_ATTACHMENT_ID, - DOCX6_ATTACHMENT_ID, - DOCX7_ATTACHMENT_ID, - DOCX8_ATTACHMENT_ID, - PDF2_ATTACHMENT_ID, - ODT_ATTACHMENT_ID, - JPG_ATTACHMENT_ID, - PNG_ATTACHMENT_ID); - } - } - - @DisplayName("with empty attachments") - @Nested - class TestWithEmptyAttachments { - @DisplayName("should throw technical exception") - @Test - void shouldThrowTechnicalException() { - depositData = DepositDataTestFactory.create(emptyList()); - - assertThatThrownBy(TestMapToFormData.this::doMapping) - .isInstanceOf(TechnicalException.class); - } + verify(classification).getAttachmentIncomingFileGroups(); } + @DisplayName("should call getRepresentationIncomingFiles") @Test - void shouldAddMetaData() { - depositData = DepositDataTestFactory.create(); + void shouldCallGetRepresentationIncomingFiles() { + doMapping(); + + verify(classification).getRepresentationIncomingFiles(); + } + + @DisplayName("should map representations") + @Test + void shouldMapRepresentations() { + var formData = doMapping(); + + assertThat(formData.getRepresentations()).containsExactly(represenationIncomingFile); + } + + @DisplayName("should map numberOfRepresentations") + @Test + void shouldMapNumberOfRepresentations() { + var formData = doMapping(); + + assertThat(formData.getNumberOfRepresentations()).isEqualTo(1); + } + + @DisplayName("should call buildMetaData") + @Test + void shouldCallBuildMetaData() { + doMapping(); + + verify(mapper).buildMetaData(depositData); + } + + @DisplayName("should map control metadata") + @Test + void shouldMapControlMetadata() { + var formData = doMapping(); + + assertThat(formData.getControl().getMetaData()).contains(intelliFormMetaData); + } + @DisplayName("should call buildControlRepresentations") + @Test + void shouldCallBuildControlRepresentations() { doMapping(); - verify(mapper).buildMetaData(any()); + verify(mapper).buildControlRepresentations(classification); + } + + @DisplayName("should map control representations") + @Test + void shouldMapControlRepresentations() { + var formData = doMapping(); + + assertThat(formData.getControl().getRepresentations()).contains(controlRepresentations); } private FormData doMapping() { @@ -271,63 +174,61 @@ class DepositDataMapperTest { } - @DisplayName("map deposit attachments to sorted incoming files") + @DisplayName("map DepositIncomingFiles") @Nested - class TestMapDepositAttachmentsToSortedIncomingFiles { + class TestMapDepositIncomingFiles { + private final DepositData depositData = DepositDataTestFactory.create(ATTACHMENTS); - @DisplayName("should fail without primaryDataAttachmentId attachment") - @Test - void shouldFailWithoutPrimaryDataAttachmentIdAttachment() { - var depositData = DepositDataTestFactory.create(emptyList()); + @Mock + private IncomingFile incomingFile1; - assertThatThrownBy(() -> mapper.mapDepositAttachmentsToSortedIncomingFiles(depositData)) - .isInstanceOf(TechnicalException.class); + @Mock + private IncomingFile incomingFile2; + + @Mock + private IncomingFile incomingFile3; + + @Captor + private ArgumentCaptor<Attachment> attachmentArgumentCaptor; + + @BeforeEach + void mock() { + doReturn(incomingFile1, incomingFile2, incomingFile3).when(mapper).mapAttachmentToIncomingFile(any()); } - @DisplayName("should keep entry order") + @DisplayName("should call mapAttachmentToIncomingFile") @Test - void shouldKeepEntryOrder() { - var depositData = DepositDataTestFactory.create(ATTACHMENTS); + void shouldCallMapAttachmentToIncomingFile() { + mapper.mapDepositIncomingFiles(depositData); - var incomingFileMap = mapper.mapDepositAttachmentsToSortedIncomingFiles(depositData); - - var keys = incomingFileMap.keySet().stream().toList(); - assertThat(keys).containsExactly(XML_ATTACHMENT_ID, PDF_ATTACHMENT_ID, PNG_ATTACHMENT_ID); + verify(mapper, times(ATTACHMENTS.size())).mapAttachmentToIncomingFile(attachmentArgumentCaptor.capture()); + assertThat(attachmentArgumentCaptor.getAllValues()) + .extracting(Attachment::getId) + .containsExactly(XML_ATTACHMENT_ID, PDF_ATTACHMENT_ID, PNG_ATTACHMENT_ID); } - @DisplayName("should move primary attachment id to first position") + @DisplayName("should map to attachment groups") @Test - void shouldMovePrimaryAttachmentIdToFirstPosition() { - var rohformId = "XML-daten-rohform"; - var depositData = DepositDataTestFactory.create(List.of( - AttachmentTestFactory.createAttachment(MetaAttachment.builder() - .id(rohformId) - .name("XML-Daten (Rohform).xml") - .contentType(XML_CONTENT_TYPE) - .content("abc") - .build()), - AttachmentTestFactory.createPdf(), - AttachmentTestFactory.createXmlDaten(), - AttachmentTestFactory.createPng())); - - var incomingFileMap = mapper.mapDepositAttachmentsToSortedIncomingFiles(depositData); - - var keys = incomingFileMap.keySet().stream().toList(); - assertThat(keys).containsExactly(XML_ATTACHMENT_ID, rohformId, PDF_ATTACHMENT_ID, PNG_ATTACHMENT_ID); - } - - @DisplayName("should keep last entry for duplicate key") + void shouldMapToAttachmentGroups() { + var result = mapper.mapDepositIncomingFiles(depositData); + + assertThat(result.primaryFormDataFileVendorId()).isEqualTo(XML_ATTACHMENT_ID); + } + + @DisplayName("should map vendorId keys") @Test - void shouldKeepLastEntryForDuplicateKey() { - var depositData = DepositDataTestFactory.create(List.of( - AttachmentTestFactory.createXmlDaten(), - AttachmentTestFactory.createPdf(), - AttachmentTestFactory.createXmlDaten())); + void shouldMapVendorIdKeys() { + var result = mapper.mapDepositIncomingFiles(depositData); + + assertThat(result.incomingFileMap()).containsOnlyKeys(XML_ATTACHMENT_ID, PDF_ATTACHMENT_ID, PNG_ATTACHMENT_ID); + } - var incomingFileMap = mapper.mapDepositAttachmentsToSortedIncomingFiles(depositData); + @DisplayName("should map incoming file values") + @Test + void shouldMapIncomingFileValues() { + var result = mapper.mapDepositIncomingFiles(depositData); - var keys = incomingFileMap.keySet().stream().toList(); - assertThat(keys).containsExactly(XML_ATTACHMENT_ID, PDF_ATTACHMENT_ID); + assertThat(result.incomingFileMap()).containsValues(incomingFile1, incomingFile2, incomingFile3); } } @@ -396,62 +297,6 @@ class DepositDataMapperTest { } } - @DisplayName("find attachment groups") - @Nested - class TestFindAttachmentGroups { - - private Document document; - - @BeforeEach - void mock() { - document = buildXMLDocument(""" - <myForm t:client-id="land"> - <Upload1> - <file content-type="image/png" description="" id="VendorId3333" length="155251">Image.png</file> - </Upload1> - <Upload2> - <file id="VendorId1111">name1.txt</file> - <file id="VendorId2222">name2.txt</file> - </Upload2> - <file id="VendorIdxxxx">namex.txt</file> - </myForm>"""); - } - - @DisplayName("should have groups with parent element name") - @Test - void shouldHaveGroupsWithParentElementName() { - var attachmentGroups = mapper.findAttachmentGroups(document); - - assertThat(attachmentGroups).containsOnlyKeys("Upload1", "Upload2", "myForm"); - } - - @DisplayName("should have Upload1 group with fileId") - @Test - void shouldHaveUpload1GroupWithFileId() { - var attachmentGroups = mapper.findAttachmentGroups(document); - - assertThat(attachmentGroups.get("Upload1")).containsExactly("VendorId3333"); - } - - @DisplayName("should have two fileIds in Upload2 group") - @Test - void shouldHaveTwoFileIdsInUpload2Group() { - var attachmentGroups = mapper.findAttachmentGroups(document); - - assertThat(attachmentGroups.get("Upload2")).containsExactly("VendorId1111", "VendorId2222"); - } - } - - private Document buildXMLDocument(String xmlString) { - try { - return DocumentBuilderFactory.newInstance() - .newDocumentBuilder() - .parse(new InputSource(new StringReader(xmlString))); - } catch (ParserConfigurationException | IOException | SAXException e) { - throw new RuntimeException(e); - } - } - @Nested class TestBuildMetaData { @@ -487,4 +332,37 @@ class DepositDataMapperTest { } } + @DisplayName("build control representations") + @Nested + class TestBuildControlRepresentations { + + @Mock + private DepositIncomingFilesClassification classification; + + private final String primaryFormDataFileName = "primaryFormDataFile.xml"; + private final String primaryFormDataPdfFileName = "primaryFormDataPdfFile.pdf"; + + @BeforeEach + void mock() { + when(classification.primaryFormDataFileName()).thenReturn(primaryFormDataFileName); + when(classification.primaryFormDataPdfFileName()).thenReturn(primaryFormDataPdfFileName); + } + + @DisplayName("should map primaryFormDataRepresentation") + @Test + void shouldMapPrimaryFormDataRepresentation() { + var representations = mapper.buildControlRepresentations(classification); + + assertThat(representations.getPrimaryFormDataRepresentation()).isEqualTo(primaryFormDataFileName); + } + + @DisplayName("should map primaryFormDataPdfRepresentation") + @Test + void shouldMapPrimaryFormDataPdfRepresentation() { + var representations = mapper.buildControlRepresentations(classification); + + assertThat(representations.getPrimaryFormDataPdfRepresentation()).isEqualTo(primaryFormDataPdfFileName); + } + } + } diff --git a/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesClassificationTest.java b/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesClassificationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..70381f26ef06db0488ed83021d22448f7f20a514 --- /dev/null +++ b/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesClassificationTest.java @@ -0,0 +1,106 @@ +package de.ozgcloud.eingang.intelliform; + +import static de.ozgcloud.eingang.intelliform.AttachmentTestFactory.*; +import static de.ozgcloud.eingang.intelliform.DepositIncomingFilesTestFactory.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import de.ozgcloud.eingang.common.formdata.IncomingFile; +import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; + +class DepositIncomingFilesClassificationTest { + + private final DepositIncomingFilesClassification classification = DepositIncomingFilesTestFactory.create() + .classifyFiles(); + + @DisplayName("get attachment incoming file groups") + @Nested + class TestGetAttachmentIncomingFileGroups { + @DisplayName("should map to three groups") + @Test + void shouldMapAttachmentGroupsToIncomingFileGroups() { + var result = getAttachmentIncomingFileGroups(); + + assertThat(result) + .extracting(IncomingFileGroup::getName).containsOnly("Upload1", "Upload2", "myForm"); + } + + @DisplayName("should map incoming files") + @Test + void shouldMapIncomingFiles() { + var result = getAttachmentIncomingFileGroups(); + + assertThat(result) + .flatExtracting(IncomingFileGroup::getFiles) + .extracting(IncomingFile::getId) + .containsOnly(PNG_FILE_ID, DOCX1_FILE_ID, DOCX2_FILE_ID, DOCX3_FILE_ID); + } + + private List<IncomingFileGroup> getAttachmentIncomingFileGroups() { + return classification.getAttachmentIncomingFileGroups(); + } + } + + @DisplayName("get representation incoming files") + @Nested + class TestGetRepresentationIncomingFiles { + @DisplayName("should map to two files") + @Test + void shouldMapToTwoFiles() { + var result = getRepresentationIncomingFiles(); + + assertThat(result) + .extracting(IncomingFile::getId) + .containsOnly(XML_FILE_ID, PDF_FILE_ID); + } + + private List<IncomingFile> getRepresentationIncomingFiles() { + return classification.getRepresentationIncomingFiles(); + } + } + + @DisplayName("primaryFormDataFileName") + @Nested + class TestPrimaryFormDataFileName { + @DisplayName("should return") + @Test + void shouldReturn() { + var result = classification.primaryFormDataFileName(); + + assertThat(result).isEqualTo(XML_FILE_NAME); + } + } + + @DisplayName("primaryFormDataPdfFileName") + @Nested + class TestPrimaryFormDataPdfFileName { + @DisplayName("should return") + @Test + void shouldReturn() { + var result = classification.primaryFormDataPdfFileName(); + + assertThat(result).isEqualTo(PDF_ATTACHMENT_NAME); + } + + @DisplayName("should return empty string if no PDF file is found") + @Test + void shouldReturnEmptyStringIfNoPdfFileIsFound() { + var incomingFilesMap = DepositIncomingFilesTestFactory.create().incomingFileMap(); + incomingFilesMap.remove(PDF_ATTACHMENT_ID); + var faultyDepositIncomingFiles = DepositIncomingFilesTestFactory.createBuilder() + .incomingFileMap(incomingFilesMap) + .build() + .classifyFiles(); + + var result = faultyDepositIncomingFiles.primaryFormDataPdfFileName(); + + assertThat(result).isEmpty(); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesTest.java b/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cae1ce0d1f4b407e3df3844a2326354e829162b0 --- /dev/null +++ b/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesTest.java @@ -0,0 +1,112 @@ +package de.ozgcloud.eingang.intelliform; + +import static de.ozgcloud.eingang.intelliform.AttachmentTestFactory.*; +import static de.ozgcloud.eingang.intelliform.DepositIncomingFilesTestFactory.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.LinkedHashMap; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import de.ozgcloud.eingang.common.errorhandling.TechnicalException; +import de.ozgcloud.eingang.common.formdata.IncomingFile; + +class DepositIncomingFilesTest { + + private final DepositIncomingFiles depositIncomingFiles = DepositIncomingFilesTestFactory.create(); + + @DisplayName("classify files") + @Nested + class TestClassifyFiles { + + @DisplayName("should have groups with parent element name") + @Test + void shouldHaveGroupsWithParentElementName() { + var result = classifyFiles(); + + assertThat(result.attachmentGroups()).containsOnlyKeys("Upload1", "Upload2", "myForm"); + } + + @DisplayName("should map one file to Upload1 group") + @Test + void shouldMapOneFileToUpload1Group() { + var result = classifyFiles(); + + assertThat(result.attachmentGroups().get("Upload1")).containsExactly(PNG_ATTACHMENT_ID); + } + + @DisplayName("should map two files to Upload2 group") + @Test + void shouldMapTwoFilesToUpload2Group() { + var result = classifyFiles(); + + assertThat(result.attachmentGroups().get("Upload2")).containsExactly(DOCX1_ATTACHMENT_ID, DOCX2_ATTACHMENT_ID); + } + + @DisplayName("should map one file to myForm group") + @Test + void shouldMapOneFileToMyFormGroup() { + var result = classifyFiles(); + + assertThat(result.attachmentGroups().get("myForm")).containsExactly(DOCX3_ATTACHMENT_ID); + } + + @DisplayName("should have incoming files") + @Test + void shouldHaveIncomingFiles() { + var result = classifyFiles(); + + assertThat(result.incomingFiles()).isEqualTo(depositIncomingFiles); + } + + @DisplayName("should throw technical exception with empty attachments") + @Test + void shouldThrowTechnicalExceptionWithEmptyAttachments() { + var faultyDepositIncomingFiles = DepositIncomingFilesTestFactory.createBuilder() + .incomingFileMap(new LinkedHashMap<>()) + .build(); + + assertThatThrownBy(faultyDepositIncomingFiles::classifyFiles) + .isInstanceOf(TechnicalException.class); + } + + @DisplayName("should throw technical exception if primary xml link is incorrect") + @Test + void shouldThrowTechnicalExceptionIfPrimaryXmlLinkIsIncorrect() { + var faultyDepositData = DepositIncomingFilesTestFactory.createBuilder() + .primaryFormDataFileVendorId("incorrect") + .build(); + + assertThatThrownBy(faultyDepositData::classifyFiles) + .isInstanceOf(TechnicalException.class); + } + + private DepositIncomingFilesClassification classifyFiles() { + return depositIncomingFiles.classifyFiles(); + } + } + + @DisplayName("get incoming file by vendorId") + @Nested + class TestGetIncomingFileByVendorId { + @DisplayName("should return") + @Test + void shouldReturn() { + var result = depositIncomingFiles.getIncomingFileByVendorId(XML_ATTACHMENT_ID); + + assertThat(result) + .extracting(IncomingFile::getId) + .isEqualTo(XML_FILE_ID); + } + + @DisplayName("should throw technical exception if vendor ID is not found") + @Test + void shouldThrowTechnicalExceptionIfVendorIdIsNotFound() { + assertThatThrownBy(() -> depositIncomingFiles.getIncomingFileByVendorId("incorrect")) + .isInstanceOf(TechnicalException.class); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesTestFactory.java b/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..40fd152989f6d6ddddecf21099862fa51bd454a4 --- /dev/null +++ b/src/test/java/de/ozgcloud/eingang/intelliform/DepositIncomingFilesTestFactory.java @@ -0,0 +1,77 @@ +package de.ozgcloud.eingang.intelliform; + +import static de.ozgcloud.eingang.intelliform.AttachmentTestFactory.*; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +import de.ozgcloud.common.binaryfile.TempFileUtils; +import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory; + +class DepositIncomingFilesTestFactory { + + static final String XML_FILE_ID = UUID.randomUUID().toString(); + static final String PDF_FILE_ID = UUID.randomUUID().toString(); + static final String PNG_FILE_ID = UUID.randomUUID().toString(); + static final String DOCX1_FILE_ID = UUID.randomUUID().toString(); + static final String DOCX2_FILE_ID = UUID.randomUUID().toString(); + static final String DOCX3_FILE_ID = UUID.randomUUID().toString(); + + static final String XML_MY_FORM_CONTENT = """ + <myForm t:client-id="land"> + <Upload1> + <file content-type="image/png" description="" id="%s" length="155251">Image.png</file> + </Upload1> + <Upload2> + <file id="%s">name1.txt</file> + <file id="%s">name2.txt</file> + </Upload2> + <file id="%s">namex.txt</file> + </myForm>""".formatted( + PNG_ATTACHMENT_ID, + DOCX1_ATTACHMENT_ID, + DOCX2_ATTACHMENT_ID, + DOCX3_ATTACHMENT_ID + ); + + static DepositIncomingFiles create() { + return createBuilder().build(); + } + + static DepositIncomingFiles.DepositIncomingFilesBuilder createBuilder() { + return DepositIncomingFiles.builder() + .primaryFormDataFileVendorId(XML_ATTACHMENT_ID) + .incomingFileMap(new LinkedHashMap<>(Map.of( + XML_ATTACHMENT_ID, IncomingFileTestFactory.createBuilder() + .id(XML_FILE_ID) + .vendorId(XML_ATTACHMENT_ID) + .name(XML_FILE_NAME) + .file(TempFileUtils.writeTmpFile(XML_MY_FORM_CONTENT)) + .build(), + PDF_ATTACHMENT_ID, IncomingFileTestFactory.createBuilder() + .id(PDF_FILE_ID) + .vendorId(PDF_ATTACHMENT_ID) + .name(PDF_ATTACHMENT_NAME) + .build(), + PNG_ATTACHMENT_ID, IncomingFileTestFactory.createBuilder() + .id(PNG_FILE_ID) + .name(PNG_ATTACHMENT_NAME) + .vendorId(PNG_ATTACHMENT_ID) + .build(), + DOCX1_ATTACHMENT_ID, IncomingFileTestFactory.createBuilder() + .id(DOCX1_FILE_ID) + .vendorId(DOCX1_ATTACHMENT_ID) + .build(), + DOCX2_ATTACHMENT_ID, IncomingFileTestFactory.createBuilder() + .id(DOCX2_FILE_ID) + .vendorId(DOCX2_ATTACHMENT_ID) + .build(), + DOCX3_ATTACHMENT_ID, IncomingFileTestFactory.createBuilder() + .id(DOCX3_FILE_ID) + .vendorId(DOCX3_ATTACHMENT_ID) + .build() + ))); + } + +} diff --git a/src/test/java/de/ozgcloud/eingang/intelliform/FormDataEndpointITCase.java b/src/test/java/de/ozgcloud/eingang/intelliform/FormDataEndpointITCase.java index d65f6776e38bf01ed14af5411f762528202063c0..4b99de0870a11c0a59a0582412120fb2a3e15353 100644 --- a/src/test/java/de/ozgcloud/eingang/intelliform/FormDataEndpointITCase.java +++ b/src/test/java/de/ozgcloud/eingang/intelliform/FormDataEndpointITCase.java @@ -177,7 +177,7 @@ class FormDataEndpointITCase { var representationVendorIds = eingang.getRepresentationsList().stream() .map(GrpcIncomingFile::getVendorId) .toList(); - assertThat(representationVendorIds).containsExactly( + assertThat(representationVendorIds).containsExactlyInAnyOrder( XML_ATTACHMENT_ID, XML_ROHFORM_ATTACHMENT_ID, XML_ORIGINALFORM_ATTACHMENT_ID);