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

OZG-3452 OZG-3826 add size information to formsolutions representations

parent facd4c20
No related branches found
No related tags found
No related merge requests found
...@@ -33,10 +33,12 @@ import java.util.Optional; ...@@ -33,10 +33,12 @@ import java.util.Optional;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFileGroup; import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFileGroup;
@Component @Component
class FormSolutionsAttachmentsMapper { class FormSolutionsAttachmentsMapper {
public static final String ZIP = "zip"; public static final String ZIP = "zip";
public static final String FILE_NAME_ZIP_ATTACHMENT = "attachments.zip"; public static final String FILE_NAME_ZIP_ATTACHMENT = "attachments.zip";
public static final String ZIP_CONTENT_TYPE = "application/zip"; public static final String ZIP_CONTENT_TYPE = "application/zip";
...@@ -47,15 +49,31 @@ class FormSolutionsAttachmentsMapper { ...@@ -47,15 +49,31 @@ class FormSolutionsAttachmentsMapper {
} }
List<IncomingFileGroup> mapZipRepresentation(Optional<String> encodedZip) { List<IncomingFileGroup> mapZipRepresentation(Optional<String> encodedZip) {
var fileGroups = new ArrayList<IncomingFileGroup>(); return encodedZip.filter(StringUtils::isNoneEmpty)
encodedZip.filter(StringUtils::isNoneEmpty).ifPresent(content -> { .map(this::buildZipFile)
var files = List.of(mapFile(decodeFile(content), ZIP_CONTENT_TYPE, FILE_NAME_ZIP_ATTACHMENT)); .map(this::buildFileGroup)
fileGroups.add(IncomingFileGroup.builder() .map(this::buildMutableList)
.orElseGet(ArrayList::new);
}
private IncomingFileGroup buildFileGroup(IncomingFile zipFile) {
return IncomingFileGroup.builder()
.name(FILE_GROUP_ZIP_NAME) .name(FILE_GROUP_ZIP_NAME)
.files(files) .files(List.of(zipFile))
.build()); .build();
}); }
private IncomingFile buildZipFile(String content) {
return IncomingFile.builder()
.contentStream(decodeFile(content))
.contentType(ZIP_CONTENT_TYPE)
.name(FILE_NAME_ZIP_ATTACHMENT)
.build();
}
return fileGroups; private List<IncomingFileGroup> buildMutableList(IncomingFileGroup fileGroup) {
var list = new ArrayList<IncomingFileGroup>();
list.add(fileGroup);
return list;
} }
} }
\ No newline at end of file
...@@ -26,22 +26,12 @@ package de.itvsh.kop.eingangsadapter.formsolutions; ...@@ -26,22 +26,12 @@ package de.itvsh.kop.eingangsadapter.formsolutions;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.Base64; import java.util.Base64;
import java.util.UUID;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
class FormSolutionsFileMapperUtils { class FormSolutionsFileMapperUtils {
static IncomingFile mapFile(InputStream data, String contentType, String fileName) {
return IncomingFile.builder()
.contentStream(data)
.contentType(contentType)
.name(fileName)
.id(UUID.randomUUID().toString())
.build();
}
static InputStream decodeFile(String base64FileContent) { static InputStream decodeFile(String base64FileContent) {
// TODO ins Dateisystem schreiben, anstatt in Memory halten // TODO ins Dateisystem schreiben, anstatt in Memory halten
......
...@@ -23,9 +23,12 @@ ...@@ -23,9 +23,12 @@
*/ */
package de.itvsh.kop.eingangsadapter.formsolutions; package de.itvsh.kop.eingangsadapter.formsolutions;
import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFileMapperUtils.*;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -35,10 +38,12 @@ import org.apache.commons.lang3.StringUtils; ...@@ -35,10 +38,12 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.itvsh.kop.common.errorhandling.TechnicalException;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile; import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile;
@Component @Component
class FormSolutionsRepresentationsMapper { class FormSolutionsRepresentationsMapper {
public static final String PDF = "pdf"; public static final String PDF = "pdf";
public static final String FILE_NAME_PDF_REP = "eingang.pdf"; public static final String FILE_NAME_PDF_REP = "eingang.pdf";
public static final String PDF_CONTENT_TYPE = MediaType.APPLICATION_PDF_VALUE; public static final String PDF_CONTENT_TYPE = MediaType.APPLICATION_PDF_VALUE;
...@@ -47,21 +52,76 @@ class FormSolutionsRepresentationsMapper { ...@@ -47,21 +52,76 @@ class FormSolutionsRepresentationsMapper {
public static final String FILE_NAME_JSON_REP = "form-data.json"; public static final String FILE_NAME_JSON_REP = "form-data.json";
public static final String JSON_CONTENT_TYPE = MediaType.APPLICATION_JSON_VALUE; public static final String JSON_CONTENT_TYPE = MediaType.APPLICATION_JSON_VALUE;
public static final String TMP_FILE_PREFIX = "filecached-inputstream-fs";
public static final String TMP_FILE_SUFFIX = ".ozg-cloud.tmp";
List<IncomingFile> mapRepresentations(Map<String, Object> plainMap, Optional<String> json) { List<IncomingFile> mapRepresentations(Map<String, Object> plainMap, Optional<String> json) {
List<IncomingFile> representations = new ArrayList<>(); List<IncomingFile> representations = new ArrayList<>();
Optional.ofNullable((String) plainMap.get(PDF)).filter(StringUtils::isNoneEmpty).ifPresent(data -> representations.add(mapPdfFile(data))); Optional.ofNullable((String) plainMap.get(PDF)).filter(StringUtils::isNoneEmpty).ifPresent(data -> representations.add(buildPdfFile(data)));
json.ifPresent(jsonData -> representations.add(mapJsonFile(jsonData))); json.ifPresent(jsonData -> representations.add(buildJsonFile(jsonData)));
return representations; return representations;
} }
private IncomingFile mapJsonFile(String jsonData) { private IncomingFile buildJsonFile(String jsonData) {
return mapFile(new ByteArrayInputStream(jsonData.getBytes()), JSON_CONTENT_TYPE, FILE_NAME_JSON_REP); var size = getSize(new ByteArrayInputStream(jsonData.getBytes()));
return IncomingFile.builder()
.contentStream(new ByteArrayInputStream(jsonData.getBytes()))
.contentType(JSON_CONTENT_TYPE)
.name(FILE_NAME_JSON_REP)
.size(size)
.build();
}
private IncomingFile buildPdfFile(String data) {
var size = getSize(FormSolutionsFileMapperUtils.decodeFile(data));
return IncomingFile.builder()
.contentStream(FormSolutionsFileMapperUtils.decodeFile(data))
.contentType(PDF_CONTENT_TYPE)
.name(FILE_NAME_PDF_REP)
.size(size)
.build();
}
long getSize(InputStream contentStream) {
var path = writeFile(contentStream);
var size = getFileSize(path);
deleteTmpFile(path);
return size;
}
Path writeFile(InputStream contentStream) {
Path tmpFile;
try {
tmpFile = Files.createTempFile(TMP_FILE_PREFIX, TMP_FILE_SUFFIX);
tmpFile.toFile().deleteOnExit();
Files.copy(contentStream, tmpFile, StandardCopyOption.REPLACE_EXISTING);
contentStream.close();
return tmpFile;
} catch (IOException e) {
throw new TechnicalException("Error writing file to temp file.", e);
}
}
long getFileSize(Path path) {
try {
var size = Files.size(path);
Files.deleteIfExists(path);
return size;
} catch (IOException e) {
throw new TechnicalException("Error calculate size of tmp file.", e);
}
} }
private IncomingFile mapPdfFile(String data) { void deleteTmpFile(Path path) {
return mapFile(decodeFile(data), PDF_CONTENT_TYPE, FILE_NAME_PDF_REP); try {
Files.deleteIfExists(path);
} catch (IOException e) {
throw new TechnicalException("Error delete tmp file.", e);
}
} }
} }
\ No newline at end of file
...@@ -27,38 +27,19 @@ import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFileMapper ...@@ -27,38 +27,19 @@ import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFileMapper
import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFilesTestFactory.*; import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFilesTestFactory.*;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import java.io.ByteArrayInputStream;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import de.itvsh.kop.common.test.TestUtils; import de.itvsh.kop.common.test.TestUtils;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile;
import lombok.SneakyThrows; import lombok.SneakyThrows;
class FormSolutionsFileMapperUtilsTest { class FormSolutionsFileMapperUtilsTest {
@DisplayName("Test mapping File data to IncommingFIle")
@Nested
class TestFileMapping {
private static final String NAME = "test.txt";
private static final String APPLICATION_TYPE = "application/text";
private static final byte[] CONTENT_BYTES = "test".getBytes();
@Test
@SneakyThrows
void shouldMapFileData() {
IncomingFile file = FormSolutionsFileMapperUtils.mapFile(new ByteArrayInputStream(CONTENT_BYTES), APPLICATION_TYPE, NAME);
assertThat(TestUtils.contentStreamToByteArray(file.getContentStream())).isEqualTo(CONTENT_BYTES);
assertThat(file.getContentType()).isEqualTo(APPLICATION_TYPE);
assertThat(file.getName()).isEqualTo(NAME);
}
}
@DisplayName("Test decoding base64 encoded file") @DisplayName("Test decoding base64 encoded file")
@Nested @Nested
class TestDecodingBase64Content { class TestDecodingBase64Content {
@Test @Test
@SneakyThrows @SneakyThrows
void shouldDecodeFile() { void shouldDecodeFile() {
......
...@@ -27,80 +27,116 @@ import static de.itvsh.kop.eingangsadapter.common.formdata.FormSolutionsTestFact ...@@ -27,80 +27,116 @@ import static de.itvsh.kop.eingangsadapter.common.formdata.FormSolutionsTestFact
import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFilesTestFactory.*; import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsFilesTestFactory.*;
import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsRepresentationsMapper.*; import static de.itvsh.kop.eingangsadapter.formsolutions.FormSolutionsRepresentationsMapper.*;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Spy;
import de.itvsh.kop.common.errorhandling.TechnicalException;
import de.itvsh.kop.common.test.TestUtils; import de.itvsh.kop.common.test.TestUtils;
import de.itvsh.kop.eingangsadapter.common.formdata.FormSolutionsTestFactory; import de.itvsh.kop.eingangsadapter.common.formdata.FormSolutionsTestFactory;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile; import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFile;
import de.itvsh.kop.eingangsadapter.common.formdata.IncomingFileTestFactory;
import lombok.SneakyThrows; import lombok.SneakyThrows;
class FormSolutionsRepresentationsMapperTest { class FormSolutionsRepresentationsMapperTest {
@Spy
private FormSolutionsRepresentationsMapper mapper = new FormSolutionsRepresentationsMapper(); private FormSolutionsRepresentationsMapper mapper = new FormSolutionsRepresentationsMapper();
@DisplayName("Map representations")
@Nested @Nested
class TestRepresentationsMapping { class TestMapRepresentations {
@DisplayName("pdf")
@Nested @Nested
class TestPdfRepresentations { class TestPdfRepresentations {
@Test @Test
@SneakyThrows @SneakyThrows
void shouldParsePdf() { void shouldSetContentStrean() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(JSON_CONTENT)); var representation = mapRepresentationPdf();
assertThat(TestUtils.contentStreamToByteArray(getRepresentation(map, 0).getContentStream())).isEqualTo(PDF_DECODED); assertThat(TestUtils.contentStreamToByteArray(representation.getContentStream())).isEqualTo(PDF_DECODED);
} }
@Test @Test
void shouldSetPdfContentType() { void shouldSetHaveContentType() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(JSON_CONTENT)); var representation = mapRepresentationPdf();
assertThat(getRepresentation(map, 0).getContentType()).isEqualTo(PDF_CONTENT_TYPE); assertThat(representation.getContentType()).isEqualTo(PDF_CONTENT_TYPE);
} }
@Test @Test
void shouldSetPdfFileName() { void shouldSetName() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(JSON_CONTENT)); var representation = mapRepresentationPdf();
assertThat(getRepresentation(map, 0).getName()).isEqualTo(FILE_NAME_PDF_REP); assertThat(representation.getName()).isEqualTo(FILE_NAME_PDF_REP);
} }
@Test
void shouldSetSize() {
var representation = mapRepresentationPdf();
assertThat(representation.getSize()).isEqualTo(FormSolutionsFilesTestFactory.PDF_DECODED.length);
} }
private IncomingFile mapRepresentationPdf() {
return mapRepresentation(JSON_CONTENT).get(0);
}
}
@DisplayName("json")
@Nested @Nested
class TestJsonRepresentation { class TestJsonRepresentation {
@Test
void shouldHaveSize() {
var map = mapRepresentation(SIMPLE_JSON_DATA);
assertThat(map).hasSize(2);
}
@Test @Test
@SneakyThrows @SneakyThrows
void shouldParseJson() { void shouldSetContentStream() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(SIMPLE_JSON_DATA)); var representation = mapRepresentationJson();
assertThat(TestUtils.contentStreamToByteArray(getRepresentation(map, 1).getContentStream())) assertThat(TestUtils.contentStreamToByteArray(representation.getContentStream()))
.isEqualTo(FormSolutionsTestFactory.SIMPLE_JSON_DATA.getBytes()); .isEqualTo(FormSolutionsTestFactory.SIMPLE_JSON_DATA.getBytes());
} }
@Test @Test
void shouldSetJsonContentType() { void shouldSetContentType() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(SIMPLE_JSON_DATA)); var representation = mapRepresentationJson();
assertThat(getRepresentation(map, 1).getContentType()).isEqualTo(JSON_CONTENT_TYPE); assertThat(representation.getContentType()).isEqualTo(JSON_CONTENT_TYPE);
} }
@Test @Test
void shouldSetJsonFileName() { void shouldSetName() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(SIMPLE_JSON_DATA)); var representation = mapRepresentationJson();
assertThat(getRepresentation(map, 1).getName()).isEqualTo(FILE_NAME_JSON_REP); assertThat(representation.getName()).isEqualTo(FILE_NAME_JSON_REP);
} }
@Test @Test
void shouldParse() { void shouldSetSize() {
var map = mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(SIMPLE_JSON_DATA)); var representation = mapRepresentationJson();
assertThat(map).hasSize(2); assertThat(representation.getSize()).isEqualTo(SIMPLE_JSON_DATA.getBytes().length);
} }
@Test @Test
...@@ -111,12 +147,67 @@ class FormSolutionsRepresentationsMapperTest { ...@@ -111,12 +147,67 @@ class FormSolutionsRepresentationsMapperTest {
Optional.of(SIMPLE_JSON_DATA)); Optional.of(SIMPLE_JSON_DATA));
assertThat(map).hasSize(1); assertThat(map).hasSize(1);
assertThat(getRepresentation(map, 0).getContentType()).isEqualTo(JSON_CONTENT_TYPE); assertThat(map.get(0).getContentType()).isEqualTo(JSON_CONTENT_TYPE);
}
private IncomingFile mapRepresentationJson() {
return mapRepresentation(SIMPLE_JSON_DATA).get(1);
}
} }
private List<IncomingFile> mapRepresentation(String content) {
return mapper.mapRepresentations(REPRESENTATIONS.getFormData(), Optional.of(content));
} }
} }
private IncomingFile getRepresentation(List<IncomingFile> map, int index) { @DisplayName("Get Size")
return map.get(index); @Nested
class TestGetSize {
@Mock
private InputStream stream;
@Mock
private Path path;
@BeforeEach
void mock() {
doReturn(path).when(mapper).writeFile(any());
doReturn(IncomingFileTestFactory.SIZE).when(mapper).getFileSize(any());
doNothing().when(mapper).deleteTmpFile(any());
}
@Test
void shouldWriteFile() {
mapper.getSize(stream);
verify(mapper).writeFile(stream);
}
@Test
void shouldGetFileSize() {
mapper.getSize(stream);
verify(mapper).getFileSize(path);
}
@Test
void shouldDeleteTmpFile() {
mapper.getSize(stream);
verify(mapper).deleteTmpFile(path);
}
}
@Nested
class TestWriteFile {
@Test
@SneakyThrows
void shouldThrowTechnicalException() {
InputStream closedStream = mock(InputStream.class);
when(closedStream.transferTo(any())).thenThrow(IOException.class);
assertThatThrownBy(() -> mapper.writeFile(closedStream)).isInstanceOf(TechnicalException.class);
}
} }
} }
...@@ -46,10 +46,9 @@ class FormSolutionsFilesMapper implements FormSolutionsEngineBasedMapper { ...@@ -46,10 +46,9 @@ class FormSolutionsFilesMapper implements FormSolutionsEngineBasedMapper {
public FormData parseFormData(FormData srcFormData) { public FormData parseFormData(FormData srcFormData) {
var formDataBuilder = FilesMapperHelper.getAttachedFileGroups(srcFormData) var formDataBuilder = FilesMapperHelper.getAttachedFileGroups(srcFormData)
.map(this::collectAttachmentContent) .map(this::collectAttachmentContent)
.map(fileGroups -> srcFormData.toBuilder() .map(fileGroups -> srcFormData.toBuilder().attachments(fileGroups).numberOfAttachments(FilesMapperHelper.countAttachedFiles(fileGroups)))
.attachments(fileGroups)
.numberOfAttachments(FilesMapperHelper.countAttachedFiles(fileGroups)))
.orElseGet(srcFormData::toBuilder); .orElseGet(srcFormData::toBuilder);
FilesMapperHelper.getRepresentations(srcFormData) FilesMapperHelper.getRepresentations(srcFormData)
.ifPresent(representations -> formDataBuilder.representations(representations).numberOfRepresentations(representations.size())); .ifPresent(representations -> formDataBuilder.representations(representations).numberOfRepresentations(representations.size()));
return FilesMapperHelper.removeProcessedData(formDataBuilder.build()); return FilesMapperHelper.removeProcessedData(formDataBuilder.build());
...@@ -61,7 +60,6 @@ class FormSolutionsFilesMapper implements FormSolutionsEngineBasedMapper { ...@@ -61,7 +60,6 @@ class FormSolutionsFilesMapper implements FormSolutionsEngineBasedMapper {
.map(file -> ZipAttachmentReader.from(file.getContentStream(), file.getName())) .map(file -> ZipAttachmentReader.from(file.getContentStream(), file.getName()))
.map(this::mapToIncomingFileGroup) .map(this::mapToIncomingFileGroup)
.toList(); .toList();
} }
protected IncomingFileGroup mapToIncomingFileGroup(ZipAttachmentReader zipAttachment) { protected IncomingFileGroup mapToIncomingFileGroup(ZipAttachmentReader zipAttachment) {
...@@ -71,14 +69,18 @@ class FormSolutionsFilesMapper implements FormSolutionsEngineBasedMapper { ...@@ -71,14 +69,18 @@ class FormSolutionsFilesMapper implements FormSolutionsEngineBasedMapper {
zipAttachment.deleteSourceFile(); zipAttachment.deleteSourceFile();
} catch (RuntimeException e) { } catch (RuntimeException e) {
LOG.error("Cannot read source ZIP. Attach it as is.", e); LOG.error("Cannot read source ZIP. Attach it as is.", e);
incomingFiles = List.of(IncomingFile.builder() incomingFiles = List.of(buildIncomingZipFile(zipAttachment));
}
return createIncomingFileGroup(incomingFiles);
}
private IncomingFile buildIncomingZipFile(ZipAttachmentReader zipAttachment) {
return IncomingFile.builder()
.name(zipAttachment.getSourceFileName()) .name(zipAttachment.getSourceFileName())
.size(zipAttachment.getSourceFileSize()) .size(zipAttachment.getSourceFileSize())
.contentStream(zipAttachment.getSourceZipAsStream()) .contentStream(zipAttachment.getSourceZipAsStream())
.contentType(ZIP_CONTENT_TYPE) .contentType(ZIP_CONTENT_TYPE)
.build()); .build();
}
return createIncomingFileGroup(incomingFiles);
} }
protected IncomingFileGroup createIncomingFileGroup(Collection<IncomingFile> incomingFiles) { protected IncomingFileGroup createIncomingFileGroup(Collection<IncomingFile> incomingFiles) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment