From abd58510c1a8d558ec7b1f1478a4abd496e417a7 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Fri, 17 May 2024 20:46:05 +0300 Subject: [PATCH 01/10] Added import resources by csv --- .../controller/ResourceController.java | 8 ++ .../MultipartFileContentTypeException.java | 12 ++- .../inventory/mapper/ElementMapper.java | 2 +- .../inventory/mapper/MetalMapper.java | 2 +- .../inventory/mapper/PearlMapper.java | 2 +- .../inventory/mapper/PreciousStoneMapper.java | 10 +- .../mapper/SemiPreciousStoneMapper.java | 8 +- .../inventory/mapper/StringTrimmer.java | 10 ++ .../jewellery/inventory/model/EventType.java | 1 + .../inventory/service/ResourceService.java | 53 ++++++++++ .../ResourceCrudIntegrationTest.java | 100 +++++++++++++++--- .../unit/mapper/ResourceMapperTest.java | 32 +++--- 12 files changed, 195 insertions(+), 45 deletions(-) create mode 100644 src/main/java/jewellery/inventory/mapper/StringTrimmer.java diff --git a/src/main/java/jewellery/inventory/controller/ResourceController.java b/src/main/java/jewellery/inventory/controller/ResourceController.java index a9ffbfcc..03d5548c 100644 --- a/src/main/java/jewellery/inventory/controller/ResourceController.java +++ b/src/main/java/jewellery/inventory/controller/ResourceController.java @@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/resources") @@ -69,4 +70,11 @@ public ResourceQuantityResponseDto getResourceQuantityById(@PathVariable("id") U public List getAllResourceQuantities() { return resourceService.getAllResourceQuantities(); } + + @Operation(summary = "Import resources from CSV") + @ResponseStatus(HttpStatus.OK) + @PostMapping("/import") + public List importResources(@RequestParam("file") MultipartFile file) { + return resourceService.importResources(file); + } } diff --git a/src/main/java/jewellery/inventory/exception/image/MultipartFileContentTypeException.java b/src/main/java/jewellery/inventory/exception/image/MultipartFileContentTypeException.java index 1f9ab59a..6313f349 100644 --- a/src/main/java/jewellery/inventory/exception/image/MultipartFileContentTypeException.java +++ b/src/main/java/jewellery/inventory/exception/image/MultipartFileContentTypeException.java @@ -1,7 +1,11 @@ package jewellery.inventory.exception.image; -public class MultipartFileContentTypeException extends RuntimeException{ - public MultipartFileContentTypeException() { - super("Only PNG or JPG images are allowed."); - } +public class MultipartFileContentTypeException extends RuntimeException { + public MultipartFileContentTypeException() { + super("Only PNG or JPG images are allowed."); + } + + public MultipartFileContentTypeException(String message) { + super(message); + } } diff --git a/src/main/java/jewellery/inventory/mapper/ElementMapper.java b/src/main/java/jewellery/inventory/mapper/ElementMapper.java index f65d0ee3..94329ea4 100644 --- a/src/main/java/jewellery/inventory/mapper/ElementMapper.java +++ b/src/main/java/jewellery/inventory/mapper/ElementMapper.java @@ -6,7 +6,7 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = StringTrimmer.class) public interface ElementMapper { ElementMapper INSTANCE = Mappers.getMapper(ElementMapper.class); diff --git a/src/main/java/jewellery/inventory/mapper/MetalMapper.java b/src/main/java/jewellery/inventory/mapper/MetalMapper.java index d8c16c75..954ace15 100644 --- a/src/main/java/jewellery/inventory/mapper/MetalMapper.java +++ b/src/main/java/jewellery/inventory/mapper/MetalMapper.java @@ -6,7 +6,7 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = StringTrimmer.class) public interface MetalMapper { MetalMapper INSTANCE = Mappers.getMapper(MetalMapper.class); diff --git a/src/main/java/jewellery/inventory/mapper/PearlMapper.java b/src/main/java/jewellery/inventory/mapper/PearlMapper.java index e8450a4a..e6265745 100644 --- a/src/main/java/jewellery/inventory/mapper/PearlMapper.java +++ b/src/main/java/jewellery/inventory/mapper/PearlMapper.java @@ -6,7 +6,7 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = StringTrimmer.class) public interface PearlMapper { PearlMapper INSTANCE = Mappers.getMapper(PearlMapper.class); diff --git a/src/main/java/jewellery/inventory/mapper/PreciousStoneMapper.java b/src/main/java/jewellery/inventory/mapper/PreciousStoneMapper.java index e767db7d..7ebbaef6 100644 --- a/src/main/java/jewellery/inventory/mapper/PreciousStoneMapper.java +++ b/src/main/java/jewellery/inventory/mapper/PreciousStoneMapper.java @@ -6,11 +6,11 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = StringTrimmer.class) public interface PreciousStoneMapper { - PreciousStoneMapper INSTANCE = Mappers.getMapper(PreciousStoneMapper.class); + PreciousStoneMapper INSTANCE = Mappers.getMapper(PreciousStoneMapper.class); - PreciousStoneResponseDto toResourceResponse(PreciousStone entity); + PreciousStoneResponseDto toResourceResponse(PreciousStone entity); - PreciousStone toResourceEntity(PreciousStoneRequestDto dto); -} \ No newline at end of file + PreciousStone toResourceEntity(PreciousStoneRequestDto dto); +} diff --git a/src/main/java/jewellery/inventory/mapper/SemiPreciousStoneMapper.java b/src/main/java/jewellery/inventory/mapper/SemiPreciousStoneMapper.java index 68145937..4b38983c 100644 --- a/src/main/java/jewellery/inventory/mapper/SemiPreciousStoneMapper.java +++ b/src/main/java/jewellery/inventory/mapper/SemiPreciousStoneMapper.java @@ -6,11 +6,11 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = StringTrimmer.class) public interface SemiPreciousStoneMapper { - SemiPreciousStoneMapper INSTANCE = Mappers.getMapper(SemiPreciousStoneMapper.class); + SemiPreciousStoneMapper INSTANCE = Mappers.getMapper(SemiPreciousStoneMapper.class); - SemiPreciousStoneResponseDto toResourceResponse(SemiPreciousStone entity); + SemiPreciousStoneResponseDto toResourceResponse(SemiPreciousStone entity); - SemiPreciousStone toResourceEntity(SemiPreciousStoneRequestDto dto); + SemiPreciousStone toResourceEntity(SemiPreciousStoneRequestDto dto); } diff --git a/src/main/java/jewellery/inventory/mapper/StringTrimmer.java b/src/main/java/jewellery/inventory/mapper/StringTrimmer.java new file mode 100644 index 00000000..3bd6cbb2 --- /dev/null +++ b/src/main/java/jewellery/inventory/mapper/StringTrimmer.java @@ -0,0 +1,10 @@ +package jewellery.inventory.mapper; + +import org.springframework.stereotype.Component; + +@Component +public class StringTrimmer { + public String trimString(String value) { + return value.trim(); + } +} diff --git a/src/main/java/jewellery/inventory/model/EventType.java b/src/main/java/jewellery/inventory/model/EventType.java index 04bede4a..c7bc6137 100644 --- a/src/main/java/jewellery/inventory/model/EventType.java +++ b/src/main/java/jewellery/inventory/model/EventType.java @@ -10,6 +10,7 @@ public enum EventType { RESOURCE_TRANSFER, RESOURCE_REMOVE_QUANTITY, RESOURCE_ADD_QUANTITY, + RESOURCE_IMPORT, PRODUCT_CREATE, PRODUCT_TRANSFER, PRODUCT_DISASSEMBLY, diff --git a/src/main/java/jewellery/inventory/service/ResourceService.java b/src/main/java/jewellery/inventory/service/ResourceService.java index c9b95b77..c2e2c4c5 100644 --- a/src/main/java/jewellery/inventory/service/ResourceService.java +++ b/src/main/java/jewellery/inventory/service/ResourceService.java @@ -1,5 +1,11 @@ package jewellery.inventory.service; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MappingIterator; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.CsvSchema; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -10,6 +16,8 @@ import jewellery.inventory.dto.request.resource.ResourceRequestDto; import jewellery.inventory.dto.response.ResourceQuantityResponseDto; import jewellery.inventory.dto.response.resource.ResourceResponseDto; +import jewellery.inventory.exception.image.MultipartFileContentTypeException; +import jewellery.inventory.exception.image.MultipartFileNotSelectedException; import jewellery.inventory.exception.not_found.ResourceNotFoundException; import jewellery.inventory.mapper.ResourceMapper; import jewellery.inventory.model.EventType; @@ -20,6 +28,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; @Service @RequiredArgsConstructor @@ -90,6 +99,50 @@ public List getAllResourceQuantities() { .toList(); } + @LogUpdateEvent(eventType = EventType.RESOURCE_IMPORT) + public List importResources(MultipartFile file) { + csvVerification(file); + + List resourcesDto = getImportedResources(file); + + List savedResources = new ArrayList<>(); + for (ResourceRequestDto resourceRequestDto : resourcesDto) { + ResourceResponseDto savedResource = createResource(resourceRequestDto); + savedResources.add(savedResource); + } + + return savedResources; + } + + private void csvVerification(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new MultipartFileNotSelectedException(); + } + + if (!file.getContentType().equals("text/csv")) { + throw new MultipartFileContentTypeException("Only CSV files are allowed"); + } + } + + private List getImportedResources(MultipartFile file) { + CsvMapper mapper = new CsvMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + CsvSchema schema = CsvSchema.emptySchema().withHeader(); + + MappingIterator resourcesIterator; + List resourcesDto; + try { + resourcesIterator = + mapper.readerFor(ResourceRequestDto.class).with(schema).readValues(file.getInputStream()); + resourcesDto = resourcesIterator.readAll(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return resourcesDto; + } + @Override public Object fetchEntity(Object... ids) { ids = Arrays.stream(ids).filter(UUID.class::isInstance).toArray(); diff --git a/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java b/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java index 282c6f99..fbe3a1b8 100644 --- a/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java +++ b/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java @@ -8,11 +8,11 @@ import static jewellery.inventory.model.EventType.RESOURCE_UPDATE; import static jewellery.inventory.utils.BigDecimalUtil.getBigDecimal; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -28,12 +28,13 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.util.Pair; import org.springframework.data.util.StreamUtils; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; class ResourceCrudIntegrationTest extends AuthenticatedIntegrationTestBase { @Autowired private ResourceMapper resourceMapper; @@ -128,10 +129,10 @@ void willUpdateResourceToDatabase() throws JsonProcessingException { assertInputMatchesFetchedFromServer(updatedInputDtos); Map expectedEventPayload = - getUpdateEventPayload( - createdDtos.get(0), - getMatchingUpdatedDto(createdDtos.get(0).getId(), updatedDtos), - objectMapper); + getUpdateEventPayload( + createdDtos.get(0), + getMatchingUpdatedDto(createdDtos.get(0).getId(), updatedDtos), + objectMapper); systemEventTestHelper.assertEventWasLogged(RESOURCE_UPDATE, expectedEventPayload); } @@ -182,6 +183,75 @@ void willFailToDeleteResourceFromDatabaseWithWrongId() { assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); } + @Test + public void willThrowWhenFileIsEmpty() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file", getEmptyTestFile().getResource()); + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + ResponseEntity response = + testRestTemplate.postForEntity(getImportUrl(), requestEntity, String.class); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + public void willThrowWhenFileContentIsWrong() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file", getTestWrongContentFile().getResource()); + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + ResponseEntity response = + testRestTemplate.postForEntity(getImportUrl(), requestEntity, String.class); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + public void willImportResourcesSuccessfully() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file", getTestFile().getResource()); + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ParameterizedTypeReference> responseType = + new ParameterizedTypeReference>() {}; + + ResponseEntity> response = + testRestTemplate.exchange(getImportUrl(), HttpMethod.POST, requestEntity, responseType); + + List responseDto = response.getBody(); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(responseDto); + assertEquals(responseDto.get(0).getClazz(), "Element"); + assertEquals(responseDto.get(0).getQuantityType(), "28"); + assertEquals(responseDto.get(0).getPricePerQuantity(), BigDecimal.valueOf(30)); + assertEquals(responseDto.get(0).getNote(), "smth"); + } + + private MockMultipartFile getEmptyTestFile() { + return new MockMultipartFile("file", "test-file.txt", "text/plain", "".getBytes()); + } + + private MockMultipartFile getTestWrongContentFile() { + String data = "smth"; + return new MockMultipartFile("file", "test-file.txt", "text/plain", data.getBytes()); + } + + private MockMultipartFile getTestFile() { + String csvData = + "clazz,quantityType,pricePerQuantity,note,description\nElement,28,30,smth,Element description\n"; + return new MockMultipartFile("file", "test-file.csv", "text/csv", csvData.getBytes()); + } + + private String getImportUrl() { + return getBaseResourceUrl() + "/" + "import"; + } + @NotNull private List getResourcesWithRequest() throws JsonProcessingException { String response = this.testRestTemplate.getForObject(getBaseResourceUrl(), String.class); @@ -251,10 +321,12 @@ private void assertInputMatchesFetchedFromServer(List update assertThat(mappedDtos).containsExactlyInAnyOrderElementsOf(updatedResources); } - private ResourceResponseDto getMatchingUpdatedDto(UUID id, List updatedDtos) { + + private ResourceResponseDto getMatchingUpdatedDto( + UUID id, List updatedDtos) { return updatedDtos.stream() - .filter(resourceResponseDto -> resourceResponseDto.getId().equals(id)) - .findFirst() - .orElseThrow(() -> new AssertionFailure("Can't find id: " + id + " in responses")); + .filter(resourceResponseDto -> resourceResponseDto.getId().equals(id)) + .findFirst() + .orElseThrow(() -> new AssertionFailure("Can't find id: " + id + " in responses")); } } diff --git a/src/test/java/jewellery/inventory/unit/mapper/ResourceMapperTest.java b/src/test/java/jewellery/inventory/unit/mapper/ResourceMapperTest.java index 5d046824..c81a5cc4 100644 --- a/src/test/java/jewellery/inventory/unit/mapper/ResourceMapperTest.java +++ b/src/test/java/jewellery/inventory/unit/mapper/ResourceMapperTest.java @@ -7,26 +7,28 @@ import jewellery.inventory.dto.response.resource.ResourceResponseDto; import jewellery.inventory.exception.MappingException; import jewellery.inventory.mapper.*; -import jewellery.inventory.mapper.MetalMapper; import jewellery.inventory.model.resource.Resource; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; - +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration( + classes = { + PearlMapperImpl.class, + PreciousStoneMapperImpl.class, + ElementMapperImpl.class, + MetalMapperImpl.class, + SemiPreciousStoneMapperImpl.class, + StringTrimmer.class, + ResourceMapper.class + }) class ResourceMapperTest { - private ResourceMapper resourceMapper; - - @BeforeEach - void setUp() { - PearlMapper pearlMapper = PearlMapper.INSTANCE; - PreciousStoneMapper preciousStoneMapper = PreciousStoneMapper.INSTANCE; - ElementMapper elementMapper = ElementMapper.INSTANCE; - MetalMapper metalMapper = MetalMapper.INSTANCE; - SemiPreciousStoneMapper semiPreciousStoneMapper = SemiPreciousStoneMapper.INSTANCE; - resourceMapper = - new ResourceMapper(pearlMapper, preciousStoneMapper, elementMapper, metalMapper, semiPreciousStoneMapper); - } + @Autowired private ResourceMapper resourceMapper; @ParameterizedTest @MethodSource("jewellery.inventory.helper.ResourceTestHelper#provideResourcesAndResponseDtos") From 707ab205265ee6e47e87f113694c51ea5256b067 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Fri, 17 May 2024 20:46:29 +0300 Subject: [PATCH 02/10] Added import resources by csv --- .../unit/service/ResourceServiceTest.java | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/test/java/jewellery/inventory/unit/service/ResourceServiceTest.java b/src/test/java/jewellery/inventory/unit/service/ResourceServiceTest.java index e5a6527d..b23f1396 100644 --- a/src/test/java/jewellery/inventory/unit/service/ResourceServiceTest.java +++ b/src/test/java/jewellery/inventory/unit/service/ResourceServiceTest.java @@ -1,7 +1,6 @@ package jewellery.inventory.unit.service; -import static jewellery.inventory.helper.ResourceTestHelper.getPreciousStone; -import static jewellery.inventory.helper.ResourceTestHelper.provideResources; +import static jewellery.inventory.helper.ResourceTestHelper.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -12,6 +11,8 @@ import java.util.UUID; import jewellery.inventory.dto.request.resource.ResourceRequestDto; import jewellery.inventory.dto.response.resource.ResourceResponseDto; +import jewellery.inventory.exception.image.MultipartFileContentTypeException; +import jewellery.inventory.exception.image.MultipartFileNotSelectedException; import jewellery.inventory.exception.not_found.ResourceNotFoundException; import jewellery.inventory.mapper.ResourceMapper; import jewellery.inventory.model.resource.Resource; @@ -24,6 +25,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockMultipartFile; @ExtendWith(MockitoExtension.class) class ResourceServiceTest { @@ -31,6 +33,10 @@ class ResourceServiceTest { @Mock private ResourceMapper resourceMapper; @InjectMocks private ResourceService resourceService; + private MockMultipartFile file; + private final String csvData = + "clazz,quantityType,pricePerQuantity,note,description\nElement,28,30,smth,Element description\n"; + @ParameterizedTest @MethodSource("jewellery.inventory.helper.ResourceTestHelper#provideResourcesAndRequestDtos") void willSaveResource(Resource resourceFromDatabase, ResourceRequestDto resourceRequestDto) { @@ -142,4 +148,40 @@ void willThrowWhenGetANonExistingResource() { assertThrows( ResourceNotFoundException.class, () -> resourceService.getResource(UUID.randomUUID())); } + + @Test + void willThrowWhenImportFileIsNull() { + assertThrows( + MultipartFileNotSelectedException.class, () -> resourceService.importResources(file)); + } + + @Test + void willThrowWhenImportFileIsEmpty() { + file = new MockMultipartFile("empty.csv", "".getBytes()); + + assertThrows( + MultipartFileNotSelectedException.class, () -> resourceService.importResources(file)); + } + + @Test + void willThrowWhenImportFileIsNotCsv() { + file = new MockMultipartFile("file.txt", "file.txt", "text/plain", csvData.getBytes()); + + assertThrows( + MultipartFileContentTypeException.class, () -> resourceService.importResources(file)); + } + + @Test + void willImportResources() { + file = new MockMultipartFile("file.csv", "file.csv", "text/csv", csvData.getBytes()); + Resource element = getElement(); + when(resourceMapper.toResourceEntity(any(ResourceRequestDto.class))).thenReturn(element); + when(resourceRepository.save(element)).thenReturn(element); + + resourceService.importResources(file); + + verify(resourceMapper, times(1)).toResourceEntity(any(ResourceRequestDto.class)); + verify(resourceRepository, times(1)).save(element); + verify(resourceMapper, times(1)).toResourceResponse(element); + } } From 053ba9cc2fff4aaec8c0840c1ec966f07c35a617 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 01:20:31 +0300 Subject: [PATCH 03/10] Integration tests fix --- .../java/jewellery/inventory/service/ResourceService.java | 1 - .../inventory/integration/ResourceCrudIntegrationTest.java | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/main/java/jewellery/inventory/service/ResourceService.java b/src/main/java/jewellery/inventory/service/ResourceService.java index c2e2c4c5..8284b222 100644 --- a/src/main/java/jewellery/inventory/service/ResourceService.java +++ b/src/main/java/jewellery/inventory/service/ResourceService.java @@ -99,7 +99,6 @@ public List getAllResourceQuantities() { .toList(); } - @LogUpdateEvent(eventType = EventType.RESOURCE_IMPORT) public List importResources(MultipartFile file) { csvVerification(file); diff --git a/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java b/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java index fbe3a1b8..c615d0f0 100644 --- a/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java +++ b/src/test/java/jewellery/inventory/integration/ResourceCrudIntegrationTest.java @@ -185,8 +185,6 @@ void willFailToDeleteResourceFromDatabaseWithWrongId() { @Test public void willThrowWhenFileIsEmpty() { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", getEmptyTestFile().getResource()); HttpEntity> requestEntity = new HttpEntity<>(body, headers); @@ -199,8 +197,6 @@ public void willThrowWhenFileIsEmpty() { @Test public void willThrowWhenFileContentIsWrong() { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", getTestWrongContentFile().getResource()); HttpEntity> requestEntity = new HttpEntity<>(body, headers); @@ -213,8 +209,6 @@ public void willThrowWhenFileContentIsWrong() { @Test public void willImportResourcesSuccessfully() { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", getTestFile().getResource()); HttpEntity> requestEntity = new HttpEntity<>(body, headers); From 50e749f48c9fded0e4fffb99b9584d9343e5a99d Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 01:31:28 +0300 Subject: [PATCH 04/10] Added jackson scv dependency --- pom.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fb0061f3..270badc8 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ org.projectlombok lombok - 1.18.22 + 1.18.30 provided @@ -182,6 +182,11 @@ hypersistence-utils-hibernate-62 3.6.0 + + com.fasterxml.jackson.dataformat + jackson-dataformat-csv + 2.17.0 + From 7dec03f78d12cf36b6da984e4c86bbc3c18cc153 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 01:39:38 +0300 Subject: [PATCH 05/10] Type check fix --- src/main/java/jewellery/inventory/service/ResourceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jewellery/inventory/service/ResourceService.java b/src/main/java/jewellery/inventory/service/ResourceService.java index 8284b222..586bea6b 100644 --- a/src/main/java/jewellery/inventory/service/ResourceService.java +++ b/src/main/java/jewellery/inventory/service/ResourceService.java @@ -118,7 +118,7 @@ private void csvVerification(MultipartFile file) { throw new MultipartFileNotSelectedException(); } - if (!file.getContentType().equals("text/csv")) { + if (file.getContentType() == null || !file.getContentType().equals("text/csv")) { throw new MultipartFileContentTypeException("Only CSV files are allowed"); } } From 3dde4419575817d16e8431dc9134204b65be9be4 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 01:48:48 +0300 Subject: [PATCH 06/10] Type check fix --- .../java/jewellery/inventory/service/ResourceService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/jewellery/inventory/service/ResourceService.java b/src/main/java/jewellery/inventory/service/ResourceService.java index 586bea6b..ef6158b9 100644 --- a/src/main/java/jewellery/inventory/service/ResourceService.java +++ b/src/main/java/jewellery/inventory/service/ResourceService.java @@ -118,9 +118,9 @@ private void csvVerification(MultipartFile file) { throw new MultipartFileNotSelectedException(); } - if (file.getContentType() == null || !file.getContentType().equals("text/csv")) { - throw new MultipartFileContentTypeException("Only CSV files are allowed"); - } + if (file.getContentType() != null && !file.getContentType().equals("text/csv")) { + throw new MultipartFileContentTypeException("Only CSV files are allowed"); + } } private List getImportedResources(MultipartFile file) { From ce4a14d8148727d6b99b2028a4a5f1dc1dad45fc Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 01:54:33 +0300 Subject: [PATCH 07/10] Type check reverse fix --- src/main/java/jewellery/inventory/service/ResourceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jewellery/inventory/service/ResourceService.java b/src/main/java/jewellery/inventory/service/ResourceService.java index ef6158b9..330cd6b7 100644 --- a/src/main/java/jewellery/inventory/service/ResourceService.java +++ b/src/main/java/jewellery/inventory/service/ResourceService.java @@ -118,7 +118,7 @@ private void csvVerification(MultipartFile file) { throw new MultipartFileNotSelectedException(); } - if (file.getContentType() != null && !file.getContentType().equals("text/csv")) { + if (!"text/csv".equals(file.getContentType())) { throw new MultipartFileContentTypeException("Only CSV files are allowed"); } } From 199137c592efdffebcc3bcf6a57a3ef944ec64ab Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 02:01:01 +0300 Subject: [PATCH 08/10] Pom lombok version fix --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 270badc8..5f02c434 100644 --- a/pom.xml +++ b/pom.xml @@ -182,6 +182,7 @@ hypersistence-utils-hibernate-62 3.6.0 + com.fasterxml.jackson.dataformat jackson-dataformat-csv From 06be4dffcc80c0a89f6bd6f2b462a40086bbb034 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Sat, 18 May 2024 02:05:33 +0300 Subject: [PATCH 09/10] Pom lombok dependency version fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f02c434..43fcf515 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ org.projectlombok lombok - 1.18.30 + 1.18.22 provided From 3d58fd87f35293e9d952e10d97193ae8fc838f49 Mon Sep 17 00:00:00 2001 From: Lubomir Karnatov Date: Mon, 20 May 2024 02:19:54 +0300 Subject: [PATCH 10/10] Refactoring after review --- pom.xml | 1 - .../inventory/service/ResourceService.java | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 43fcf515..863e8834 100644 --- a/pom.xml +++ b/pom.xml @@ -186,7 +186,6 @@ com.fasterxml.jackson.dataformat jackson-dataformat-csv - 2.17.0 diff --git a/src/main/java/jewellery/inventory/service/ResourceService.java b/src/main/java/jewellery/inventory/service/ResourceService.java index 330cd6b7..5fa2808d 100644 --- a/src/main/java/jewellery/inventory/service/ResourceService.java +++ b/src/main/java/jewellery/inventory/service/ResourceService.java @@ -100,7 +100,7 @@ public List getAllResourceQuantities() { } public List importResources(MultipartFile file) { - csvVerification(file); + verifyIsCsv(file); List resourcesDto = getImportedResources(file); @@ -113,14 +113,14 @@ public List importResources(MultipartFile file) { return savedResources; } - private void csvVerification(MultipartFile file) { + private void verifyIsCsv(MultipartFile file) { if (file == null || file.isEmpty()) { throw new MultipartFileNotSelectedException(); } - if (!"text/csv".equals(file.getContentType())) { - throw new MultipartFileContentTypeException("Only CSV files are allowed"); - } + if (!"text/csv".equals(file.getContentType())) { + throw new MultipartFileContentTypeException("Only CSV files are allowed"); + } } private List getImportedResources(MultipartFile file) { @@ -130,16 +130,16 @@ private List getImportedResources(MultipartFile file) { CsvSchema schema = CsvSchema.emptySchema().withHeader(); MappingIterator resourcesIterator; - List resourcesDto; + List resourceDtos; try { resourcesIterator = mapper.readerFor(ResourceRequestDto.class).with(schema).readValues(file.getInputStream()); - resourcesDto = resourcesIterator.readAll(); + resourceDtos = resourcesIterator.readAll(); } catch (IOException e) { throw new RuntimeException(e); } - return resourcesDto; + return resourceDtos; } @Override