From 47c1096add3eed1f9a71cd5f39670b1468e019c1 Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 12:42:15 +0100 Subject: [PATCH 1/7] [PRDP-257] Added blob storage client exception --- .../exception/BlobStorageClientException.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/BlobStorageClientException.java diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/BlobStorageClientException.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/BlobStorageClientException.java new file mode 100644 index 0000000..1739a95 --- /dev/null +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/BlobStorageClientException.java @@ -0,0 +1,24 @@ +package it.gov.pagopa.receipt.pdf.helpdesk.exception; + +import lombok.Getter; + +/** + * Thrown in case of error when retrieving the PDF receipt from blob storage + */ +@Getter +public class BlobStorageClientException extends Exception { + + private final int statusCode; + + /** + * Constructs new exception with provided error code, message and cause + * + * @param statusCode Error code + * @param message Detail message + * @param cause Exception causing the constructed one + */ + public BlobStorageClientException(int statusCode, String message, Throwable cause) { + super(message, cause); + this.statusCode = statusCode; + } +} From 84f9a97c50a3950b0c1518a30f8ca6fae1f8eb4a Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 12:42:27 +0100 Subject: [PATCH 2/7] [PRDP-257] Added getReceiptPdf http trigger function --- .../receipt/pdf/helpdesk/GetReceiptPdf.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java new file mode 100644 index 0000000..3fde8ab --- /dev/null +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java @@ -0,0 +1,85 @@ +package it.gov.pagopa.receipt.pdf.helpdesk; + +import com.microsoft.azure.functions.*; +import com.microsoft.azure.functions.annotation.AuthorizationLevel; +import com.microsoft.azure.functions.annotation.BindingName; +import com.microsoft.azure.functions.annotation.FunctionName; +import com.microsoft.azure.functions.annotation.HttpTrigger; +import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptBlobClient; +import it.gov.pagopa.receipt.pdf.helpdesk.client.impl.ReceiptBlobClientImpl; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.BlobStorageClientException; +import it.gov.pagopa.receipt.pdf.helpdesk.model.ProblemJson; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.time.LocalDateTime; +import java.util.Optional; + +/** + * Azure Functions with HTTP Trigger that retrieves a PDF from Azure Blob Storage + */ +public class GetReceiptPdf { + + private final Logger logger = LoggerFactory.getLogger(GetReceiptPdf.class); + + private final ReceiptBlobClient receiptBlobClient; + + public GetReceiptPdf() { + this.receiptBlobClient = ReceiptBlobClientImpl.getInstance(); + } + + GetReceiptPdf(ReceiptBlobClient receiptBlobClient) { + this.receiptBlobClient = receiptBlobClient; + } + + /** + * This function will be invoked when a Http Trigger occurs. + *

+ * It retrieves the receipt pdf with the specified file name + *

+ * + * @return response with {@link HttpStatus#OK} and the pdf if found + */ + @FunctionName("GetReceiptPdf") + public HttpResponseMessage run( + @HttpTrigger(name = "GetReceiptTrigger", + methods = {HttpMethod.GET}, + route = "pdf-receipts/{file-name}", + authLevel = AuthorizationLevel.FUNCTION) + HttpRequestMessage> request, + @BindingName("file-name") String fileName, + final ExecutionContext context) { + logger.info("[{}] function called at {}", context.getFunctionName(), LocalDateTime.now()); + + if (fileName == null || fileName.isBlank()) { + return request + .createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ProblemJson.builder() + .title(HttpStatus.BAD_REQUEST.name()) + .detail("Please pass a valid file name") + .status(HttpStatus.BAD_REQUEST.value()) + .build()) + .build(); + } + + try { + File pdfFile = this.receiptBlobClient.getAttachmentFromBlobStorage(fileName); + FileInputStream inputStream = new FileInputStream(pdfFile); + byte [] result = IOUtils.toByteArray(inputStream); + Files.deleteIfExists(pdfFile.toPath()); + return request + .createResponseBuilder(HttpStatus.OK) + .body(result) + .build(); + } catch (BlobStorageClientException | IOException e) { + String responseMsg = String.format("Unable to retrieve the receipt pdf with file name %s", fileName); + logger.error("[{}] {}", context.getFunctionName(), responseMsg, e); + return request.createResponseBuilder(HttpStatus.NOT_FOUND).body(responseMsg).build(); + } + } +} From 4169d5746ce362503ca4cf73c624357151ca1d77 Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 12:42:44 +0100 Subject: [PATCH 3/7] [PRDP-257] Added download attachment method to blob client --- .../helpdesk/client/ReceiptBlobClient.java | 3 + .../client/impl/ReceiptBlobClientImpl.java | 94 ++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptBlobClient.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptBlobClient.java index dcdb49b..a5a8fc2 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptBlobClient.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptBlobClient.java @@ -1,10 +1,13 @@ package it.gov.pagopa.receipt.pdf.helpdesk.client; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.BlobStorageClientException; import it.gov.pagopa.receipt.pdf.helpdesk.model.response.BlobStorageResponse; +import java.io.File; import java.io.InputStream; public interface ReceiptBlobClient { BlobStorageResponse savePdfToBlobStorage(InputStream pdf, String fileName); + File getAttachmentFromBlobStorage(String fileName) throws BlobStorageClientException; } diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImpl.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImpl.java index 1bed09c..367bba1 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImpl.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImpl.java @@ -1,30 +1,47 @@ package it.gov.pagopa.receipt.pdf.helpdesk.client.impl; import com.azure.core.http.rest.Response; +import com.azure.core.util.Context; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.models.BlobStorageException; import com.azure.storage.blob.models.BlockBlobItem; +import com.azure.storage.blob.models.DownloadRetryOptions; +import com.azure.storage.blob.options.BlobDownloadToFileOptions; import com.azure.storage.blob.options.BlobParallelUploadOptions; import com.microsoft.azure.functions.HttpStatus; import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptBlobClient; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.BlobStorageClientException; import it.gov.pagopa.receipt.pdf.helpdesk.model.response.BlobStorageResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; /** * Client for the Blob Storage */ public class ReceiptBlobClientImpl implements ReceiptBlobClient { + private final Logger logger = LoggerFactory.getLogger(ReceiptBlobClientImpl.class); private static ReceiptBlobClientImpl instance; - private final String containerName = System.getenv("BLOB_STORAGE_CONTAINER_NAME"); - private static final String FILE_EXTENSION = ".pdf"; - private final BlobServiceClient blobServiceClient; + private final int downloadTimeout = Integer.parseInt(System.getenv().getOrDefault("BLOB_STORAGE_DOWNLOAD_TIMEOUT", "10")); + private final int maxRetryDownload = Integer.parseInt(System.getenv().getOrDefault("BLOB_STORAGE_DOWNLOAD_MAX_RETRY", "5")); + private ReceiptBlobClientImpl() { String connectionString = System.getenv("RECEIPTS_STORAGE_CONN_STRING"); @@ -84,4 +101,75 @@ public BlobStorageResponse savePdfToBlobStorage(InputStream pdf, String fileName return blobStorageResponse; } + + /** + * Retrieve a PDF receipt from the blob storage + * + * @param fileName file name of the PDF receipt + * @return the file where the PDF receipt was stored + */ + public File getAttachmentFromBlobStorage(String fileName) throws BlobStorageClientException { + BlobContainerClient blobContainerClient = this.blobServiceClient.getBlobContainerClient(containerName); + BlobClient blobClient = blobContainerClient.getBlobClient(fileName); + String filePath = createTempDirectory(); + downloadAttachment(fileName, blobClient, filePath); + return new File(filePath); + } + + private String createTempDirectory() throws BlobStorageClientException { + try { + File workingDirectory = createWorkingDirectory(); + Path tempDirectory = Files.createTempDirectory(workingDirectory.toPath(), "receipt-pdf-service"); + return tempDirectory.toAbsolutePath() + "/receiptPdf.pdf"; + } catch (IOException e) { + logger.error("Error creating the temp directory to download the PDF receipt from Blob Storage"); + throw new BlobStorageClientException(org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); + } + } + + private void downloadAttachment(String fileName, BlobClient blobClient, String filePath) throws BlobStorageClientException { + try { + blobClient.downloadToFileWithResponse( + getBlobDownloadToFileOptions(filePath), + Duration.ofSeconds(downloadTimeout), + Context.NONE); + } catch (UncheckedIOException e) { + logger.error("I/O error downloading the PDF receipt from Blob Storage"); + throw new BlobStorageClientException(org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); + } catch (BlobStorageException e) { + String errMsg; + if (e.getStatusCode() == HttpStatus.NOT_FOUND.value()) { + errMsg = String.format("PDF receipt with name: %s not found in Blob Storage: %s", fileName, blobClient.getAccountName()); + logger.error(errMsg); + throw new BlobStorageClientException(e.getStatusCode(), errMsg, e); + } + errMsg = String.format("Unable to download the PDF receipt with name: %s from Blob Storage: %s. Error message from server: %s", + fileName, + blobClient.getAccountName(), + e.getServiceMessage() + ); + logger.error(errMsg); + throw new BlobStorageClientException(e.getStatusCode(), errMsg, e); + } + } + + private BlobDownloadToFileOptions getBlobDownloadToFileOptions(String filePath) { + return new BlobDownloadToFileOptions(filePath) + .setDownloadRetryOptions(new DownloadRetryOptions().setMaxRetryRequests(maxRetryDownload)) + .setOpenOptions(new HashSet<>( + Arrays.asList( + StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE, + StandardOpenOption.READ + )) + ); + } + + private File createWorkingDirectory() throws IOException { + File workingDirectory = new File("temp"); + if (!workingDirectory.exists()) { + Files.createDirectory(workingDirectory.toPath()); + } + return workingDirectory; + } } From c67cbcc323045a78edf7d1cecd90190922fa1933 Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 12:42:54 +0100 Subject: [PATCH 4/7] [PRDP-257] Updated blob client unit tests --- .../impl/ReceiptBlobClientImplTest.java | 127 ++++++++++++++++-- 1 file changed, 119 insertions(+), 8 deletions(-) diff --git a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImplTest.java b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImplTest.java index 752e067..5ac7128 100644 --- a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImplTest.java +++ b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptBlobClientImplTest.java @@ -1,25 +1,37 @@ package it.gov.pagopa.receipt.pdf.helpdesk.client.impl; +import com.azure.core.http.HttpResponse; import com.azure.core.http.rest.Response; +import com.azure.core.util.Context; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.models.BlobStorageException; +import com.azure.storage.blob.options.BlobDownloadToFileOptions; import com.microsoft.azure.functions.HttpStatus; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.BlobStorageClientException; import it.gov.pagopa.receipt.pdf.helpdesk.model.response.BlobStorageResponse; +import lombok.SneakyThrows; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.time.Duration; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doThrow; import static uk.org.webcompere.systemstubs.SystemStubs.withEnvironmentVariables; +@ExtendWith(MockitoExtension.class) class ReceiptBlobClientImplTest { @Test @@ -78,10 +90,6 @@ void runKo() throws IOException { when(mockClient.uploadWithResponse(any(), eq(null), eq(null))).thenReturn( mockBlockItem ); - String VALID_BLOB_NAME = "a valid blob name"; - String VALID_BLOB_URL = "a valid blob url"; - when(mockClient.getBlobName()).thenReturn(VALID_BLOB_NAME); - when(mockClient.getBlobUrl()).thenReturn(VALID_BLOB_URL); when(mockContainer.getBlobClient(any())).thenReturn(mockClient); @@ -97,4 +105,107 @@ void runKo() throws IOException { } + @Test + @SneakyThrows + void getAttachmentFromBlobStorageSuccess() { + BlobClient blobClientMock = mock(BlobClient.class); + doReturn(null).when(blobClientMock) + .downloadToFileWithResponse( + any(BlobDownloadToFileOptions.class), + any(Duration.class), + any(Context.class) + ); + + BlobServiceClient serviceClient = mock(BlobServiceClient.class); + BlobContainerClient containerClient = mock(BlobContainerClient.class); + when(containerClient.getBlobClient(any())).thenReturn(blobClientMock); + + when(serviceClient.getBlobContainerClient(any())).thenReturn(containerClient); + + ReceiptBlobClientImpl sut = new ReceiptBlobClientImpl(serviceClient); + + File result = sut.getAttachmentFromBlobStorage(anyString()); + + assertNotNull(result); + } + + @Test + @SneakyThrows + void getAttachmentFromBlobStorageFailDownloadThrowsUncheckedIOException() { + BlobClient blobClientMock = mock(BlobClient.class); + doThrow(UncheckedIOException.class).when(blobClientMock) + .downloadToFileWithResponse( + any(BlobDownloadToFileOptions.class), + any(Duration.class), + any(Context.class) + ); + + BlobServiceClient serviceClient = mock(BlobServiceClient.class); + BlobContainerClient containerClient = mock(BlobContainerClient.class); + when(containerClient.getBlobClient(any())).thenReturn(blobClientMock); + + when(serviceClient.getBlobContainerClient(any())).thenReturn(containerClient); + + ReceiptBlobClientImpl sut = new ReceiptBlobClientImpl(serviceClient); + + BlobStorageClientException e = assertThrows(BlobStorageClientException.class, () -> sut.getAttachmentFromBlobStorage(anyString())); + + assertNotNull(e); + assertEquals(org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getStatusCode()); + } + + @Test + @SneakyThrows + void getAttachmentFromBlobStorageFailDownloadAttachmentNotFound() { + BlobClient blobClientMock = mock(BlobClient.class); + HttpResponse responseMock = mock(HttpResponse.class); + doReturn(404).when(responseMock).getStatusCode(); + doThrow(new BlobStorageException("", responseMock, null)).when(blobClientMock) + .downloadToFileWithResponse( + any(BlobDownloadToFileOptions.class), + any(Duration.class), + any(Context.class) + ); + + BlobServiceClient serviceClient = mock(BlobServiceClient.class); + BlobContainerClient containerClient = mock(BlobContainerClient.class); + when(containerClient.getBlobClient(any())).thenReturn(blobClientMock); + + when(serviceClient.getBlobContainerClient(any())).thenReturn(containerClient); + + ReceiptBlobClientImpl sut = new ReceiptBlobClientImpl(serviceClient); + + BlobStorageClientException e = assertThrows(BlobStorageClientException.class, () -> sut.getAttachmentFromBlobStorage(anyString())); + + assertNotNull(e); + assertEquals(HttpStatus.NOT_FOUND.value(), e.getStatusCode()); + } + + @Test + @SneakyThrows + void getAttachmentFromBlobStorageFailDownloadThrowsBlobStorageException() { + BlobClient blobClientMock = mock(BlobClient.class); + HttpResponse responseMock = mock(HttpResponse.class); + doReturn(500).when(responseMock).getStatusCode(); + doThrow(new BlobStorageException("", responseMock, null)).when(blobClientMock) + .downloadToFileWithResponse( + any(BlobDownloadToFileOptions.class), + any(Duration.class), + any(Context.class) + ); + + BlobServiceClient serviceClient = mock(BlobServiceClient.class); + BlobContainerClient containerClient = mock(BlobContainerClient.class); + when(containerClient.getBlobClient(any())).thenReturn(blobClientMock); + + when(serviceClient.getBlobContainerClient(any())).thenReturn(containerClient); + + ReceiptBlobClientImpl sut = new ReceiptBlobClientImpl(serviceClient); + + BlobStorageClientException e = assertThrows(BlobStorageClientException.class, () -> sut.getAttachmentFromBlobStorage(anyString())); + + assertNotNull(e); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getStatusCode()); + } + } \ No newline at end of file From cb512b2b7f649ba255d6c7e3aa43e44d1fb30540 Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 13:04:08 +0100 Subject: [PATCH 5/7] [PRDP-257] Defined getReceiptPdf unit tests --- .../receipt/pdf/helpdesk/GetReceiptPdf.java | 9 +- .../pdf/helpdesk/GetReceiptPdfTest.java | 119 ++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdfTest.java diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java index 3fde8ab..ea29314 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdf.java @@ -58,7 +58,7 @@ public HttpResponseMessage run( if (fileName == null || fileName.isBlank()) { return request - .createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) + .createResponseBuilder(HttpStatus.BAD_REQUEST) .body(ProblemJson.builder() .title(HttpStatus.BAD_REQUEST.name()) .detail("Please pass a valid file name") @@ -79,7 +79,12 @@ public HttpResponseMessage run( } catch (BlobStorageClientException | IOException e) { String responseMsg = String.format("Unable to retrieve the receipt pdf with file name %s", fileName); logger.error("[{}] {}", context.getFunctionName(), responseMsg, e); - return request.createResponseBuilder(HttpStatus.NOT_FOUND).body(responseMsg).build(); + return request.createResponseBuilder(HttpStatus.NOT_FOUND) + .body(ProblemJson.builder() + .title(HttpStatus.NOT_FOUND.name()) + .detail(responseMsg) + .status(HttpStatus.NOT_FOUND.value()) + .build()).build(); } } } diff --git a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdfTest.java b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdfTest.java new file mode 100644 index 0000000..7b1c988 --- /dev/null +++ b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptPdfTest.java @@ -0,0 +1,119 @@ +package it.gov.pagopa.receipt.pdf.helpdesk; + +import com.microsoft.azure.functions.ExecutionContext; +import com.microsoft.azure.functions.HttpRequestMessage; +import com.microsoft.azure.functions.HttpResponseMessage; +import com.microsoft.azure.functions.HttpStatus; +import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptBlobClient; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.BlobStorageClientException; +import it.gov.pagopa.receipt.pdf.helpdesk.model.ProblemJson; +import it.gov.pagopa.receipt.pdf.helpdesk.util.HttpResponseMessageMock; +import lombok.SneakyThrows; +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doAnswer; + +class GetReceiptPdfTest { + private static final String FILE_NAME = "fileName"; + + @Mock + private ExecutionContext executionContextMock; + + @Mock + private ReceiptBlobClient receiptBlobClient; + + @Mock + private HttpRequestMessage> requestMock; + + private GetReceiptPdf sut; + + private AutoCloseable closeable; + + @BeforeEach + public void openMocks() { + closeable = MockitoAnnotations.openMocks(this); + sut = spy(new GetReceiptPdf(receiptBlobClient)); + } + + @AfterEach + public void releaseMocks() throws Exception { + closeable.close(); + } + + @Test + @SneakyThrows + void getReceiptPdfSuccess() { + File pdfFile = new File("src/test/resources/testFile.pdf"); + pdfFile.createNewFile(); + when(receiptBlobClient.getAttachmentFromBlobStorage(FILE_NAME)).thenReturn(pdfFile); + + doAnswer((Answer) invocation -> { + HttpStatus status = (HttpStatus) invocation.getArguments()[0]; + return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status); + }).when(requestMock).createResponseBuilder(any(HttpStatus.class)); + + // test execution + HttpResponseMessage response = sut.run(requestMock, FILE_NAME, executionContextMock); + + // test assertion + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatus()); + assertNotNull(response.getBody()); + + assertNotNull(response.getBody()); + } + + @Test + void getReceiptPdfMissingFileName() { + doAnswer((Answer) invocation -> { + HttpStatus status = (HttpStatus) invocation.getArguments()[0]; + return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status); + }).when(requestMock).createResponseBuilder(any(HttpStatus.class)); + + // test execution + HttpResponseMessage response = sut.run(requestMock, "", executionContextMock); + + // test assertion + assertNotNull(response); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatus()); + + ProblemJson problemJson = (ProblemJson) response.getBody(); + assertNotNull(problemJson); + assertEquals(HttpStatus.BAD_REQUEST.value(), problemJson.getStatus()); + assertEquals(HttpStatus.BAD_REQUEST.name(), problemJson.getTitle()); + assertNotNull(problemJson.getDetail()); + } + + @Test + @SneakyThrows + void getReceiptPdfBlobStorageException() { + when(receiptBlobClient.getAttachmentFromBlobStorage(FILE_NAME)).thenThrow(BlobStorageClientException.class); + + doAnswer((Answer) invocation -> { + HttpStatus status = (HttpStatus) invocation.getArguments()[0]; + return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status); + }).when(requestMock).createResponseBuilder(any(HttpStatus.class)); + + // test execution + HttpResponseMessage response = sut.run(requestMock, FILE_NAME, executionContextMock); + + // test assertion + assertNotNull(response); + assertEquals(HttpStatus.NOT_FOUND, response.getStatus()); + assertNotNull(response.getBody()); + } +} \ No newline at end of file From a248c9d978d6cc679e5abb3d3917283095eba7aa Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 13:11:37 +0100 Subject: [PATCH 6/7] [PRDP-257] Updated README with env valued --- README.md | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 0ad426e..8c0ca3d 100644 --- a/README.md +++ b/README.md @@ -79,31 +79,35 @@ On terminal type: then replace env variables with correct values (if there is NO default value, the variable HAS to be defined) -| VARIABLE | USAGE | DEFAULT VALUE | -|----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------:| -| `RECEIPT_QUEUE_CONN_STRING` | Connection string to the Receipt Queue | | -| `RECEIPT_QUEUE_TOPIC` | Topic name of the Receipt Queue | | -| `COSMOS_BIZ_EVENT_CONN_STRING` | Connection string to the BizEvent CosmosDB | | -| `COSMOS_BIZ_EVENT_SERVICE_ENDPOINT` | Endpoint to the BizEvent CosmosDB | | -| `COSMOS_BIZ_EVENT_DB_NAME` | Database name of the BizEvent database in CosmosDB | | -| `COSMOS_BIZ_EVENT_CONTAINER_NAME` | Container name of the BizEvent container in CosmosDB | | -| `COSMOS_RECEIPTS_CONN_STRING` | Connection string to the Receipt CosmosDB | | -| `COSMOS_RECEIPT_SERVICE_ENDPOINT` | Endpoint to the Receipt CosmosDB | | -| `COSMOS_RECEIPT_KEY` | Key to the Receipt CosmosDB | | -| `COSMOS_RECEIPT_DB_NAME` | Database name of the Receipt database in CosmosDB | | -| `COSMOS_RECEIPT_CONTAINER_NAME` | Container name of the Receipt container in CosmosDB | | -| `COSMOS_RECEIPT_ERROR_CONTAINER_NAME` | Container name of the Receipt-message-error container in CosmosDB | | -| `PDV_TOKENIZER_BASE_PATH` | PDV Tokenizer API base path | "https://api.uat.tokenizer.pdv.pagopa.it/tokenizer/v1" | -| `PDV_TOKENIZER_SEARCH_TOKEN_ENDPOINT` | PDV Tokenizer API search token endpoint | "/tokens/search" | -| `PDV_TOKENIZER_FIND_PII_ENDPOINT` | PDV Tokenizer API find pii endpoint | "/tokens/%s/pii" | -| `PDV_TOKENIZER_CREATE_TOKEN_ENDPOINT` | PDV Tokenizer API create token endpoint | "/tokens" | -| `PDV_TOKENIZER_SUBSCRIPTION_KEY` | API azure ocp apim subscription key | | -| `PDV_TOKENIZER_INITIAL_INTERVAL` | PDV Tokenizer initial interval for retry a request that fail with 429 status code | 200 | -| `PDV_TOKENIZER_MULTIPLIER` | PDV Tokenizer interval multiplier for subsequent request retry | 2.0 | -| `PDV_TOKENIZER_RANDOMIZATION_FACTOR` | PDV Tokenizer randomization factor for interval retry calculation | 0.6 | -| `PDV_TOKENIZER_MAX_RETRIES` | PDV Tokenizer max request retry | 3 | -| `TOKENIZER_APIM_HEADER_KEY` | Tokenizer APIM header key | x-api-key | -| `MAX_DATE_DIFF_MILLIS` | Difference in millis between the current time and the date from witch the
receipts will be fetched in massive recover operation | 360000 | +| VARIABLE | USAGE | DEFAULT VALUE | +|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------:| +| `RECEIPTS_STORAGE_CONN_STRING` | Connection string to the Receipt Queue | | +| `RECEIPT_QUEUE_TOPIC` | Topic name of the Receipt Queue | | +| `COSMOS_BIZ_EVENT_CONN_STRING` | Connection string to the BizEvent CosmosDB | | +| `COSMOS_BIZ_EVENT_SERVICE_ENDPOINT` | Endpoint to the BizEvent CosmosDB | | +| `COSMOS_BIZ_EVENT_DB_NAME` | Database name of the BizEvent database in CosmosDB | | +| `COSMOS_BIZ_EVENT_CONTAINER_NAME` | Container name of the BizEvent container in CosmosDB | | +| `COSMOS_RECEIPTS_CONN_STRING` | Connection string to the Receipt CosmosDB | | +| `COSMOS_RECEIPT_SERVICE_ENDPOINT` | Endpoint to the Receipt CosmosDB | | +| `COSMOS_RECEIPT_KEY` | Key to the Receipt CosmosDB | | +| `COSMOS_RECEIPT_DB_NAME` | Database name of the Receipt database in CosmosDB | | +| `COSMOS_RECEIPT_CONTAINER_NAME` | Container name of the Receipt container in CosmosDB | | +| `COSMOS_RECEIPT_ERROR_CONTAINER_NAME` | Container name of the Receipt-message-error container in CosmosDB | | +| `BLOB_STORAGE_ACCOUNT_ENDPOINT` | Endpoint to the Receipt Blob Storage | | +| `BLOB_STORAGE_CONTAINER_NAME` | Container name of the Blob Storage containing the pdf attachments | | +| `BLOB_STORAGE_DOWNLOAD_TIMEOUT` | Timeout for the call to retrieve the attachment from the blob storage | 10 | +| `BLOB_STORAGE_DOWNLOAD_MAX_RETRY` | Max number of retry for the call to retrieve the attachment from the blob storage | 5 | +| `PDV_TOKENIZER_BASE_PATH` | PDV Tokenizer API base path | "https://api.uat.tokenizer.pdv.pagopa.it/tokenizer/v1" | +| `PDV_TOKENIZER_SEARCH_TOKEN_ENDPOINT` | PDV Tokenizer API search token endpoint | "/tokens/search" | +| `PDV_TOKENIZER_FIND_PII_ENDPOINT` | PDV Tokenizer API find pii endpoint | "/tokens/%s/pii" | +| `PDV_TOKENIZER_CREATE_TOKEN_ENDPOINT` | PDV Tokenizer API create token endpoint | "/tokens" | +| `PDV_TOKENIZER_SUBSCRIPTION_KEY` | API azure ocp apim subscription key | | +| `PDV_TOKENIZER_INITIAL_INTERVAL` | PDV Tokenizer initial interval for retry a request that fail with 429 status code | 200 | +| `PDV_TOKENIZER_MULTIPLIER` | PDV Tokenizer interval multiplier for subsequent request retry | 2.0 | +| `PDV_TOKENIZER_RANDOMIZATION_FACTOR` | PDV Tokenizer randomization factor for interval retry calculation | 0.6 | +| `PDV_TOKENIZER_MAX_RETRIES` | PDV Tokenizer max request retry | 3 | +| `TOKENIZER_APIM_HEADER_KEY` | Tokenizer APIM header key | x-api-key | +| `MAX_DATE_DIFF_MILLIS` | Difference in millis between the current time and the date from witch the
receipts will be fetched in massive recover operation | 360000 | > to doc details about AZ fn config > see [here](https://stackoverflow.com/questions/62669672/azure-functions-what-is-the-purpose-of-having-host-json-and-local-settings-jso) From 566a6ef1526ad6b4513e5cdf23c72bcc9a0a8e56 Mon Sep 17 00:00:00 2001 From: svariant Date: Mon, 4 Dec 2023 13:12:22 +0100 Subject: [PATCH 7/7] [PRDP-257] Removed reduntant env --- .env.example | 2 +- helm/values-dev.yaml | 1 - helm/values-prod.yaml | 1 - helm/values-uat.yaml | 1 - .../pdf/helpdesk/client/impl/ReceiptQueueClientImpl.java | 2 +- 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 10c8ed7..acb6d4f 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ FUNCTIONS_WORKER_RUNTIME=java AzureWebJobsStorage=DefaultEndpointsProtocol=https;AccountName= -RECEIPT_QUEUE_CONN_STRING=DefaultEndpointsProtocol=https;AccountName= +RECEIPTS_STORAGE_CONN_STRING=DefaultEndpointsProtocol=https;AccountName= RECEIPT_QUEUE_TOPIC=pagopa-d-weu-receipts-queue-receipt-waiting-4-gen RECEIPT_QUEUE_DELAY=1 diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 24bed4c..b930e79 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -115,7 +115,6 @@ microservice-chart: envSecret: APPLICATIONINSIGHTS_CONNECTION_STRING: "ai-d-connection-string" COSMOS_RECEIPTS_CONN_STRING: "cosmos-receipt-connection-string" - RECEIPT_QUEUE_CONN_STRING: "receipts-storage-account-connection-string", RECEIPTS_STORAGE_CONN_STRING: "receipts-storage-account-connection-string", COSMOS_BIZ_EVENT_CONN_STRING: "cosmos-biz-event-d-connection-string" COSMOS_RECEIPT_KEY: "cosmos-receipt-pkey" diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 4f50ee0..4a3d733 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -116,7 +116,6 @@ microservice-chart: APPLICATIONINSIGHTS_CONNECTION_STRING: "ai-p-connection-string" COSMOS_RECEIPTS_CONN_STRING: "cosmos-receipt-connection-string" RECEIPTS_STORAGE_CONN_STRING: "receipts-storage-account-connection-string", - RECEIPT_QUEUE_CONN_STRING: "receipts-storage-account-connection-string" COSMOS_BIZ_EVENT_CONN_STRING: "cosmos-biz-event-p-connection-string" COSMOS_RECEIPT_KEY: "cosmos-receipt-pkey" COSMOS_BIZ_EVENT_KEY: "cosmos-bizevent-pkey" diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 1a51c5b..07b1a29 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -116,7 +116,6 @@ microservice-chart: APPLICATIONINSIGHTS_CONNECTION_STRING: "ai-u-connection-string" COSMOS_RECEIPTS_CONN_STRING: "cosmos-receipt-connection-string" RECEIPTS_STORAGE_CONN_STRING: "receipts-storage-account-connection-string", - RECEIPT_QUEUE_CONN_STRING: "receipts-storage-account-connection-string" COSMOS_BIZ_EVENT_CONN_STRING: "cosmos-biz-event-u-connection-string" COSMOS_RECEIPT_KEY: "cosmos-receipt-pkey" COSMOS_BIZ_EVENT_KEY: "cosmos-bizevent-pkey" diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptQueueClientImpl.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptQueueClientImpl.java index eef300c..2094d69 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptQueueClientImpl.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptQueueClientImpl.java @@ -21,7 +21,7 @@ public class ReceiptQueueClientImpl implements ReceiptQueueClient { private final QueueClient queueClient; private ReceiptQueueClientImpl() { - String receiptQueueConnString = System.getenv("RECEIPT_QUEUE_CONN_STRING"); + String receiptQueueConnString = System.getenv("RECEIPTS_STORAGE_CONN_STRING"); String receiptQueueTopic = System.getenv("RECEIPT_QUEUE_TOPIC"); this.queueClient = new QueueClientBuilder()