RED-9771 - Fixes for updating component mappings
This commit is contained in:
parent
f41e99d2d6
commit
4795e3a591
@ -10,6 +10,7 @@ import static java.lang.String.format;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -81,11 +82,13 @@ import lombok.AccessLevel;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.experimental.FieldDefaults;
|
import lombok.experimental.FieldDefaults;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Tag(name = "1. Dossier templates endpoints", description = "Provides operations related to dossier templates")
|
@Tag(name = "1. Dossier templates endpoints", description = "Provides operations related to dossier templates")
|
||||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||||
|
@Slf4j
|
||||||
public class DossierTemplateControllerV2 implements DossierTemplateResource {
|
public class DossierTemplateControllerV2 implements DossierTemplateResource {
|
||||||
|
|
||||||
private static final String RULES_DOWNLOAD_FILE_NAME_SUFFIX = "-rules.drl";
|
private static final String RULES_DOWNLOAD_FILE_NAME_SUFFIX = "-rules.drl";
|
||||||
@ -247,8 +250,38 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
|
|||||||
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
|
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
|
||||||
public ComponentMappingMetadataModel updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String name, String encoding, String delimiter) {
|
public ComponentMappingMetadataModel updateMapping(String dossierTemplateId, String componentMappingId, MultipartFile file, String name, String encoding, String delimiter) {
|
||||||
|
|
||||||
|
log.info("Starting update");
|
||||||
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
|
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
|
||||||
|
|
||||||
|
String nameToUse = validateFileName(file, name);
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(delimiter)) {
|
||||||
|
throw new BadRequestException("The provided delimiter is not valid! Can't be null or empty.");
|
||||||
|
} else if (delimiter.length() != 1) {
|
||||||
|
throw new BadRequestException(format("The provided delimiter %s is not valid! Only a single character is allowed.", delimiter));
|
||||||
|
}
|
||||||
|
char cleanDelimiter = delimiter.charAt(0);
|
||||||
|
|
||||||
|
Path mappingFile = saveToFile(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ComponentMappingMetadata resultMetaData = componentMappingService.update(dossierTemplateId,
|
||||||
|
componentMappingId,
|
||||||
|
nameToUse,
|
||||||
|
encoding,
|
||||||
|
cleanDelimiter,
|
||||||
|
mappingFile.toFile(),
|
||||||
|
file.getOriginalFilename());
|
||||||
|
|
||||||
|
return componentMappingMapper.toModel(resultMetaData);
|
||||||
|
} finally {
|
||||||
|
Files.deleteIfExists(mappingFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String validateFileName(MultipartFile file, String name) {
|
||||||
|
|
||||||
if (Strings.isNullOrEmpty(file.getOriginalFilename()) || !file.getOriginalFilename().endsWith(".csv")) {
|
if (Strings.isNullOrEmpty(file.getOriginalFilename()) || !file.getOriginalFilename().endsWith(".csv")) {
|
||||||
throw new BadRequestException(format("File name \"%s\" does not end with .csv", file.getOriginalFilename()));
|
throw new BadRequestException(format("File name \"%s\" does not end with .csv", file.getOriginalFilename()));
|
||||||
}
|
}
|
||||||
@ -261,31 +294,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
|
|||||||
throw new BadRequestException(format("The provided file name \"%s\" is not valid!", nameToUse));
|
throw new BadRequestException(format("The provided file name \"%s\" is not valid!", nameToUse));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Strings.isNullOrEmpty(delimiter)) {
|
return nameToUse;
|
||||||
throw new BadRequestException("The provided delimiter is not valid! Can't be null or empty.");
|
|
||||||
} else if (delimiter.length() != 1) {
|
|
||||||
throw new BadRequestException(format("The provided delimiter %s is not valid! Only a single character is allowed.", delimiter));
|
|
||||||
}
|
|
||||||
char cleanDelimiter = delimiter.charAt(0);
|
|
||||||
|
|
||||||
if (Strings.isNullOrEmpty(delimiter) || delimiter.length() != 1) {
|
|
||||||
throw new BadRequestException("The provided delimiter is not valid! Only a single character is allowed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Path mappingFile = saveToFile(file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ComponentMappingMetadata resultMetaData = componentMappingService.update(dossierTemplateId,
|
|
||||||
componentMappingId,
|
|
||||||
nameToUse,
|
|
||||||
encoding,
|
|
||||||
cleanDelimiter,
|
|
||||||
mappingFile.toFile());
|
|
||||||
|
|
||||||
return componentMappingMapper.toModel(resultMetaData);
|
|
||||||
} finally {
|
|
||||||
Files.deleteIfExists(mappingFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -487,6 +496,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
|
|||||||
private static Path saveToFile(MultipartFile file) {
|
private static Path saveToFile(MultipartFile file) {
|
||||||
|
|
||||||
Path mappingFile = Files.createTempFile(file.getName(), ".csv");
|
Path mappingFile = Files.createTempFile(file.getName(), ".csv");
|
||||||
|
log.info("Saving file {}", file.getName());
|
||||||
|
|
||||||
try (var out = new FileOutputStream(mappingFile.toFile())) {
|
try (var out = new FileOutputStream(mappingFile.toFile())) {
|
||||||
out.write(file.getBytes());
|
out.write(file.getBytes());
|
||||||
|
|||||||
@ -62,11 +62,11 @@ public class ComponentMappingService {
|
|||||||
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public ComponentMappingMetadata update(String dossierTemplateId, String mappingId, String name, String encoding, char delimiter, File mappingFile) {
|
public ComponentMappingMetadata update(String dossierTemplateId, String mappingId, String name, String encoding, char delimiter, File mappingFile, String fileName) {
|
||||||
|
|
||||||
ComponentMappingEntity entity = componentMappingPersistenceService.getEntityById(dossierTemplateId, mappingId);
|
ComponentMappingEntity entity = componentMappingPersistenceService.getEntityById(dossierTemplateId, mappingId);
|
||||||
|
|
||||||
return updateOrCreate(entity, name, encoding, delimiter, mappingFile);
|
return updateOrCreate(entity, name, encoding, delimiter, mappingFile, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -86,13 +86,13 @@ public class ComponentMappingService {
|
|||||||
.fileName(fileName)
|
.fileName(fileName)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return updateOrCreate(entity, name, encoding, delimiter, mappingFile);
|
return updateOrCreate(entity, name, encoding, delimiter, mappingFile, fileName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private ComponentMappingMetadata updateOrCreate(ComponentMappingEntity entity, String name, String encoding, char delimiter, File mappingFile) {
|
private ComponentMappingMetadata updateOrCreate(ComponentMappingEntity entity, String name, String encoding, char delimiter, File mappingFile, String fileName) {
|
||||||
|
|
||||||
Charset charset = resolveCharset(encoding);
|
Charset charset = resolveCharset(encoding);
|
||||||
|
|
||||||
@ -104,6 +104,7 @@ public class ComponentMappingService {
|
|||||||
entity.setNumberOfLines(stats.numberOfLines());
|
entity.setNumberOfLines(stats.numberOfLines());
|
||||||
entity.setColumnLabels(stats.columnLabels());
|
entity.setColumnLabels(stats.columnLabels());
|
||||||
entity.setVersion(entity.getVersion() + 1);
|
entity.setVersion(entity.getVersion() + 1);
|
||||||
|
entity.setFileName(fileName);
|
||||||
|
|
||||||
componentMappingPersistenceService.updateOrCreate(entity.getStorageId(), mappingFile, entity);
|
componentMappingPersistenceService.updateOrCreate(entity.getStorageId(), mappingFile, entity);
|
||||||
|
|
||||||
@ -139,6 +140,10 @@ public class ComponentMappingService {
|
|||||||
|
|
||||||
List<String[]> rows = reader.readAll();
|
List<String[]> rows = reader.readAll();
|
||||||
|
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
throw new BadRequestException("CSV file can not be empty!");
|
||||||
|
}
|
||||||
|
|
||||||
columnLabels = rows.remove(0); // remove header row
|
columnLabels = rows.remove(0); // remove header row
|
||||||
|
|
||||||
if (Arrays.stream(columnLabels)
|
if (Arrays.stream(columnLabels)
|
||||||
|
|||||||
@ -0,0 +1,98 @@
|
|||||||
|
package com.iqser.red.service.peristence.v1.server.integration.tests;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
|
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateExternalClient;
|
||||||
|
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
|
||||||
|
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
|
||||||
|
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
|
||||||
|
import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity;
|
||||||
|
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
|
||||||
|
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingPersistenceService;
|
||||||
|
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel;
|
||||||
|
|
||||||
|
import feign.FeignException;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
@EnableJpaAuditing
|
||||||
|
public class ComponentMappingTest extends AbstractPersistenceServerServiceTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DossierTesterAndProvider dossierTesterAndProvider;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DossierTemplateExternalClient dossierTemplateExternalClient;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private ComponentMappingPersistenceService componentMappingPersistenceService;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
public void testComponentMappingUpdate() {
|
||||||
|
|
||||||
|
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
|
||||||
|
|
||||||
|
var mockMultipartFile = new MockMultipartFile("file.csv",
|
||||||
|
"file.csv",
|
||||||
|
"application/csv",
|
||||||
|
IOUtils.toByteArray(new ClassPathResource("files/componentmapping/file.csv").getInputStream()));
|
||||||
|
|
||||||
|
ComponentMappingMetadataModel componentMappingMetadataModel = dossierTemplateExternalClient.uploadMapping(dossierTemplate.getId(), mockMultipartFile, "file", "UTF-8", ",");
|
||||||
|
|
||||||
|
assertEquals(componentMappingMetadataModel.getFileName(), mockMultipartFile.getOriginalFilename());
|
||||||
|
|
||||||
|
var updateMockMultipartFile = new MockMultipartFile("update_file.csv",
|
||||||
|
"update_file.csv",
|
||||||
|
"application/csv",
|
||||||
|
IOUtils.toByteArray(new ClassPathResource("files/componentmapping/update_file.csv").getInputStream()));
|
||||||
|
|
||||||
|
when(componentMappingPersistenceService.getEntityById(anyString(), anyString())).thenReturn(ComponentMappingEntity.builder()
|
||||||
|
.id(componentMappingMetadataModel.getId())
|
||||||
|
.dossierTemplate(new DossierTemplateEntity())
|
||||||
|
.storageId(buildStorageId(dossierTemplate.getId(), componentMappingMetadataModel.getId(), "file", "update_file.csv"))
|
||||||
|
.fileName("update_file.csv")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
componentMappingMetadataModel = dossierTemplateExternalClient.updateMapping(dossierTemplate.getId(), componentMappingMetadataModel.getId(), updateMockMultipartFile, "file", "UTF-8", ",");
|
||||||
|
|
||||||
|
assertEquals(componentMappingMetadataModel.getFileName(), updateMockMultipartFile.getOriginalFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
public void testCreateComponentMappingWithEmptyCsv() {
|
||||||
|
|
||||||
|
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
|
||||||
|
|
||||||
|
var mockMultipartFile = new MockMultipartFile("file.csv",
|
||||||
|
"empty.csv",
|
||||||
|
"application/csv",
|
||||||
|
IOUtils.toByteArray(new ClassPathResource("files/componentmapping/empty.csv").getInputStream()));
|
||||||
|
|
||||||
|
var result = assertThrows(FeignException.class, () -> dossierTemplateExternalClient.uploadMapping(dossierTemplate.getId(), mockMultipartFile, "file", "UTF-8", ","));
|
||||||
|
assertTrue(result.getMessage().contains("CSV file can not be empty!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String buildStorageId(String dossierTemplateId, String id, String name, String fileName) {
|
||||||
|
|
||||||
|
return dossierTemplateId + "/" + id + "_" + name + "_" + fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
1,"Eldon Base for stackable storage shelf, platinum",Muhammed MacIntyre,3,-213.25,38.94,35,Nunavut,Storage & Organization,0.8
|
||||||
|
2,"1.7 Cubic Foot Compact ""Cube"" Office Refrigerators",Barry French,293,457.81,208.16,68.02,Nunavut,Appliances,0.58
|
||||||
|
3,"Cardinal Slant-D® Ring Binder, Heavy Gauge Vinyl",Barry French,293,46.71,8.69,2.99,Nunavut,Binders and Binder Accessories,0.39
|
||||||
|
4,R380,Clay Rozendal,483,1198.97,195.99,3.99,Nunavut,Telephones and Communication,0.58
|
||||||
|
5,Holmes HEPA Air Purifier,Carlos Soltero,515,30.94,21.78,5.94,Nunavut,Appliances,0.5
|
||||||
|
6,G.E. Longer-Life Indoor Recessed Floodlight Bulbs,Carlos Soltero,515,4.43,6.64,4.95,Nunavut,Office Furnishings,0.37
|
||||||
|
7,"Angle-D Binders with Locking Rings, Label Holders",Carl Jackson,613,-54.04,7.3,7.72,Nunavut,Binders and Binder Accessories,0.38
|
||||||
|
8,"SAFCO Mobile Desk Side File, Wire Frame",Carl Jackson,613,127.70,42.76,6.22,Nunavut,Storage & Organization,
|
||||||
|
9,"SAFCO Commercial Wire Shelving, Black",Monica Federle,643,-695.26,138.14,35,Nunavut,Storage & Organization,
|
||||||
|
10,Xerox 198,Dorothy Badders,678,-226.36,4.98,8.33,Nunavut,Paper,0.38
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
1,"Eldon Base for stackable storage shelf, platinum",Muhammed MacIntyre,3,-213.25,38.94,35,Nunavut,Storage & Organization,0.8
|
||||||
|
2,"1.7 Cubic Foot Compact ""Cube"" Office Refrigerators",Barry French,293,457.81,208.16,68.02,Nunavut,Appliances,0.58
|
||||||
|
3,"Cardinal Slant-D<> Ring Binder, Heavy Gauge Vinyl",Barry French,293,46.71,8.69,2.99,Nunavut,Binders and Binder Accessories,0.39
|
||||||
|
4,R380,Clay Rozendal,483,1198.97,195.99,3.99,Nunavut,Telephones and Communication,0.58
|
||||||
|
5,Holmes HEPA Air Purifier,Carlos Soltero,515,30.94,21.78,5.94,Nunavut,Appliances,0.5
|
||||||
|
Loading…
x
Reference in New Issue
Block a user