Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package it.gov.pagopa.idpay.transactions.controller;

import it.gov.pagopa.idpay.transactions.dto.PatchReportRequest;
import it.gov.pagopa.idpay.transactions.dto.ReportDTO;
import it.gov.pagopa.idpay.transactions.dto.ReportListDTO;
import it.gov.pagopa.idpay.transactions.dto.ReportRequest;
import it.gov.pagopa.idpay.transactions.dto.*;
import it.gov.pagopa.idpay.transactions.dto.report.Report2RunDto;
import it.gov.pagopa.idpay.transactions.dto.report.ReportGenerateForce;
import jakarta.validation.Valid;
Expand All @@ -25,6 +22,14 @@ Mono<ReportListDTO> getTransactionsReports(
@PageableDefault Pageable pageable
);

@GetMapping("/initiatives/{initiativeId}/reports/{reportId}/download")
Mono<DownloadReportResponseDTO> downloadTransactionsReport(
@RequestHeader(value = "x-merchant-id", required = false) String merchantId,
@RequestHeader(value = "x-organization-role", required = false) String organizationRole,
@PathVariable("initiativeId") String initiativeId,
@PathVariable("reportId") String reportId
);

@PostMapping("/initiatives/{initiativeId}/reports")
Mono<ReportDTO> generateReport(@RequestHeader("x-merchant-id") String merchantId,
@RequestHeader(value = "x-organization-role", required = false) String organizationRole,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package it.gov.pagopa.idpay.transactions.controller;

import it.gov.pagopa.idpay.transactions.dto.PatchReportRequest;
import it.gov.pagopa.idpay.transactions.dto.ReportDTO;
import it.gov.pagopa.idpay.transactions.dto.ReportListDTO;
import it.gov.pagopa.idpay.transactions.dto.ReportRequest;
import it.gov.pagopa.idpay.transactions.dto.*;
import it.gov.pagopa.idpay.transactions.dto.report.Report2RunDto;
import it.gov.pagopa.idpay.transactions.dto.report.ReportGenerateForce;
import it.gov.pagopa.idpay.transactions.service.ReportService;
Expand Down Expand Up @@ -41,6 +38,25 @@ public Mono<ReportListDTO> getTransactionsReports(
.flatMap(page -> Mono.just(reportMapper.toListDTO(page)));
}

@Override
public Mono<DownloadReportResponseDTO> downloadTransactionsReport(
String merchantId,
String organizationRole,
String initiativeId,
String reportId
) {
log.info("[DOWNLOAD_TRANSACTIONS_REPORT] Request received for initiative: {}, reportId: {}",
Utilities.sanitizeString(initiativeId),
Utilities.sanitizeString(reportId));

return reportService.downloadTransactionsReport(
merchantId,
organizationRole,
initiativeId,
reportId
);
}


@Override
public Mono<ReportDTO> generateReport(String merchantId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package it.gov.pagopa.idpay.transactions.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class DownloadReportResponseDTO {

private String reportUrl;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@
public interface ReportRepository extends ReactiveMongoRepository<Report, String>, ReportSpecificRepository {

Mono<Report> findByIdAndInitiativeId(String reportId, String initiativeId);

Mono<Report> findByIdAndInitiativeIdAndMerchantId(
String reportId,
String initiativeId,
String merchantId
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package it.gov.pagopa.idpay.transactions.service;

import it.gov.pagopa.idpay.transactions.dto.DownloadReportResponseDTO;
import it.gov.pagopa.idpay.transactions.dto.PatchReportRequest;
import it.gov.pagopa.idpay.transactions.dto.ReportDTO;
import it.gov.pagopa.idpay.transactions.dto.ReportRequest;
Expand All @@ -24,5 +25,13 @@ Mono<ReportDTO> patchReport(String initiativeId,
String reportId,
PatchReportRequest request);

Mono<DownloadReportResponseDTO> downloadTransactionsReport(
String merchantId,
String organizationRole,
String initiativeId,
String reportId
);


Mono<List<Report2RunDto>> forceGenerateReports(ReportGenerateForce reportGenerateForce);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package it.gov.pagopa.idpay.transactions.service;

import it.gov.pagopa.common.web.exception.ClientExceptionWithBody;
import it.gov.pagopa.idpay.transactions.dto.DownloadReportResponseDTO;
import it.gov.pagopa.idpay.transactions.dto.PatchReportRequest;
import it.gov.pagopa.idpay.transactions.data.factory.DataFactoryService;
import it.gov.pagopa.idpay.transactions.dto.ReportDTO;
Expand All @@ -14,6 +15,7 @@
import it.gov.pagopa.idpay.transactions.exception.AzureConnectingErrorException;
import it.gov.pagopa.idpay.transactions.model.Report;
import it.gov.pagopa.idpay.transactions.repository.ReportRepository;
import it.gov.pagopa.idpay.transactions.storage.ReportBlobService;
import it.gov.pagopa.idpay.transactions.utils.Utilities;
import it.gov.pagopa.idpay.transactions.connector.rest.MerchantRestClient;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -42,16 +44,19 @@ public class ReportServiceImpl implements ReportService {

private final ReportMapper reportMapper;

private final ReportBlobService reportBlobService;

private final DataFactoryService dataFactoryService;

public ReportServiceImpl(ReportRepository reportRepository, MerchantRestClient merchantRestClient, ReportMapper reportMapper, DataFactoryService dataFactoryService) {
public ReportServiceImpl(ReportRepository reportRepository, MerchantRestClient merchantRestClient, ReportMapper reportMapper, ReportBlobService reportBlobService, DataFactoryService dataFactoryService) {
this.reportRepository = reportRepository;
this.merchantRestClient = merchantRestClient;
this.reportMapper = reportMapper;
this.reportBlobService = reportBlobService;
this.dataFactoryService = dataFactoryService;
}

private static final List<String> ALLOWED_ROLES = List.of(
static final List<String> ALLOWED_ROLES = List.of(
"operator1", "operator2", "operator3"
);
private static final DateTimeFormatter FILE_NAME_FORMAT = DateTimeFormatter.ofPattern("ddMMyyyyHHmmss");
Expand Down Expand Up @@ -225,4 +230,82 @@ private Mono<Report2RunDto> triggerTransactionReportPipeline(Report report) {
.reportId(report.getId())
.runId(runId).build());
}
@Override
public Mono<DownloadReportResponseDTO> downloadTransactionsReport(
String merchantId,
String organizationRole,
String initiativeId,
String reportId
) {

if ((merchantId == null || merchantId.isBlank()) &&
(organizationRole == null || organizationRole.isBlank())) {

return Mono.error(new ClientExceptionWithBody(
HttpStatus.BAD_REQUEST,
MERCHANT_ID_OR_ORGANIZATION_ROLE_ARE_MANDATORY,
ERROR_MESSAGE_MERCHANT_ID_OR_ORGANIZATION_ROLE_ARE_MANDATORY
));
}

if (merchantId != null && organizationRole != null) {
return Mono.error(new ClientExceptionWithBody(
HttpStatus.BAD_REQUEST,
MERCHANT_ID_AND_ORGANIZATION_ROLE_CANNOT_COEXIST,
ERROR_MESSAGE_MERCHANT_ID_AND_ORGANIZATION_ROLE_CANNOT_COEXIST
));
}

if (organizationRole != null &&
ALLOWED_ROLES.stream().noneMatch(role -> role.equalsIgnoreCase(organizationRole))) {

return Mono.error(new ClientExceptionWithBody(
HttpStatus.BAD_REQUEST,
INVALID_ORGANIZATION_ROLE,
ERROR_MESSAGE_INVALID_ORGANIZATION_ROLE
));
}

Mono<Report> query = merchantId == null
? reportRepository.findByIdAndInitiativeId(reportId, initiativeId)
: reportRepository.findByIdAndInitiativeIdAndMerchantId(reportId, initiativeId, merchantId);

return query
.switchIfEmpty(Mono.error(new ClientExceptionWithBody(
HttpStatus.NOT_FOUND,
REPORT_NOT_FOUND,
ERROR_MESSAGE_REPORT_NOT_FOUND.formatted(reportId, initiativeId)
)))
.map(report -> {

if (!ReportStatus.GENERATED.equals(report.getReportStatus())) {
throw new ClientExceptionWithBody(
HttpStatus.BAD_REQUEST,
REPORT_NOT_GENERATED,
ERROR_MESSAGE_REPORT_NOT_GENERATED.formatted(reportId)
);
}

String filename = report.getFileName();
if (filename == null || filename.isBlank()) {
throw new ClientExceptionWithBody(
HttpStatus.INTERNAL_SERVER_ERROR,
REPORT_MISSING_FILENAME,
ERROR_MESSAGE_REPORT_MISSING_FILENAME.formatted(reportId)
);
}

String blobPath = String.format(
"/initiative/%s/merchant/%s/report/%s",
initiativeId,
report.getMerchantId(),
filename
);

return DownloadReportResponseDTO.builder()
.reportUrl(reportBlobService.getFileSignedUrl(blobPath))
.build();
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ public BlobContainerClient rewardBatchesContainerClient(BlobServiceClient blobSe
return blobServiceClient.getBlobContainerClient(properties.getCsvContainerReference());
}

@Bean("reportsContainerClient")
public BlobContainerClient reportsContainerClient(BlobServiceClient blobServiceClient){
return blobServiceClient.getBlobContainerClient(properties.getReportsContainerReference());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public class BlobStorageProperties {
private String containerReference;
private String csvContainerReference;
private Integer invoiceTokenDurationSeconds;
private String reportsContainerReference;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package it.gov.pagopa.idpay.transactions.storage;

public interface ReportBlobService {
String getFileSignedUrl(String blobPath);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package it.gov.pagopa.idpay.transactions.storage;

import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class ReportBlobServiceImpl extends AbstractBlobStorageClient implements ReportBlobService {

public ReportBlobServiceImpl(
BlobServiceClient blobServiceClient,
@Qualifier("reportsContainerClient") BlobContainerClient reportsContainerClient,
BlobStorageProperties properties) {

super(blobServiceClient, reportsContainerClient, properties.getInvoiceTokenDurationSeconds());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ private ExceptionCode(){}
public static final String MERCHANT_ID_OR_ORGANIZATION_ROLE_ARE_MANDATORY = "MERCHANT_ID_OR_ORGANIZATION_ROLE_ARE_MANDATORY";
public static final String MERCHANT_ID_AND_ORGANIZATION_ROLE_CANNOT_COEXIST = "MERCHANT_ID_AND_ORGANIZATION_ROLE_CANNOT_COEXIST";
public static final String INVALID_ORGANIZATION_ROLE = "INVALID_ORGANIZATION_ROLE";
public static final String REPORT_MISSING_FILENAME = "REPORT_MISSING_FILENAME";

public static final String ROLE_NOT_ALLOWED_FOR_L1_PROMOTION = "ROLE_NOT_ALLOWED_FOR_L1_PROMOTION";
public static final String ROLE_NOT_ALLOWED_FOR_L2_PROMOTION = "ROLE_NOT_ALLOWED_FOR_L2_PROMOTION";
Expand All @@ -39,6 +40,7 @@ private ExceptionCode(){}
public static final String REWARD_BATCH_PREVIOUS_NOT_SENT = "REWARD_BATCH_PREVIOUS_NOT_SENT";
public static final String INVALID_CHECKS_ERROR = "INVALID_CHECKS_ERROR";
public static final String REPORT_NOT_FOUND = "REPORT_NOT_FOUND";
public static final String REPORT_NOT_GENERATED = "REPORT_NOT_GENERATED";
public static final String MERCHANT_NOT_FOUND = "MERCHANT_NOT_FOUND";
}

Expand Down Expand Up @@ -81,5 +83,7 @@ private ExceptionMessage(){}
public static final String ERROR_MESSAGE_INVALID_CHECKS_ERROR = "At least one checksError field must be true";
public static final String ERROR_MESSAGE_REPORT_NOT_FOUND = "Report %s not found for initiative %s ";
public static final String ERROR_MESSAGE_MERCHANT_NOT_FOUND = "Merchant %s not found for initiative %s ";
public static final String ERROR_MESSAGE_REPORT_MISSING_FILENAME = "The report %s does not have an associated file name and cannot be downloaded";
public static final String ERROR_MESSAGE_REPORT_NOT_GENERATED = "The report %s is not generated yet and cannot be downloaded";
}
}
Loading
Loading