From 811730bb43708ec850da5307eb8f601db38825cb Mon Sep 17 00:00:00 2001 From: Andrea Morabito <78792023+and-mora@users.noreply.github.com> Date: Tue, 2 Apr 2024 11:18:32 +0200 Subject: [PATCH 1/2] breaking: [RTD-2455] report v2 with additional information (#131) --- .github/workflows/release.yml | 4 +- .github/workflows/scan.yml | 4 +- docs/entities.uml | 40 ++++++ helm/rtd/values.yaml | 2 + pom.xml | 16 +-- .../RtdMsFileReporterApplication.java | 9 +- .../configuration/AppConfiguration.java | 8 +- .../ConsumeEventControllerImpl.java | 8 +- .../controller/FileReportController.java | 7 +- .../controller/FileReportControllerImpl.java | 13 +- .../model/{ => v1}/FileMetadataDto.java | 2 +- .../model/{ => v1}/FileReportDto.java | 2 +- .../model/{ => v1}/FileReportDtoMapper.java | 2 +- .../model/v2/FileMetadataV2Dto.java | 15 +++ .../controller/model/v2/FileReportV2Dto.java | 10 ++ .../model/v2/FileReportV2DtoMapper.java | 16 +++ .../domain/FileReportCommandFactory.java | 8 +- .../domain/model/AggregatesDataSummary.java | 36 +++++ .../domain/model/FileMetadata.java | 22 +++- .../domain/model/FileReport.java | 11 +- .../domain/service/DecryptedEventCommand.java | 26 ++++ .../domain/service/StorageAccountService.java | 41 ++++++ .../event/FileReportEventAdapter.java | 2 +- .../event/model/EventToDomainMapper.java | 27 +++- .../event/model/ProjectorEventDto.java | 2 +- .../feign/AggregatesDataSummaryMapper.java | 20 +++ .../feign/StorageAccountRestConnector.java | 82 ++++++++++++ .../feign/config/HttpClientConfig.java | 42 ++++++ .../feign/config/StorageProperties.java | 16 +++ src/main/resources/application.yml | 6 + .../controller/DomainToDtoMapperTest.java | 9 +- .../FileReportControllerImplTest.java | 57 +++++++- .../domain/model/FileReportTest.java | 58 +++++---- .../service/DecryptedEventCommandTest.java | 75 +++++++++++ .../service/StorageAccountServiceTest.java | 89 +++++++++++++ .../event/EventHandlerTest.java | 2 +- .../event/FileReportEventAdapterTest.java | 48 ++++--- .../event/model/EventToDomainMapperTest.java | 21 ++- .../AggregatesDataSummaryMapperTest.java | 37 ++++++ .../StorageAccountRestConnectorTest.java | 123 ++++++++++++++++++ .../FileReportRepositoryImplTest.java | 6 +- 41 files changed, 928 insertions(+), 96 deletions(-) create mode 100644 docs/entities.uml rename src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/{ => v1}/FileMetadataDto.java (92%) rename src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/{ => v1}/FileReportDto.java (67%) rename src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/{ => v1}/FileReportDtoMapper.java (83%) create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileMetadataV2Dto.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2Dto.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2DtoMapper.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/AggregatesDataSummary.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommand.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountService.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapper.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnector.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/HttpClientConfig.java create mode 100644 src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/StorageProperties.java create mode 100644 src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommandTest.java create mode 100644 src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountServiceTest.java create mode 100644 src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapperTest.java create mode 100644 src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnectorTest.java diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f3d7f9c..aac6940 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,13 +17,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 with: persist-credentials: false fetch-depth: 0 - name: Release - uses: cycjimmy/semantic-release-action@8e58d20d0f6c8773181f43eb74d6a05e3099571d # v3.4.2 + uses: cycjimmy/semantic-release-action@61680d0e9b02ff86f5648ade99e01be17f0260a4 #v4.0.0 with: semantic_version: 18.0.0 extra_plugins: | diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index a3d5502..7445433 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -33,12 +33,12 @@ jobs: environment: dev steps: - name: Checkout the code - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 - name: Build the Docker image run: docker build . --file ${{ env.DOCKERFILE }} --target cve --tag localbuild/testimage:latest - name: Run the Trivy scan action itself with GitHub Advanced Security code scanning integration enabled id: scan - uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f #v0.12.0 + uses: aquasecurity/trivy-action@062f2592684a31eb3aa050cc61e7ca1451cecd3d #v0.18.0 with: image-ref: "localbuild/testimage:latest" format: 'sarif' diff --git a/docs/entities.uml b/docs/entities.uml new file mode 100644 index 0000000..771fa71 --- /dev/null +++ b/docs/entities.uml @@ -0,0 +1,40 @@ +@startuml +class FileReport { + -senderCodes: String[] + -filesUploaded: FileMetadata[] + -ackToDownload: String[] + +addFileUploaded(file) + +removeFileUploaded(file) + +addSenderCode(senderCode) + +addAckToDownload(file) + +removeAckToDownload(file) + +addFileOrUpdateStatus(file) + +removeFilesOlderThan(days) + +addSquaringDataToFile(file) +} + +class FileMetadata { + -name: String + -path: String + -size: Long + -status: Enum + -transmissionDate: Date + -dataSummary: AggregatesDataSummary + + +enrichWithSquaringData() +} + +class AggregatesDataSummary { + -minAccountingDate: Date + -maxAccountingDate: Date + -numberOfMerchants: Integer + -countNegativeTransactions: Long + -countPositiveTransactions: Long + -sumAmountNegativeTransactions: Long + -sumAmountPositiveTransactions: Long + -sha256OriginFile: String +} + +FileReport "1" *-- "n" FileMetadata +FileMetadata *-- AggregatesDataSummary +@enduml \ No newline at end of file diff --git a/helm/rtd/values.yaml b/helm/rtd/values.yaml index 1cf6a77..3c4f85a 100644 --- a/helm/rtd/values.yaml +++ b/helm/rtd/values.yaml @@ -56,6 +56,7 @@ microservice-chart: MONGODB_CONNECTION_URI: mongo-db-connection-uri KAFKA_RTD_PROJECTOR_SASL_JAAS_CONFIG: evh-rtd-file-register-projector-rtd-file-register-projector-consumer-policy-rtd APPLICATIONINSIGHTS_CONNECTION_STRING: appinsights-instrumentation-key + INTERNAL_SERVICES_API_KEY: rtd-internal-api-product-subscription-key envConfigMapExternals: rtd-file-register-projector-consumer: @@ -64,3 +65,4 @@ microservice-chart: rtd-filereporter: OPENTELEMETRY_LOG_LEVEL: APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL + STORAGE_ACCOUNT_HOST: STORAGE_ACCOUNT_HOST diff --git a/pom.xml b/pom.xml index 214f55f..6c3801b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,20 +5,20 @@ org.springframework.boot spring-boot-starter-parent - 3.2.3 + 3.2.4 it.gov.pagopa.rtd.ms rtd-ms-file-reporter - 1.4.1 + 2.0.0 rtd-ms-file-reporter Micro-service to retrieve and keep updated the file reports for senders 17 2023.0.0 - 1.19.3 + 1.19.7 2.2 3.6.1 **/telemetry/**.java @@ -115,6 +115,10 @@ mongodb test + + org.apache.httpcomponents.client5 + httpclient5 + @@ -150,12 +154,6 @@ pom import - - - org.springframework - spring-core - 6.1.3 - diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/RtdMsFileReporterApplication.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/RtdMsFileReporterApplication.java index ccc0b3f..0069b04 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/RtdMsFileReporterApplication.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/RtdMsFileReporterApplication.java @@ -1,13 +1,16 @@ package it.gov.pagopa.rtd.ms.rtdmsfilereporter; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.config.StorageProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; @SpringBootApplication +@EnableConfigurationProperties(StorageProperties.class) public class RtdMsFileReporterApplication { - public static void main(String[] args) { - SpringApplication.run(RtdMsFileReporterApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(RtdMsFileReporterApplication.class, args); + } } diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/configuration/AppConfiguration.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/configuration/AppConfiguration.java index 70d08b7..5f02f04 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/configuration/AppConfiguration.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/configuration/AppConfiguration.java @@ -2,6 +2,7 @@ import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.FileReportCommandFactory; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.repository.FileReportRepository; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.DecryptedEventCommand; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.FileReportService; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.FileReportServiceImpl; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.persistance.FileReportDao; @@ -23,12 +24,13 @@ public FileReportService getFileReportService(FileReportRepository repository) { } @Bean - public FileReportRepository getFileReportRepository(FileReportDao dao, FileReportEntityMapper mapper) { + public FileReportRepository getFileReportRepository(FileReportDao dao, + FileReportEntityMapper mapper) { return new FileReportRepositoryImpl(dao, mapper); } @Bean - public FileReportCommandFactory getFileReportCommandFactory() { - return new FileReportCommandFactory(); + public FileReportCommandFactory getFileReportCommandFactory(DecryptedEventCommand command) { + return new FileReportCommandFactory(command); } } diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java index 4a86526..684c5c6 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java @@ -3,8 +3,8 @@ import it.gov.pagopa.rtd.ms.rtdmsfilereporter.event.FileReportEventAdapter; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.event.model.ProjectorEventDto; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.support.MessageBuilder; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; @@ -13,14 +13,14 @@ @RestController @ResponseBody @Slf4j +@RequiredArgsConstructor public class ConsumeEventControllerImpl implements ConsumeEventController { - @Autowired - private FileReportEventAdapter adapter; + private final FileReportEventAdapter adapter; @Override public void consumeEvent(@RequestBody @Valid ProjectorEventDto eventData) { - log.info("Received event [{}]", eventData.getFileName()); + log.info("Received event [{}]", eventData.getFilePath()); adapter.consumeEvent(MessageBuilder.withPayload(eventData).build().getPayload()); } diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportController.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportController.java index 818e098..6c9a478 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportController.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportController.java @@ -1,7 +1,8 @@ package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileReportDto; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.SenderAdeAckListDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileReportDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2.FileReportV2Dto; import jakarta.validation.constraints.NotNull; import java.util.Collection; import org.springframework.http.MediaType; @@ -13,6 +14,10 @@ public interface FileReportController { @GetMapping(value = "file-report", produces = MediaType.APPLICATION_JSON_VALUE) FileReportDto getFileReport(@RequestParam(name = "senderCodes") Collection senderCodes); + @GetMapping(value = "v2/file-report", produces = MediaType.APPLICATION_JSON_VALUE) + FileReportV2Dto getFileReportV2( + @RequestParam(name = "senderCodes") Collection senderCodes); + @GetMapping(value = "/sender-ade-ack", produces = MediaType.APPLICATION_JSON_VALUE) SenderAdeAckListDto getSenderAdeAckList( @NotNull @RequestParam(name = "senderCodes") Collection senderCodes); diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImpl.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImpl.java index 22e733f..56e2f3e 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImpl.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImpl.java @@ -1,8 +1,10 @@ package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileReportDto; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileReportDtoMapper; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.SenderAdeAckListDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileReportDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileReportDtoMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2.FileReportV2Dto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2.FileReportV2DtoMapper; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.FileReportService; import jakarta.validation.constraints.NotNull; import java.util.Collection; @@ -17,6 +19,7 @@ public class FileReportControllerImpl implements FileReportController { private final FileReportService fileReportService; private final FileReportDtoMapper mapper; + private final FileReportV2DtoMapper mapperV2; @Override public FileReportDto getFileReport(Collection senderCodes) { @@ -24,6 +27,12 @@ public FileReportDto getFileReport(Collection senderCodes) { return mapper.fileReportToDto(fileReportService.getAggregateFileReport(senderCodes)); } + @Override + public FileReportV2Dto getFileReportV2(Collection senderCodes) { + log.info("GET file report v2 for sender codes: {}", senderCodes.toString()); + return mapperV2.fileReportToDto(fileReportService.getAggregateFileReport(senderCodes)); + } + @Override public SenderAdeAckListDto getSenderAdeAckList(@NotNull Collection senderCodes) { log.info("GET ack to download list for sender codes: {}", senderCodes.toString()); diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileMetadataDto.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileMetadataDto.java similarity index 92% rename from src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileMetadataDto.java rename to src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileMetadataDto.java index b5fcc32..281605e 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileMetadataDto.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileMetadataDto.java @@ -1,4 +1,4 @@ -package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model; +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileReportDto.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileReportDto.java similarity index 67% rename from src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileReportDto.java rename to src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileReportDto.java index c575593..83be2a5 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileReportDto.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileReportDto.java @@ -1,4 +1,4 @@ -package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model; +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1; import java.util.Collection; import lombok.Data; diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileReportDtoMapper.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileReportDtoMapper.java similarity index 83% rename from src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileReportDtoMapper.java rename to src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileReportDtoMapper.java index d6a98d3..92f0d3e 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/FileReportDtoMapper.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v1/FileReportDtoMapper.java @@ -1,4 +1,4 @@ -package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model; +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; import org.mapstruct.Mapper; diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileMetadataV2Dto.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileMetadataV2Dto.java new file mode 100644 index 0000000..c0eab7a --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileMetadataV2Dto.java @@ -0,0 +1,15 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileMetadataDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class FileMetadataV2Dto extends FileMetadataDto { + + @NotNull + private AggregatesDataSummary dataSummary; +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2Dto.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2Dto.java new file mode 100644 index 0000000..f880089 --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2Dto.java @@ -0,0 +1,10 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2; + +import java.util.Collection; +import lombok.Data; + +@Data +public class FileReportV2Dto { + + Collection filesRecentlyUploaded; +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2DtoMapper.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2DtoMapper.java new file mode 100644 index 0000000..405bb76 --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/model/v2/FileReportV2DtoMapper.java @@ -0,0 +1,16 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileMetadata; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring") +public interface FileReportV2DtoMapper { + + @Mapping(source = "filesUploaded", target = "filesRecentlyUploaded") + FileReportV2Dto fileReportToDto(FileReport fileReport); + + @Mapping(source = "aggregatesDataSummary", target = "dataSummary") + FileMetadataV2Dto fileMetadataToDto(FileMetadata fileMetadata); +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/FileReportCommandFactory.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/FileReportCommandFactory.java index 925a210..f887c3a 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/FileReportCommandFactory.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/FileReportCommandFactory.java @@ -2,18 +2,24 @@ import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileMetadata; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.DecryptedEventCommand; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.event.model.EventStatusEnum; import java.util.function.BiConsumer; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor public class FileReportCommandFactory { + private final DecryptedEventCommand decryptCommand; + public BiConsumer getCommandByStatus(String status) { return switch (EventStatusEnum.valueOf(status)) { case ACK_TO_DOWNLOAD -> (fileReport, fileMetadata) -> fileReport.addAckToDownload(fileMetadata.getName()); case ACK_DOWNLOADED -> (fileReport, fileMetadata) -> fileReport.removeAckToDownload(fileMetadata.getName()); - default -> FileReport::addFileOrUpdateStatusIfPresent; + case DECRYPTED -> decryptCommand; + case RECEIVED, SENT_TO_ADE -> FileReport::addFileOrUpdateStatusIfPresent; }; } } diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/AggregatesDataSummary.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/AggregatesDataSummary.java new file mode 100644 index 0000000..555b33f --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/AggregatesDataSummary.java @@ -0,0 +1,36 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model; + +import java.time.LocalDate; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AggregatesDataSummary { + + private LocalDate minAccountingDate; + private LocalDate maxAccountingDate; + private int numberOfMerchants; + private long countNegativeTransactions; + private long countPositiveTransactions; + private long sumAmountNegativeTransactions; + private long sumAmountPositiveTransactions; + // sha256 of the initial input file containing the transactions + private String sha256OriginFile; + + public static AggregatesDataSummary createInvalidDataSummary() { + return AggregatesDataSummary.builder() + .sumAmountPositiveTransactions(-1) + .sumAmountNegativeTransactions(-1) + .countPositiveTransactions(-1) + .countNegativeTransactions(-1) + .numberOfMerchants(-1) + .minAccountingDate(null) + .maxAccountingDate(null) + .build(); + } +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileMetadata.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileMetadata.java index b071246..be7cdba 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileMetadata.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileMetadata.java @@ -1,9 +1,11 @@ package it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model; -import java.time.LocalDateTime; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.Objects; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -13,12 +15,16 @@ @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode +@Builder public class FileMetadata implements Comparable { @NotNull @NotBlank private String name; + @NotNull + private String path; + private Long size; @NotNull @@ -28,12 +34,14 @@ public class FileMetadata implements Comparable { @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) private LocalDateTime transmissionDate; + private AggregatesDataSummary aggregatesDataSummary; + public static FileMetadata createNewFileMetadata(String name) { return createNewFileMetadataWithStatus(name, null); } public static FileMetadata createNewFileMetadataWithStatus(String name, FileStatusEnum status) { - return new FileMetadata(name, null, status, LocalDateTime.now()); + return new FileMetadata(name, null, null, status, LocalDateTime.now(), null); } /** @@ -48,4 +56,14 @@ public int compareTo(@NotNull FileMetadata o) { return o.getTransmissionDate().isEqual(this.transmissionDate) ? o.getName() .compareTo(this.name) : o.getTransmissionDate().compareTo(this.transmissionDate); } + + /** + * Enrich the file metadata with the summary of the content. + * + * @param dataSummary additional information about content of file + */ + public void enrichWithSquaringData(AggregatesDataSummary dataSummary) { + dataSummary = Objects.requireNonNullElse(dataSummary, AggregatesDataSummary.builder().build()); + this.aggregatesDataSummary = dataSummary; + } } diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReport.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReport.java index 8658797..05cff7a 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReport.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReport.java @@ -2,13 +2,13 @@ import static java.util.Collections.emptyList; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.time.LocalDateTime; import java.util.Collection; import java.util.HashSet; import java.util.Objects; import java.util.TreeSet; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -86,4 +86,11 @@ public static FileReport mergeFileReports(FileReport firstReport, FileReport sec Objects.requireNonNullElse(secondReport.getAckToDownload(), emptyList())); return firstReport; } + + public void addSquaringDataToFile(FileMetadata fileMetadata) { + this.filesUploaded.stream() + .filter(file -> file.getName().equals(fileMetadata.getName())) + .findAny() + .ifPresent(file -> file.setAggregatesDataSummary(fileMetadata.getAggregatesDataSummary())); + } } diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommand.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommand.java new file mode 100644 index 0000000..012b83b --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommand.java @@ -0,0 +1,26 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileMetadata; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; +import java.util.function.BiConsumer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class DecryptedEventCommand implements BiConsumer { + + private final StorageAccountService service; + + @Override + public void accept(FileReport fileReport, FileMetadata fileMetadata) { + // preliminary api call + var dataSummary = service.getMetadata(fileMetadata.getPath(), fileMetadata.getName()); + // actions on domain object + fileMetadata.enrichWithSquaringData(dataSummary); + // two operations are needed: update status + add aggregates summary + fileReport.addFileOrUpdateStatusIfPresent(fileMetadata); + fileReport.addSquaringDataToFile(fileMetadata); + } + +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountService.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountService.java new file mode 100644 index 0000000..3a69940 --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountService.java @@ -0,0 +1,41 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.AggregatesDataSummaryMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.StorageAccountRestConnector; +import java.io.IOException; +import java.time.format.DateTimeParseException; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +@Slf4j +public class StorageAccountService { + + private final StorageAccountRestConnector connector; + private final AggregatesDataSummaryMapper mapper; + + public AggregatesDataSummary getMetadata(String basePath, String fileName) { + Map response; + try { + response = connector.getBlobMetadata(basePath, fileName); + } catch (IOException e) { + log.warn("Failed to retrieve the file metadata from the storage! Error: {}", e.getMessage()); + return AggregatesDataSummary.createInvalidDataSummary(); + } + + // catch any eventual cast exceptions + AggregatesDataSummary dataSummary; + try { + dataSummary = mapper.getDataSummary(response); + } catch (DateTimeParseException | NumberFormatException ex) { + log.error("Error in parsing some metadata! Error: {}", ex.getMessage()); + return AggregatesDataSummary.createInvalidDataSummary(); + } + + return dataSummary; + } +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapter.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapter.java index 94dc161..9d1c97c 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapter.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapter.java @@ -34,7 +34,7 @@ public void consumeEvent(@Valid ProjectorEventDto eventDto) { if (!validationErrors.isEmpty()) { throw new ConstraintViolationException(validationErrors); } - log.info("Received event for file {} with status {}", eventDto.getFileName(), eventDto.getStatus().name()); + log.info("Received event for file {} with status {}", eventDto.getFilePath(), eventDto.getStatus().name()); // retrieve the report var report = service.getFileReport(eventDto.getSender()) diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapper.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapper.java index 26942e5..7b69c61 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapper.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapper.java @@ -4,12 +4,37 @@ import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileStatusEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; @Mapper(componentModel = "spring") public interface EventToDomainMapper { - @Mapping(source = "fileName", target = "name") + + /** + * Extract the file name from a path. e.g. '/container/filename.txt' returns 'filename.txt' + */ + @Named("filePathToFileName") + static String filePathToFileName(String filePath) { + return filePath.substring(filePath.lastIndexOf('/') + 1); + } + + /** + * Map from a path containing the file name to a string containing the path only. + * e.g. '/container/filename.txt' returns '/container/' + */ + @Named("filePathToPath") + static String filePathToPath(String filePath) { + if (filePath.contains("/")) { + return filePath.substring(0, filePath.lastIndexOf('/') + 1); + } else { + return ""; + } + } + + @Mapping(source = "filePath", target = "name", qualifiedByName = "filePathToFileName") + @Mapping(source = "filePath", target = "path", qualifiedByName = "filePathToPath") @Mapping(source = "receiveTimestamp", target = "transmissionDate") + @Mapping(target = "aggregatesDataSummary", ignore = true) FileMetadata eventToDomain(ProjectorEventDto eventDto); default FileStatusEnum convertFromStringToStatusEnum(EventStatusEnum status) { diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/ProjectorEventDto.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/ProjectorEventDto.java index 09438ef..d879304 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/ProjectorEventDto.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/ProjectorEventDto.java @@ -21,7 +21,7 @@ public class ProjectorEventDto { @NotNull @NotBlank - private String fileName; + private String filePath; @NotNull @NotBlank diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapper.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapper.java new file mode 100644 index 0000000..f274f6b --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapper.java @@ -0,0 +1,20 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; +import java.util.Map; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring") +public interface AggregatesDataSummaryMapper { + + @Mapping(source = "numMerchant", target = "numberOfMerchants") + @Mapping(source = "numCanceledTrx", target = "countNegativeTransactions") + @Mapping(source = "numPositiveTrx", target = "countPositiveTransactions") + @Mapping(source = "totalAmountCanceledTrx", target = "sumAmountNegativeTransactions") + @Mapping(source = "totalAmountPositiveTrx", target = "sumAmountPositiveTransactions") + @Mapping(source = "maxAccountingDate", target = "maxAccountingDate") + @Mapping(source = "minAccountingDate", target = "minAccountingDate") + @Mapping(source = "checkSum", target = "sha256OriginFile") + AggregatesDataSummary getDataSummary(Map data); +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnector.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnector.java new file mode 100644 index 0000000..3885e80 --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnector.java @@ -0,0 +1,82 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.config.StorageProperties; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +import org.apache.hc.core5.http.message.BasicHeader; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Slf4j +@Component +public class StorageAccountRestConnector { + + public static final String BLOB_METADATA_PREFIX = "x-ms-meta-"; + public static final String BLOB_METADATA_QUERY = "?comp=metadata"; + public static final String SUBSCRIPTION_KEY_HEADER = "Ocp-Apim-Subscription-Key"; + private final StorageProperties properties; + private final CloseableHttpClient httpClient; + + /** + * Returns a map with header name and header value as key-value. The headers are filtered by the + * expected prefix for metadata. + * + * @param basePath - path of blob storage starting and ending with '/' + * @param fileName - name of blob storage + * @return a map with the headers + * @throws IOException - thrown by an error occurred in the http client or when the returned + * status code does not match 200 + */ + public Map getBlobMetadata(String basePath, String fileName) throws IOException { + + if (!basePath.startsWith("/") || !basePath.endsWith("/")) { + throw new IllegalArgumentException( + "Invalid format for basePath! Make it start and end with '/'"); + } + + var uri = properties.url() + + basePath + + fileName + + BLOB_METADATA_QUERY; + + final HttpGet httpGet = new HttpGet(uri); + httpGet.setHeader(new BasicHeader(SUBSCRIPTION_KEY_HEADER, properties.apiKey())); + + return httpClient.execute(httpGet, validateAndGetMetadata()); + } + + /** + * Returns the metadata as a Map. Contains the logic about where to retrieve the metadata in the + * response entity (e.g. in the body, in the headers etc...) and cleanup the data. + * + * @return a map containing the metadata + */ + protected HttpClientResponseHandler> validateAndGetMetadata() { + return response -> { + if (200 != response.getCode()) { + throw new IOException(response.getReasonPhrase()); + } + + return Arrays.stream(response.getHeaders()) + .filter(header -> header.getName().startsWith(BLOB_METADATA_PREFIX)) + .map(this::removeAdditionalHeaderText) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + }; + } + + @NotNull + private Entry removeAdditionalHeaderText(Header header) { + return Map.entry(header.getName().replaceFirst(BLOB_METADATA_PREFIX, ""), + header.getValue()); + } +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/HttpClientConfig.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/HttpClientConfig.java new file mode 100644 index 0000000..da0c267 --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/HttpClientConfig.java @@ -0,0 +1,42 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.config; + +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import javax.net.ssl.SSLContext; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.socket.ConnectionSocketFactory; +import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy; +import org.apache.hc.core5.http.config.Registry; +import org.apache.hc.core5.http.config.RegistryBuilder; +import org.apache.hc.core5.ssl.SSLContexts; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class HttpClientConfig { + + @Bean + CloseableHttpClient getHttpClient() + throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE) + .build(); + + Registry registry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", + new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)) + .build(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager( + registry); + + return HttpClients.custom().setConnectionManager(connectionManager).build(); + } +} diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/StorageProperties.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/StorageProperties.java new file mode 100644 index 0000000..4e96834 --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/config/StorageProperties.java @@ -0,0 +1,16 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.config; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "storage-account-connector") +public record StorageProperties + (@NotNull + @NotBlank + String url, + @NotNull + @NotBlank + String apiKey) { + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e597836..55c8f7e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,6 +21,10 @@ topics: report: fileTTL: ${FILE_TTL_IN_DAYS:15} +storage-account-connector: + url: https://${STORAGE_ACCOUNT_HOST:httpbin.org}/storage + apiKey: ${INTERNAL_SERVICES_API_KEY:myApiKey} + spring: config: activate: @@ -30,6 +34,8 @@ spring: uri: ${MONGODB_CONNECTION_URI:mongodb://localhost:27017} database: ${MONGODB_NAME:rtd} cloud: + function: + definition: projectorConsumer stream: bindings: projectorConsumer-in-0: # name must match [handler name]-in-0 diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/DomainToDtoMapperTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/DomainToDtoMapperTest.java index 5a42f8c..3d312a5 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/DomainToDtoMapperTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/DomainToDtoMapperTest.java @@ -2,8 +2,8 @@ import static org.assertj.core.api.Assertions.assertThat; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileMetadataDto; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileReportDtoMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileMetadataDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileReportDtoMapper; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileMetadata; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileStatusEnum; @@ -21,8 +21,9 @@ class DomainToDtoMapperTest { void mappingFromDomainToDtoWorksCorrectly() { var currentDate = LocalDateTime.now(); FileReport fileReport = FileReport.createFileReport(); - FileMetadata fileMetadata = new FileMetadata("file", 3000L, FileStatusEnum.RECEIVED_BY_PAGOPA, - currentDate); + FileMetadata fileMetadata = FileMetadata.builder().name("file").size(3000L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build(); fileReport.addFileUploaded(fileMetadata); fileReport.addAckToDownload("ack1"); fileReport.setSenderCodes(List.of("senderCode")); diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImplTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImplTest.java index 4d258f7..ca27f80 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImplTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/FileReportControllerImplTest.java @@ -8,8 +8,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.TestUtils; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileReportDto; -import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.FileReportDtoMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileReportDto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v1.FileReportDtoMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2.FileReportV2Dto; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.controller.model.v2.FileReportV2DtoMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.FileReportService; import java.util.Collections; @@ -34,15 +37,15 @@ class FileReportControllerImplTest { private final String FILE_REPORT_URL = "/file-report"; private final String SENDER_ADE_ACK_URL = "/sender-ade-ack"; - + private final String FILE_REPORT_URL_V2 = "/v2/file-report"; ObjectMapper objectMapper = new ObjectMapper(); @Autowired private MockMvc mockMvc; - @MockBean private FileReportDtoMapper mapper; - + @MockBean + private FileReportV2DtoMapper mapperV2; @MockBean private FileReportService fileReportService; @@ -63,7 +66,6 @@ void givenEmptyReportWhenGetFileReportThenReturnEmptyListJson() { content().contentType(MediaType.APPLICATION_JSON_VALUE), content().string(emptyFileReportAsJson)) .andReturn(); - } @SneakyThrows @@ -117,4 +119,47 @@ void whenGetAdeAckToDownloadThenReturns200AndCorrectBody() throws Exception { BDDMockito.verify(fileReportService, Mockito.times(1)) .getAckToDownloadList(ArgumentMatchers.anyCollection()); } + + @SneakyThrows + @Test + void givenReportWhenGetFileReportV2ThenReturnCorrectJson() { + var reportMock = TestUtils.createFileReport(0, 1); + var reportDto = Mappers.getMapper(FileReportV2DtoMapper.class).fileReportToDto(reportMock); + reportDto.getFilesRecentlyUploaded().stream() + .findAny() + .ifPresent(file -> file.setDataSummary( + AggregatesDataSummary.builder() + .countPositiveTransactions(10) + .sumAmountPositiveTransactions(1000) + .build())); + Mockito.when(mapperV2.fileReportToDto(any())).thenReturn(reportDto); + + MvcResult result = mockMvc.perform( + MockMvcRequestBuilders.get(FILE_REPORT_URL_V2).param("senderCodes", "12345")) + .andExpectAll(status().isOk(), + content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andReturn(); + FileReportV2Dto fileReportResponse = objectMapper.readValue( + result.getResponse().getContentAsString(), + FileReportV2Dto.class); + + assertThat(fileReportResponse.getFilesRecentlyUploaded()).isNotNull().hasSize(1); + var file = fileReportResponse.getFilesRecentlyUploaded().stream().findAny(); + assertThat(file).isPresent(); + var summary = file.get().getDataSummary(); + assertThat(summary).isNotNull(); + assertThat(summary.getCountPositiveTransactions()).isEqualTo(10); + assertThat(summary.getSumAmountPositiveTransactions()).isEqualTo(1000); + } + + @SneakyThrows + @Test + void givenNoQueryParamsWhenGetFileReportV2ThenReturn400() { + Mockito.when(fileReportService.getAggregateFileReport(any())) + .thenReturn(FileReport.createFileReport()); + + mockMvc.perform(MockMvcRequestBuilders.get(FILE_REPORT_URL_V2)) + .andExpect(status().isBadRequest()) + .andReturn(); + } } diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReportTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReportTest.java index 31ee51f..d67a190 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReportTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/model/FileReportTest.java @@ -24,14 +24,16 @@ void whenAddFileUploadedThenCollectionIsUpdatedAndSortedCorrectly() { FileReport fileReport = FileReport.createFileReport(); fileReport.addFileUploaded( - new FileMetadata("oldestFile", 200L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.of(2022, 1, 1, 10, 0))); + FileMetadata.builder().name("oldestFile").size(200L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.of(2022, 1, 1, 10, 0)).build()); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(1) .map(FileMetadata::getName).contains("oldestFile"); fileReport.addFileUploaded( - new FileMetadata("mostRecentFile", 200L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.of(2022, 1, 2, 10, 0))); + FileMetadata.builder().name("mostRecentFile").size(200L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.of(2022, 1, 2, 10, 0)).build()); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(2) .map(FileMetadata::getName).containsExactly("mostRecentFile", "oldestFile"); } @@ -41,14 +43,14 @@ void whenAddFileUploadedThenCollectionIsUpdatedAndSortedCorrectlyWithSameTimesta FileReport fileReport = FileReport.createFileReport(); fileReport.addFileUploaded( - new FileMetadata("fileA", 200L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.of(2022, 1, 1, 10, 0))); + FileMetadata.builder().name("fileA").size(200L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.of(2022, 1, 1, 10, 0)).build()); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(1) .map(FileMetadata::getName).contains("fileA"); fileReport.addFileUploaded( - new FileMetadata("fileB", 200L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.of(2022, 1, 1, 10, 0))); + FileMetadata.builder().name("fileB").size(200L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.of(2022, 1, 1, 10, 0)).build()); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(2) .map(FileMetadata::getName).containsExactly("fileB", "fileA"); } @@ -166,10 +168,12 @@ void givenAckListContainsOnlyOneAckWhenRemoveAckThenCollectionIsEmpty() { @Test void givenTwoFilesInsideDateRangeWhenRemoveOldFilesThenRemoveNothing() { FileReport fileReport = FileReport.createFileReport(); - var fileMetadata = new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.now().minusDays(5)); - var fileMetadata2 = new FileMetadata("file2", 3000L, FileStatusEnum.VALIDATED_BY_PAGOPA, - LocalDateTime.now().minusDays(2)); + var fileMetadata = FileMetadata.builder().name("file").size(100L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(5)).build(); + var fileMetadata2 = FileMetadata.builder().name("file2").size(3000L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(2)).build(); fileReport.addFileUploaded(fileMetadata); fileReport.addFileUploaded(fileMetadata2); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(2); @@ -182,10 +186,12 @@ void givenTwoFilesInsideDateRangeWhenRemoveOldFilesThenRemoveNothing() { @Test void givenOneFileOutDateRangeWhenRemoveOldFilesThenRemoveOneFile() { FileReport fileReport = FileReport.createFileReport(); - var fileMetadata = new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.now().minusDays(5)); - var fileMetadata2 = new FileMetadata("file2", 3000L, FileStatusEnum.VALIDATED_BY_PAGOPA, - LocalDateTime.now().minusDays(11)); + var fileMetadata = FileMetadata.builder().name("file").size(100L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(5)).build(); + var fileMetadata2 = FileMetadata.builder().name("file2").size(3000L) + .status(FileStatusEnum.VALIDATED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(11)).build(); fileReport.addFileUploaded(fileMetadata); fileReport.addFileUploaded(fileMetadata2); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(2); @@ -199,10 +205,12 @@ void givenOneFileOutDateRangeWhenRemoveOldFilesThenRemoveOneFile() { @Test void givenTwoFileOutDateRangeWhenRemoveOldFilesThenRemoveAllFiles() { FileReport fileReport = FileReport.createFileReport(); - var fileMetadata = new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.now().minusDays(12)); - var fileMetadata2 = new FileMetadata("file2", 3000L, FileStatusEnum.VALIDATED_BY_PAGOPA, - LocalDateTime.now().minusDays(11)); + var fileMetadata = FileMetadata.builder().name("file").size(100L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(12)).build(); + var fileMetadata2 = FileMetadata.builder().name("file2").size(3000L) + .status(FileStatusEnum.VALIDATED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(11)).build(); fileReport.addFileUploaded(fileMetadata); fileReport.addFileUploaded(fileMetadata2); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(2); @@ -215,10 +223,12 @@ void givenTwoFileOutDateRangeWhenRemoveOldFilesThenRemoveAllFiles() { @Test void whenRemoveOldFilesWithNullParamThenDoNothing() { FileReport fileReport = FileReport.createFileReport(); - var fileMetadata = new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, - LocalDateTime.now().minusDays(12)); - var fileMetadata2 = new FileMetadata("file2", 3000L, FileStatusEnum.VALIDATED_BY_PAGOPA, - LocalDateTime.now().minusDays(11)); + var fileMetadata = FileMetadata.builder().name("file").size(100L) + .status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(12)).build(); + var fileMetadata2 = FileMetadata.builder().name("file2").size(3000L) + .status(FileStatusEnum.VALIDATED_BY_PAGOPA) + .transmissionDate(LocalDateTime.now().minusDays(11)).build(); fileReport.addFileUploaded(fileMetadata); fileReport.addFileUploaded(fileMetadata2); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(2); diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommandTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommandTest.java new file mode 100644 index 0000000..9bd3a24 --- /dev/null +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/DecryptedEventCommandTest.java @@ -0,0 +1,75 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service; + + +import static it.gov.pagopa.rtd.ms.rtdmsfilereporter.TestUtils.createFileReport; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileMetadata; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class DecryptedEventCommandTest { + + private DecryptedEventCommand command; + @Mock + private StorageAccountService client; + + @BeforeEach + void setUp() { + command = new DecryptedEventCommand(client); + } + + @Test + void whenCommandIsExecutedThenMetadataAreAddedToReport() { + // arrange + var report = createFileReport(0, 1); + var metadata = FileMetadata.createNewFileMetadata("filename"); + var dataSummary = AggregatesDataSummary.builder().numberOfMerchants(10) + .countPositiveTransactions(5).sumAmountPositiveTransactions(100).build(); + Mockito.when(client.getMetadata(any(), any())).thenReturn(dataSummary); + + command.accept(report, metadata); + + Mockito.verify(client, Mockito.times(1)).getMetadata(any(), any()); + + // assert metadata has been enriched with stuff + assertThat(metadata).isNotNull(); + var summary = metadata.getAggregatesDataSummary(); + assertThat(summary).isNotNull(); + assertThat(summary.getCountPositiveTransactions()).isEqualTo(5); + assertThat(summary.getNumberOfMerchants()).isEqualTo(10); + assertThat(summary.getSumAmountPositiveTransactions()).isEqualTo( + 100); + + // assert report has been filled with additional data + assertThat(report).isNotNull(); + var fileData = report.getFilesUploaded().stream().findAny(); + assertThat(fileData).isPresent(); + summary = fileData.get().getAggregatesDataSummary(); + assertThat(summary).isNotNull(); + assertThat(summary.getNumberOfMerchants()).isEqualTo(10); + assertThat(summary.getSumAmountPositiveTransactions()).isEqualTo(100); + assertThat(summary.getCountPositiveTransactions()).isEqualTo(5); + } + + @Test + void whenDataSummaryIsNullThenThrowsSummaryIsEmpty() { + var report = createFileReport(0, 1); + var metadata = FileMetadata.createNewFileMetadata("filename"); + Mockito.when(client.getMetadata(any(), any())).thenReturn(null); + + command.accept(report, metadata); + + assertThat(metadata).isNotNull(); + assertThat(metadata.getAggregatesDataSummary().getCountPositiveTransactions()).isZero(); + assertThat(metadata.getAggregatesDataSummary().getNumberOfMerchants()).isZero(); + assertThat(metadata.getAggregatesDataSummary().getSumAmountPositiveTransactions()).isZero(); + } +} \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountServiceTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountServiceTest.java new file mode 100644 index 0000000..6591629 --- /dev/null +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/domain/service/StorageAccountServiceTest.java @@ -0,0 +1,89 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.AggregatesDataSummaryMapper; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.StorageAccountRestConnector; +import java.io.IOException; +import java.time.format.DateTimeParseException; +import java.util.Map; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class StorageAccountServiceTest { + + @Mock + private StorageAccountRestConnector connector; + private StorageAccountService service; + @Mock + private AggregatesDataSummaryMapper mapper; + + @BeforeEach + void setUp() { + service = new StorageAccountService(connector, mapper); + } + + @Test + @SneakyThrows + void whenGetMetadataThenReturnsDataSummary() { + + when(connector.getBlobMetadata(any(), any())) + .thenReturn(Map.of("data1", "value1", "key2", "value2")); + when(mapper.getDataSummary(any())) + .thenReturn(AggregatesDataSummary.builder() + .sumAmountPositiveTransactions(10000) + .numberOfMerchants(10) + .build()); + + var response = service.getMetadata("container", "fileName"); + + verify(connector).getBlobMetadata(any(), any()); + verify(mapper).getDataSummary(any()); + assertThat(response).isNotNull(); + assertThat(response.getNumberOfMerchants()).isEqualTo(10); + assertThat(response.getSumAmountPositiveTransactions()).isEqualTo(10000); + } + + @Test + @SneakyThrows + void whenGetMetadataThrowsExceptionThenReturnsInvalidDataSummary() { + + when(connector.getBlobMetadata(any(), any())).thenThrow(new IOException()); + + var response = service.getMetadata("container", "fileName"); + + verify(connector).getBlobMetadata(any(), any()); + verify(mapper, times(0)).getDataSummary(any()); + assertThat(response).isNotNull(); + assertThat(response.getNumberOfMerchants()).isEqualTo(-1); + assertThat(response.getSumAmountPositiveTransactions()).isEqualTo(-1); + } + + @Test + @SneakyThrows + void givenBadFormatMetadataWhenGetMetadataThenReturnsInvalidDataSummary() { + + when(connector.getBlobMetadata(any(), any())) + .thenReturn(Map.of("date", "2022-10-01", "key2", "value2")); + when(mapper.getDataSummary(any())).thenThrow(new DateTimeParseException("error", "2022-10-01", 10)); + + var response = service.getMetadata("container", "fileName"); + + verify(connector).getBlobMetadata(any(), any()); + verify(mapper).getDataSummary(any()); + assertThat(response).isNotNull(); + assertThat(response.getNumberOfMerchants()).isEqualTo(-1); + assertThat(response.getSumAmountPositiveTransactions()).isEqualTo(-1); + } + +} \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/EventHandlerTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/EventHandlerTest.java index 537f3c5..f4beb69 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/EventHandlerTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/EventHandlerTest.java @@ -65,7 +65,7 @@ void whenACorrectEventIsSentThenConsumeIt() { void whenAMalformedEventIsSentThenRaiseException() { var currentDate = LocalDateTime.now(); String eventDtoJson = - "{\"fileName\": \"prova\", \"sender\": \"Sender\", \"size\":100, \"receiveTimestamp\": \"" + + "{\"filePath\": \"prova\", \"sender\": \"Sender\", \"size\":100, \"receiveTimestamp\": \"" + currentDate + "\", \"status\": \"UNMAPPED\"}"; assertThatThrownBy(() -> objectMapper.readValue(eventDtoJson, ProjectorEventDto.class)) diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapterTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapterTest.java index 37cbd0e..b41e8a5 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapterTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/FileReportEventAdapterTest.java @@ -8,6 +8,7 @@ import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileMetadata; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileReport; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.FileStatusEnum; +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.DecryptedEventCommand; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.service.FileReportService; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.event.model.EventStatusEnum; import it.gov.pagopa.rtd.ms.rtdmsfilereporter.event.model.EventToDomainMapper; @@ -18,19 +19,21 @@ import java.time.LocalDateTime; import java.util.Collections; import java.util.Optional; -import lombok.SneakyThrows; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mapstruct.factory.Mappers; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) class FileReportEventAdapterTest { @Mock private FileReportService service; + @Mock + private DecryptedEventCommand command; private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); private FileReportEventAdapter adapter; private AutoCloseable autoCloseable; @@ -39,17 +42,11 @@ class FileReportEventAdapterTest { @BeforeEach void setUp() { - autoCloseable = MockitoAnnotations.openMocks(this); - adapter = new FileReportEventAdapter(service, validator, mapper, new FileReportCommandFactory()); + adapter = new FileReportEventAdapter(service, validator, mapper, + new FileReportCommandFactory(command)); adapter.setFileTimeToLiveInDays(fileTTL); } - @SneakyThrows - @AfterEach - void tearDown() { - autoCloseable.close(); - } - @Test void whenReceiveAMalformedEventThenRaiseException() { ProjectorEventDto eventDto = new ProjectorEventDto("file", null, 100L, LocalDateTime.now(), @@ -69,7 +66,8 @@ void whenReceiveAnEventThenAddFileToEmptyReport() { FileReport expectedReport = FileReport.createFileReport(); expectedReport.setSenderCodes(Collections.singleton("12345")); expectedReport.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); adapter.consumeEvent(eventDto); @@ -82,7 +80,8 @@ void whenReceiveAnEventThenChangeStatusToFile() { var currentDate = LocalDateTime.now(); FileReport reportToBeRead = FileReport.createFileReport(); reportToBeRead.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); Mockito.when(service.getFileReport(any())).thenReturn(Optional.of(reportToBeRead)); // even if other fields are different, the only field that will be changed is "status" @@ -90,7 +89,8 @@ void whenReceiveAnEventThenChangeStatusToFile() { EventStatusEnum.DECRYPTED); FileReport expectedReport = FileReport.createFileReport(); expectedReport.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.VALIDATED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.VALIDATED_BY_PAGOPA) + .transmissionDate(currentDate).build()); adapter.consumeEvent(eventDto); @@ -103,15 +103,16 @@ void whenReceiveAnEventThenRemoveOldFile() { var currentDate = LocalDateTime.now(); FileReport reportToBeRead = FileReport.createFileReport(); reportToBeRead.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, - currentDate.minusDays(fileTTL + 1))); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate.minusDays(fileTTL + 1)).build()); Mockito.when(service.getFileReport(any())).thenReturn(Optional.of(reportToBeRead)); ProjectorEventDto eventDto = new ProjectorEventDto("newFile", "12345", 300L, currentDate, EventStatusEnum.RECEIVED); FileReport expectedReport = FileReport.createFileReport(); expectedReport.addFileUploaded( - new FileMetadata("newFile", 300L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("newFile").size(300L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); adapter.consumeEvent(eventDto); @@ -124,16 +125,20 @@ void whenReceiveAnEventThenAddNewFile() { var currentDate = LocalDateTime.now(); FileReport reportToBeRead = FileReport.createFileReport(); reportToBeRead.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); Mockito.when(service.getFileReport(any())).thenReturn(Optional.of(reportToBeRead)); ProjectorEventDto eventDto = new ProjectorEventDto("newFile", "12345", 300L, currentDate, EventStatusEnum.SENT_TO_ADE); FileReport expectedReport = FileReport.createFileReport(); expectedReport.addFileUploaded( - new FileMetadata("newFile", 300L, FileStatusEnum.SENT_TO_AGENZIA_DELLE_ENTRATE, currentDate)); + FileMetadata.builder().name("newFile").size(300L) + .status(FileStatusEnum.SENT_TO_AGENZIA_DELLE_ENTRATE) + .transmissionDate(currentDate).build()); expectedReport.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); adapter.consumeEvent(eventDto); @@ -185,7 +190,8 @@ void whenReceiveStatusNotMappedThenThrowException() { FileReport expectedReport = FileReport.createFileReport(); expectedReport.addSenderCode("12345"); expectedReport.addFileUploaded( - new FileMetadata("file", 100L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(100L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); adapter.consumeEvent(eventDto); diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapperTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapperTest.java index 2f8cde2..c9d86e5 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapperTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/event/model/EventToDomainMapperTest.java @@ -24,6 +24,14 @@ private static Stream provideStatus() { ); } + private static Stream provideFilePath() { + return Stream.of( + Arguments.of("fileWithoutPath.txt", "", "fileWithoutPath.txt"), + Arguments.of("storage/container/fileName.txt", "storage/container/", "fileName.txt"), + Arguments.of("/fileName.txt", "/", "fileName.txt") + ); + } + @ParameterizedTest @MethodSource("provideStatus") void mappingFromEventToDomainWorksCorrectly(EventStatusEnum eventStatus, @@ -35,10 +43,21 @@ void mappingFromEventToDomainWorksCorrectly(EventStatusEnum eventStatus, var domainFile = mapper.eventToDomain(eventDto); assertThat(domainFile).isNotNull(); - assertThat(domainFile.getName()).isEqualTo("filename"); assertThat(domainFile.getStatus()).isEqualTo(domainStatus); assertThat(domainFile.getSize()).isEqualTo(100L); assertThat(domainFile.getTransmissionDate()).isEqualTo(currentDate); } + @ParameterizedTest + @MethodSource("provideFilePath") + void mappingPathToTwoDifferentFields(String filePath, String expectedPath, String expectedName) { + var eventDto = new ProjectorEventDto(filePath, "12345", 100L, null, + EventStatusEnum.RECEIVED); + + var domainFile = mapper.eventToDomain(eventDto); + + assertThat(domainFile).isNotNull(); + assertThat(domainFile.getName()).isEqualTo(expectedName); + assertThat(domainFile.getPath()).isEqualTo(expectedPath); + } } \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapperTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapperTest.java new file mode 100644 index 0000000..e69c860 --- /dev/null +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/AggregatesDataSummaryMapperTest.java @@ -0,0 +1,37 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign; + +import static org.assertj.core.api.Assertions.assertThat; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.domain.model.AggregatesDataSummary; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class AggregatesDataSummaryMapperTest { + + private final AggregatesDataSummaryMapper mapper = Mappers.getMapper( + AggregatesDataSummaryMapper.class); + + @Test + void whenMapToDomainPojoThenOk() { + var metadataMap = Map.of("numMerchant", "10", + "totalAmountCanceledTrx", "1000"); + + var dataSummary = mapper.getDataSummary(metadataMap); + + assertThat(dataSummary).isNotNull() + .extracting(AggregatesDataSummary::getCountNegativeTransactions).isEqualTo(0L); + assertThat(dataSummary) + .extracting(AggregatesDataSummary::getCountPositiveTransactions).isEqualTo(0L); + assertThat(dataSummary) + .extracting(AggregatesDataSummary::getNumberOfMerchants).isEqualTo(10); + assertThat(dataSummary) + .extracting(AggregatesDataSummary::getSumAmountPositiveTransactions).isEqualTo(0L); + assertThat(dataSummary) + .extracting(AggregatesDataSummary::getSumAmountNegativeTransactions).isEqualTo(1000L); + assertThat(dataSummary) + .extracting(AggregatesDataSummary::getMinAccountingDate).isNull(); + assertThat(dataSummary) + .extracting(AggregatesDataSummary::getMaxAccountingDate).isNull(); + } +} \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnectorTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnectorTest.java new file mode 100644 index 0000000..bf94efb --- /dev/null +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/feign/StorageAccountRestConnectorTest.java @@ -0,0 +1,123 @@ +package it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import it.gov.pagopa.rtd.ms.rtdmsfilereporter.feign.config.StorageProperties; +import java.io.IOException; +import java.util.Map; +import lombok.SneakyThrows; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +import org.apache.hc.core5.http.message.BasicHeader; +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.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class StorageAccountRestConnectorTest { + + StorageAccountRestConnector connector; + @Mock + CloseableHttpClient client; + + @BeforeEach + void setUp() { + connector = new StorageAccountRestConnector(new StorageProperties("/url/", "key"), client); + } + + @Test + @SneakyThrows + void whenGetBlobTheReturnMap() { + var basePath = "/container/"; + var name = "name"; + var httpGet = new HttpGet(basePath + name + StorageAccountRestConnector.BLOB_METADATA_QUERY); + httpGet.setHeaders(new BasicHeader("Ocp-Apim-Subscription-Key", "key")); + when(client.execute(any(ClassicHttpRequest.class), any(HttpClientResponseHandler.class))) + .thenReturn(Map.of()); + + var response = connector.getBlobMetadata(basePath, name); + + verify(client).execute(any(ClassicHttpRequest.class), any(HttpClientResponseHandler.class)); + assertThat(response).isNotNull().isEmpty(); + } + + @Test + @SneakyThrows + void givenResponseWith404WhenClientExecuteThenThrowException() { + when(client.execute(any(ClassicHttpRequest.class), any(HttpClientResponseHandler.class))) + .thenThrow(new IOException("KO")); + + assertThatThrownBy(() -> connector.getBlobMetadata("/basePath/", "name")) + .isInstanceOf(IOException.class) + .hasMessage("KO"); + + verify(client).execute(any(ClassicHttpRequest.class), any(HttpClientResponseHandler.class)); + } + + @SneakyThrows + @ParameterizedTest + @ValueSource(strings = {"missingBothSlash", "/missingFinalSlash", "missingInitialSlash/"}) + void givenABadPathFormatWhenGetMetadataThenThrowException(String basePath) { + + assertThatThrownBy(() -> connector.getBlobMetadata(basePath, "name")) + .isInstanceOf(IllegalArgumentException.class); + + verify(client, times(0)).execute(any(ClassicHttpRequest.class), + any(HttpClientResponseHandler.class)); + } + + @Test + @SneakyThrows + void givenResponseWith404WhenValidateThenThrowException() { + var response = DefaultClassicHttpResponseFactory.INSTANCE.newHttpResponse(404, "not found"); + response.setHeaders(new BasicHeader("content-length", 10), + new BasicHeader(StorageAccountRestConnector.BLOB_METADATA_PREFIX + "foo", "bar"), + new BasicHeader(StorageAccountRestConnector.BLOB_METADATA_PREFIX + "key", "value")); + + var lambda = connector.validateAndGetMetadata(); + + assertThatThrownBy(() -> lambda.handleResponse(response)) + .isInstanceOf(IOException.class); + } + + @Test + @SneakyThrows + void givenResponseWith200WhenValidateThenReturnsMetadata() { + var response = DefaultClassicHttpResponseFactory.INSTANCE.newHttpResponse(200, "ok"); + response.setHeaders(new BasicHeader("content-length", 10), + new BasicHeader(StorageAccountRestConnector.BLOB_METADATA_PREFIX + "foo", "bar"), + new BasicHeader(StorageAccountRestConnector.BLOB_METADATA_PREFIX + "key", "value")); + + var metadata = connector.validateAndGetMetadata().handleResponse(response); + + assertThat(metadata) + .hasSize(2) + .containsExactlyInAnyOrderEntriesOf(Map.ofEntries( + entry("foo", "bar"), + entry("key", "value"))); + } + + @Test + @SneakyThrows + void givenResponseWith200WhenValidateThenReturnsEmptyMap() { + var response = DefaultClassicHttpResponseFactory.INSTANCE.newHttpResponse(200, "ok"); + response.setHeaders(new BasicHeader("content-length", 10)); + + var metadata = connector.validateAndGetMetadata().handleResponse(response); + + assertThat(metadata).isNotNull().isEmpty(); + } +} \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/persistance/FileReportRepositoryImplTest.java b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/persistance/FileReportRepositoryImplTest.java index bd96737..819c56d 100644 --- a/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/persistance/FileReportRepositoryImplTest.java +++ b/src/test/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/persistance/FileReportRepositoryImplTest.java @@ -99,7 +99,8 @@ void mappingFromEntityToDomainWorksCorrectly() { FileReportEntity fileReportEntity = new FileReportEntity(); fileReportEntity.setSenderCode("12345"); fileReportEntity.setFilesUploaded( - List.of(new FileMetadata("file", 200L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate))); + List.of(FileMetadata.builder().name("file").size(200L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build())); fileReportEntity.setAckToDownload(List.of("ack1", "ack2")); fileReportEntity.setId("testID"); @@ -108,7 +109,8 @@ void mappingFromEntityToDomainWorksCorrectly() { assertThat(fileReport).isNotNull(); assertThat(fileReport.getFilesUploaded()).isNotNull().hasSize(1) .containsExactly( - new FileMetadata("file", 200L, FileStatusEnum.RECEIVED_BY_PAGOPA, currentDate)); + FileMetadata.builder().name("file").size(200L).status(FileStatusEnum.RECEIVED_BY_PAGOPA) + .transmissionDate(currentDate).build()); assertThat(fileReport.getAckToDownload()).isNotNull().hasSize(2).contains("ack1", "ack2"); assertThat(fileReport.getSenderCodes()).isNotNull().hasSize(1).contains("12345"); assertThat(fileReport.getId()).isNotNull().isEqualTo("testID"); From b37d533de1a3fcd4b9a88b1768df9718a3397f82 Mon Sep 17 00:00:00 2001 From: Andrea Morabito <78792023+and-mora@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:39:21 +0200 Subject: [PATCH 2/2] fix: remove log injection (#135) --- .../controller/ConsumeEventControllerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java index 684c5c6..de2f830 100644 --- a/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java +++ b/src/main/java/it/gov/pagopa/rtd/ms/rtdmsfilereporter/controller/ConsumeEventControllerImpl.java @@ -20,7 +20,7 @@ public class ConsumeEventControllerImpl implements ConsumeEventController { @Override public void consumeEvent(@RequestBody @Valid ProjectorEventDto eventData) { - log.info("Received event [{}]", eventData.getFilePath()); + log.info("Received event from controller"); adapter.consumeEvent(MessageBuilder.withPayload(eventData).build().getPayload()); }