diff --git a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java index a7336c244d458f062819dc4536b699744e665950..a62c58af97e8b2fb96cc9c8f0e31128a20a5d35c 100644 --- a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java +++ b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapper.java @@ -23,43 +23,72 @@ */ package de.ozgcloud.eingang.formsolutions; +import static java.util.Collections.*; + import java.io.File; -import java.util.Collections; import java.util.List; import java.util.Objects; +import jakarta.annotation.Nullable; + import org.springframework.stereotype.Component; import de.ozgcloud.eingang.common.formdata.IncomingFile; import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; +import de.ozgcloud.eingang.semantik.common.ZipAttachmentReader; +import lombok.extern.log4j.Log4j2; +@Log4j2 @Component class FormSolutionsAttachmentsMapper { public static final String ZIP = "zip"; public static final String FILE_NAME_ZIP_ATTACHMENT = "attachments.zip"; public static final String ZIP_CONTENT_TYPE = "application/zip"; - public static final String FILE_GROUP_ZIP_NAME = "gezippte Anhänge"; - public List<IncomingFileGroup> mapAttachments(File zipFile) { + public List<IncomingFileGroup> mapAttachments(@Nullable File zipFile) { if (Objects.nonNull(zipFile) && zipFile.length() > 0) { - return Collections.singletonList(buildFileGroup(buildZipFile(zipFile))); + return extractAttachmentGroups(zipFile); } - return Collections.emptyList(); + return emptyList(); } - private IncomingFileGroup buildFileGroup(IncomingFile zipFile) { - return IncomingFileGroup.builder() - .name(FILE_GROUP_ZIP_NAME) - .files(List.of(zipFile)) - .build(); + List<IncomingFileGroup> extractAttachmentGroups(File zipFile) { + var attachmentIncomingFiles = extractAttachments(zipFile); + return attachmentIncomingFiles.isEmpty() + ? emptyList() + : List.of(IncomingFileGroup.builder() + .name("Anhänge") + .files(attachmentIncomingFiles) + .build()); + } + + List<IncomingFile> extractAttachments(File zipFile) { + try { + return readFromZip(zipFile); + } catch (RuntimeException e) { + LOG.error("Cannot read source ZIP. Attach it as is.", e); + return List.of(buildZipFile(zipFile)); + } } - private IncomingFile buildZipFile(File zipFile) { + IncomingFile buildZipFile(File zipFile) { return IncomingFile.builder() .file(zipFile) .contentType(ZIP_CONTENT_TYPE) .name(FILE_NAME_ZIP_ATTACHMENT) .build(); } + + List<IncomingFile> readFromZip(File zipFile) { + var reader = buildReader(zipFile); + var readContent = reader.readContent(); + reader.deleteSourceFile(); + return readContent; + } + + ZipAttachmentReader buildReader(File zipFile) { + return ZipAttachmentReader.from(zipFile, null); + } + } \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java index c62262d90f231c1f1d9b91b23094fcec72ff889b..af212bdc945a13cc038ad756e4fe4bc4d53f274a 100644 --- a/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java +++ b/src/main/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapper.java @@ -26,6 +26,7 @@ package de.ozgcloud.eingang.formsolutions; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -40,6 +41,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.eingang.common.formdata.FormData; import de.ozgcloud.eingang.common.formdata.IncomingFile; +import de.ozgcloud.eingang.semantik.enginebased.FilesMapperHelper; import lombok.RequiredArgsConstructor; @Component @@ -67,22 +69,25 @@ class FormSolutionsRequestMapper { } FormData buildFormData(File jsonFile, FormSolutionsEingang eingang) { - var builder = FormData.builder() - .attachments(attachmentMapper.mapAttachments(eingang.getZip())) - .representation(buildJsonFile(jsonFile)); - var numberOfRepresentations = 1; - - if (Objects.nonNull(eingang.getPdf())) { - builder.representation(buildPdfFile(eingang.getPdf())); - numberOfRepresentations++; - } + var attachments = attachmentMapper.mapAttachments(eingang.getZip()); + var representations = getRepresentations(jsonFile, eingang); - return builder + return FormData.builder() + .attachments(attachments) + .representations(representations) .control(buildFormDataControl()) - .numberOfRepresentations(numberOfRepresentations) + .numberOfRepresentations(representations.size()) + .numberOfAttachments(FilesMapperHelper.countAttachedFiles(attachments)) .build(); } + List<IncomingFile> getRepresentations(File jsonFile, FormSolutionsEingang eingang) { + var jsonRepresentation = buildJsonFile(jsonFile); + return Objects.nonNull(eingang.getPdf()) + ? List.of(jsonRepresentation, buildPdfFile(eingang.getPdf())) + : List.of(jsonRepresentation); + } + FormData.FormDataControl buildFormDataControl() { return FormData.FormDataControl.builder() .representations(Optional.of(buildControlRepresentations())) diff --git a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java index 01ea26c58bd8f407da8a5ea7e9fd9cbb72aa6dcf..d220f095cb745339909d1d2851930e99832a0080 100644 --- a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java +++ b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsAttachmentsMapperTest.java @@ -24,74 +24,273 @@ package de.ozgcloud.eingang.formsolutions; import static de.ozgcloud.eingang.formsolutions.FormSolutionsAttachmentsMapper.*; -import static de.ozgcloud.eingang.formsolutions.FormSolutionsFilesTestFactory.*; import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.io.File; import java.util.List; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; 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 de.ozgcloud.common.binaryfile.TempFileUtils; -import de.ozgcloud.common.test.TestUtils; import de.ozgcloud.eingang.common.formdata.IncomingFile; import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; -import lombok.SneakyThrows; +import de.ozgcloud.eingang.semantik.common.ZipAttachmentReader; class FormSolutionsAttachmentsMapperTest { + @Spy @InjectMocks private FormSolutionsAttachmentsMapper mapper; + @Mock private File zipFile; - @BeforeEach - void writeZipFile() { - zipFile = TempFileUtils.writeTmpFile(ZIP_DECODED); - } + @DisplayName("map attachments") + @Nested + class TestMapAttachments { + + @Mock + private IncomingFileGroup incomingFileGroup; + + @DisplayName("with valid file") + @Nested + class TestWithValidFile { + @BeforeEach + void mock() { + when(zipFile.length()).thenReturn(1L); + doReturn(List.of(incomingFileGroup)).when(mapper).extractAttachmentGroups(any()); + } + + @DisplayName("should call extractAttachmentGroups") + @Test + void shouldCallExtractAttachmentGroups() { + mapper.mapAttachments(zipFile); + + verify(mapper).extractAttachmentGroups(zipFile); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = mapper.mapAttachments(zipFile); + + assertThat(result).containsExactly(incomingFileGroup); + } + } - @AfterEach - void delZipFile() { - zipFile.delete(); + @DisplayName("should return empty if file null") + @Test + void shouldReturnEmptyIfFileNull() { + var result = mapper.mapAttachments(null); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return empty if file empty") + @Test + void shouldReturnEmptyIfFileEmpty() { + when(zipFile.length()).thenReturn(0L); + + var result = mapper.mapAttachments(zipFile); + + assertThat(result).isEmpty(); + } } + @DisplayName("extract attachment groups") @Nested - class TestAttachmentsMapping { + class TestExtractAttachmentGroups { + + @Mock + private IncomingFile incomingFile; + + @DisplayName("with files") + @Nested + class TestWithFiles { + @BeforeEach + void mock() { + doReturn(List.of(incomingFile)).when(mapper).extractAttachments(any()); + } + + @DisplayName("should call extractAttachments") + @Test + void shouldCallExtractAttachments() { + mapper.extractAttachmentGroups(zipFile); + + verify(mapper).extractAttachments(zipFile); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = mapper.extractAttachmentGroups(zipFile); + + assertThat(result) + .flatExtracting(IncomingFileGroup::getFiles) + .containsExactly(incomingFile); + } + + @DisplayName("should return name") + @Test + void shouldReturnName() { + var result = mapper.extractAttachmentGroups(zipFile); + + assertThat(result) + .flatExtracting(IncomingFileGroup::getName) + .containsExactly("Anhänge"); + } + } + + @DisplayName("should return empty with no files") @Test - @SneakyThrows - void shouldParseZip() { - var map = mapper.mapAttachments(zipFile); + void shouldReturnEmptyWithNoFiles() { + doReturn(List.of()).when(mapper).extractAttachments(any()); + + var result = mapper.extractAttachmentGroups(zipFile); + + assertThat(result).isEmpty(); + } + } + + @DisplayName("extract attachments") + @Nested + class TestExtractAttachments { + @Mock + private IncomingFile incomingFile; - assertThat(TestUtils.contentStreamToByteArray(getAttachment(map).getContentStream())).isEqualTo(ZIP_DECODED); + @DisplayName("without exception") + @Nested + class TestWithoutException { + @BeforeEach + void mock() { + doReturn(List.of(incomingFile)).when(mapper).readFromZip(any()); + + } + + @DisplayName("should call readFromZip") + @Test + void shouldCallReadFromZip() { + mapper.extractAttachments(zipFile); + + verify(mapper).readFromZip(zipFile); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = mapper.extractAttachments(zipFile); + + assertThat(result).containsExactly(incomingFile); + } } + @DisplayName("with exception") + @Nested + class TestWithException { + @BeforeEach + void mock() { + doThrow(new RuntimeException()).when(mapper).readFromZip(any()); + doReturn(incomingFile).when(mapper).buildZipFile(any()); + } + + @DisplayName("should call buildZipFile") + @Test + void shouldCallBuildZipFile() { + mapper.extractAttachments(zipFile); + + verify(mapper).buildZipFile(zipFile); + } + + @DisplayName("should return zip") + @Test + void shouldReturnZip() { + var result = mapper.extractAttachments(zipFile); + + assertThat(result).containsExactly(incomingFile); + } + } + } + + @DisplayName("build zip file") + @Nested + class TestBuildZipFile { + @DisplayName("should map file") @Test - void shouldSetContentType() { - var map = mapper.mapAttachments(zipFile); + void shouldMapFile() { + var result = mapper.buildZipFile(zipFile); - assertThat(getAttachment(map).getContentType()).isEqualTo(ZIP_CONTENT_TYPE); + assertThat(result.getFile()).isEqualTo(zipFile); } + @DisplayName("should map content type") @Test - void shouldSetFileName() { - var map = mapper.mapAttachments(zipFile); + void shouldMapContentType() { + var result = mapper.buildZipFile(zipFile); - assertThat(getAttachment(map).getName()).isEqualTo(FILE_NAME_ZIP_ATTACHMENT); + assertThat(result.getContentType()).isEqualTo(ZIP_CONTENT_TYPE); } + @DisplayName("should map name") @Test - void shouldSetGroupName() { - var map = mapper.mapAttachments(zipFile); + void shouldMapName() { + var result = mapper.buildZipFile(zipFile); - assertThat(map.get(0).getName()).isEqualTo(FILE_GROUP_ZIP_NAME); + assertThat(result.getName()).isEqualTo(FILE_NAME_ZIP_ATTACHMENT); } } - private IncomingFile getAttachment(List<IncomingFileGroup> attachments) { - return attachments.get(0).getFiles().get(0); + @DisplayName("read from zip") + @Nested + class TestReadFromZip { + @Mock + private IncomingFile incomingFile; + + @Mock + private ZipAttachmentReader reader; + + @BeforeEach + void mock() { + doReturn(reader).when(mapper).buildReader(any()); + when(reader.readContent()).thenReturn(List.of(incomingFile)); + } + + @DisplayName("should call buildReader") + @Test + void shouldCallBuildReader() { + mapper.readFromZip(zipFile); + + verify(mapper).buildReader(zipFile); + } + + @DisplayName("should call readContent") + @Test + void shouldCallReadContent() { + mapper.readFromZip(zipFile); + + verify(reader).readContent(); + } + + @DisplayName("should call deleteSourceFile") + @Test + void shouldCallDeleteSourceFile() { + mapper.readFromZip(zipFile); + + verify(reader).deleteSourceFile(); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = mapper.readFromZip(zipFile); + + assertThat(result).containsExactly(incomingFile); + } } } diff --git a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java index b5627d9b795710d4e8612f830c41a0cd69bc822a..d8f5a1f7ab39bb8982ca735599c643669832fc1a 100644 --- a/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java +++ b/src/test/java/de/ozgcloud/eingang/formsolutions/FormSolutionsRequestMapperTest.java @@ -138,8 +138,7 @@ class FormSolutionsRequestMapperTest { @Mock private File zipFile; - @Mock - private IncomingFileGroup attachmentGroup; + private final IncomingFileGroup attachmentGroup = IncomingFileGroupTestFactory.create(); @Mock private IncomingFile jsonIncomingFile; @@ -153,9 +152,9 @@ class FormSolutionsRequestMapperTest { @BeforeEach void mock() { when(eingang.getZip()).thenReturn(zipFile); - when(attachmentMapper.mapAttachments(any())).thenReturn(List.of(attachmentGroup)); - doReturn(jsonIncomingFile).when(mapper).buildJsonFile(any()); + + doReturn(List.of(jsonIncomingFile)).when(mapper).getRepresentations(any(), any()); doReturn(formDataControl).when(mapper).buildFormDataControl(); } @@ -175,12 +174,12 @@ class FormSolutionsRequestMapperTest { assertThat(result.getAttachments()).containsExactly(attachmentGroup); } - @DisplayName("should call buildJsonFile") + @DisplayName("should call getRepresentations") @Test - void shouldCallBuildJsonFile() { + void shouldCallGetRepresentations() { buildFormData(); - verify(mapper).buildJsonFile(simpleJsonFile); + verify(mapper).getRepresentations(simpleJsonFile, eingang); } @DisplayName("should return representations") @@ -199,10 +198,60 @@ class FormSolutionsRequestMapperTest { assertThat(result.getControl()).isEqualTo(formDataControl); } + @DisplayName("should return numberOfRepresentations") + @Test + void shouldReturnNumberOfRepresentations() { + var result = buildFormData(); + + assertThat(result.getNumberOfRepresentations()).isEqualTo(1); + } + + @DisplayName("should return numberOfAttachments") + @Test + void shouldReturnNumberOfAttachments() { + var result = buildFormData(); + + assertThat(result.getNumberOfAttachments()).isEqualTo(1); + } + + private FormData buildFormData() { + return mapper.buildFormData(simpleJsonFile, eingang); + } + } + + @DisplayName("get representations") + @Nested + class TestGetRepresentations { + + @Mock + private FormSolutionsEingang eingang; + + @Mock + private IncomingFile jsonIncomingFile; + + @BeforeEach + void mock() { + doReturn(jsonIncomingFile).when(mapper).buildJsonFile(any()); + } + + @DisplayName("should call buildJsonFile") + @Test + void shouldCallBuildJsonFile() { + getRepresentations(); + + verify(mapper).buildJsonFile(simpleJsonFile); + } + @DisplayName("with pdf") @Nested class TestWithPdf { + @Mock + private File pdfFile; + + @Mock + private IncomingFile pdfIncomingFile; + @BeforeEach void mock() { when(eingang.getPdf()).thenReturn(pdfFile); @@ -212,7 +261,7 @@ class FormSolutionsRequestMapperTest { @DisplayName("should call buildPdfFile") @Test void shouldCallBuildPdfFile() { - buildFormData(); + getRepresentations(); verify(mapper).buildPdfFile(eingang.getPdf()); } @@ -220,14 +269,24 @@ class FormSolutionsRequestMapperTest { @DisplayName("should return representations") @Test void shouldReturnRepresentations() { - var result = buildFormData(); + var result = getRepresentations(); - assertThat(result.getRepresentations()).containsExactly(jsonIncomingFile, pdfIncomingFile); + assertThat(result).containsExactly(jsonIncomingFile, pdfIncomingFile); } } - private FormData buildFormData() { - return mapper.buildFormData(simpleJsonFile, eingang); + @DisplayName("should return without pdf") + @Test + void shouldReturnWithoutPdf() { + when(eingang.getPdf()).thenReturn(null); + + var result = getRepresentations(); + + assertThat(result).containsExactly(jsonIncomingFile); + } + + private List<IncomingFile> getRepresentations() { + return mapper.getRepresentations(simpleJsonFile, eingang); } }