diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index f57de18..aef08a3 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -89,6 +89,7 @@ microservice-chart: COSMOS_BIZ_EVENT_DB_NAME: "db" COSMOS_RECEIPT_CONTAINER_NAME: "receipts" COSMOS_RECEIPT_ERROR_CONTAINER_NAME: "receipts-message-errors" + COSMOS_RECEIPT_MESSAGE_CONTAINER_NAME: "receipts-io-messages" COSMOS_BIZ_EVENT_CONTAINER_NAME: "biz-events" PDF_ENGINE_ENDPOINT: "https://api.dev.platform.pagopa.it/shared/pdf-engine/v1/generate-pdf" BLOB_STORAGE_ACCOUNT_ENDPOINT: "https://pagopadweureceiptsfnsa.blob.core.windows.net/" diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 9abb4a4..6c36bae 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -89,6 +89,7 @@ microservice-chart: COSMOS_BIZ_EVENT_DB_NAME: "db" COSMOS_RECEIPT_CONTAINER_NAME: "receipts" COSMOS_RECEIPT_ERROR_CONTAINER_NAME: "receipts-message-errors" + COSMOS_RECEIPT_MESSAGE_CONTAINER_NAME: "receipts-io-messages" COSMOS_BIZ_EVENT_CONTAINER_NAME: "biz-events" PDF_ENGINE_ENDPOINT: "https://api.platform.pagopa.it/shared/pdf-engine/v1/generate-pdf" BLOB_STORAGE_ACCOUNT_ENDPOINT: "https://pagopapweureceiptsfnsa.blob.core.windows.net" diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index 715c9c6..bca76a5 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -89,6 +89,7 @@ microservice-chart: COSMOS_BIZ_EVENT_DB_NAME: "db" COSMOS_RECEIPT_CONTAINER_NAME: "receipts" COSMOS_RECEIPT_ERROR_CONTAINER_NAME: "receipts-message-errors" + COSMOS_RECEIPT_MESSAGE_CONTAINER_NAME: "receipts-io-messages" COSMOS_BIZ_EVENT_CONTAINER_NAME: "biz-events" PDF_ENGINE_ENDPOINT: "https://api.uat.platform.pagopa.it/shared/pdf-engine/v1/generate-pdf" BLOB_STORAGE_ACCOUNT_ENDPOINT: "https://pagopauweureceiptsfnsa.blob.core.windows.net" diff --git a/integration-test/src/features/receipt_pdf_helpdesk.feature b/integration-test/src/features/receipt_pdf_helpdesk.feature index 51b64e4..0744c48 100644 --- a/integration-test/src/features/receipt_pdf_helpdesk.feature +++ b/integration-test/src/features/receipt_pdf_helpdesk.feature @@ -25,6 +25,13 @@ Feature: All about payment events to recover managed by Azure functions receipt- When getReceiptPdf API is called with filename "int-test-helpdesk-receipt.pdf" Then the api response has a 200 Http status + Scenario: getReceiptMessage API return receipt-error stored on datastore + Given a receipt-io-message with bizEventId "receipt-helpdesk-int-test-id-3" and messageId "receipt-helpdesk-int-test-message-id-3" stored into receipt-io-message datastore + When getReceiptMessage API is called with messageId "receipt-helpdesk-int-test-id-3" + Then the api response has a 200 Http status + And the receipt-message has messageId "receipt-helpdesk-int-test-message-id-3" + And the receipt-message has eventId "receipt-helpdesk-int-test-id-3" + Scenario: receiptToReviewed API retrieve a receipt error and updates its status to REVIEWED Given a receipt-error with bizEventId "receipt-helpdesk-int-test-id-5" and status "TO_REVIEW" stored into receipt-error datastore When receiptToReviewed API is called with bizEventId "receipt-helpdesk-int-test-id-5" diff --git a/integration-test/src/script/teardown_script.js b/integration-test/src/script/teardown_script.js index 2605205..d7e33a6 100644 --- a/integration-test/src/script/teardown_script.js +++ b/integration-test/src/script/teardown_script.js @@ -7,10 +7,12 @@ const cosmos_db_conn_string = process.env.RECEIPTS_COSMOS_CONN_STRING || ""; const databaseId = process.env.RECEIPT_COSMOS_DB_NAME; const receiptContainerId = process.env.RECEIPT_COSMOS_DB_CONTAINER_NAME; const receiptErrorContainerId = process.env.RECEIPT_ERROR_COSMOS_DB_CONTAINER_NAME; +const receiptMessageContainerId = process.env.RECEIPT_MESSAGE_COSMOS_DB_CONTAINER_NAME; const client = new CosmosClient(cosmos_db_conn_string); const receiptContainer = client.database(databaseId).container(receiptContainerId); const receiptErrorContainer = client.database(databaseId).container(receiptErrorContainerId); +const receiptMessageContainer = client.database(databaseId).container(receiptMessageContainerId); //COSMOS BIZEVENT const biz_cosmos_db_conn_string = process.env.BIZEVENTS_COSMOS_CONN_STRING; @@ -82,6 +84,24 @@ const deleteDocumentFromAllDatabases = async () => { } } + //Delete Receipt message from CosmosDB + try { + let response = await receiptMessageContainer.items.query({ + query: "SELECT * from c WHERE c.eventId = @eventId", + parameters: [{ name: "@eventId", value: el.eventId }] + }).fetchAll(); + + let resourcesMessage = response.resources; + + for (let message of resourcesMessage) { + await receiptMessageContainer.item(message.id, message.id).delete(); + } + } catch (error) { + if (error.code !== 404) { + console.error(`Error deleting receipt message ${el.eventId}`); + } + } + //Delete BizEvent from CosmosDB try { await bizContainer.item(el.eventId, el.eventId).delete(); diff --git a/integration-test/src/step_definitions/common.js b/integration-test/src/step_definitions/common.js index 15dddef..f068f58 100644 --- a/integration-test/src/step_definitions/common.js +++ b/integration-test/src/step_definitions/common.js @@ -175,11 +175,19 @@ function createReceiptError(id, status) { } } +function createReceiptMessage(eventId, messageId) { + return { + "messageId": messageId, + "eventId": eventId + } +} + module.exports = { TOKENIZED_FISCAL_CODE, createEvent, sleep, createReceipt, createReceiptError, + createReceiptMessage, getRandomInt } \ No newline at end of file diff --git a/integration-test/src/step_definitions/receipt_pdf_helpdesk_step.js b/integration-test/src/step_definitions/receipt_pdf_helpdesk_step.js index 5ea84c1..277d943 100644 --- a/integration-test/src/step_definitions/receipt_pdf_helpdesk_step.js +++ b/integration-test/src/step_definitions/receipt_pdf_helpdesk_step.js @@ -11,12 +11,15 @@ const { deleteDocumentFromReceiptErrorDatastore, getDocumentFromReceiptsErrorDatastoreByBizEventId, getDocumentFromReceiptsDatastoreByEventId, - deleteMultipleDocumentFromReceiptErrorDatastoreByEventId + deleteMultipleDocumentFromReceiptErrorDatastoreByEventId, + deleteDocumentFromReceiptMessageDatastore, + createDocumentInReceiptIoMessageDatastore } = require("./receipts_datastore_client"); const { getReceipt, getReceiptByOrganizationFiscalCodeAndIUV, getReceiptError, + getReceiptMessage, getReceiptPdf, postReceiptToReviewed, postRecoverFailedReceipt, @@ -32,9 +35,11 @@ setDefaultTimeout(360 * 1000); // initialize variables let eventId = null; +let messageId = null; let responseAPI = null; let receipt = null; let receiptError = null; +let receiptMessage = null; let receiptPdfFileName = null; let listOfReceipts = []; @@ -243,6 +248,28 @@ Then("wait {int} ms", async function (milliSec) { sleep(milliSec) }); +Given('a receipt-io-message with bizEventId {string} and status {string} stored into receipt-error datastore', async function (eventId, messageId) { + messageId = messageId; + // prior cancellation to avoid dirty cases + await deleteDocumentFromReceiptMessageDatastore(messageId); + + let receiptsMessageStoreResponse = await createDocumentInReceiptIoMessageDatastore(eventId, messageId); + assert.strictEqual(receiptsMessageStoreResponse.statusCode, 201); +}); + +When("getReceiptMessage API is called with messageId {string}", async function (id) { + responseAPI = await getReceiptMessage(id); + receiptMessage = responseAPI.data; +}); + +Then("the receipt-message has eventId {string}", async function (id) { + assert.strictEqual(receiptMessage.eventId, id); +}); + +Then("the receipt-message has messageId {string}", async function (id) { + assert.strictEqual(receiptMessage.messageId, id); +}); + diff --git a/integration-test/src/step_definitions/receipts_datastore_client.js b/integration-test/src/step_definitions/receipts_datastore_client.js index 74a15f8..b5ad050 100644 --- a/integration-test/src/step_definitions/receipts_datastore_client.js +++ b/integration-test/src/step_definitions/receipts_datastore_client.js @@ -1,14 +1,16 @@ const { CosmosClient } = require("@azure/cosmos"); -const { createReceipt, createReceiptError } = require("./common"); +const { createReceipt, createReceiptError, createReceiptMessage } = require("./common"); const cosmos_db_conn_string = process.env.RECEIPTS_COSMOS_CONN_STRING || ""; const databaseId = process.env.RECEIPT_COSMOS_DB_NAME; const receiptContainerId = process.env.RECEIPT_COSMOS_DB_CONTAINER_NAME; const receiptErrorContainerId = process.env.RECEIPT_ERROR_COSMOS_DB_CONTAINER_NAME; +const receiptMessageContainerId = process.env.RECEIPT_MESSAGE_COSMOS_DB_CONTAINER_NAME; const client = new CosmosClient(cosmos_db_conn_string); const receiptContainer = client.database(databaseId).container(receiptContainerId); const receiptErrorContainer = client.database(databaseId).container(receiptErrorContainerId); +const receiptMessageContainer = client.database(databaseId).container(receiptMessageContainerId); //RECEIPT async function createDocumentInReceiptsDatastore(id, status) { @@ -57,6 +59,16 @@ async function createDocumentInReceiptErrorDatastore(id, status) { } } +//RECEIPT-MESSAGE +async function createDocumentInReceiptIoMessageDatastore(eventId, messageId) { + let message = createReceiptMessage(eventId, messageId); + try { + return await receiptMessageContainer.items.create(receipt); + } catch (err) { + console.log(err); + } +} + async function getDocumentFromReceiptsErrorDatastoreByBizEventId(id) { return await receiptErrorContainer.items .query({ @@ -84,6 +96,16 @@ async function deleteDocumentFromReceiptErrorDatastore(id) { } } +async function deleteDocumentFromReceiptMessageDatastore(id) { + try { + return await receiptMessageContainer.item(id, id).delete(); + } catch (error) { + if (error.code !== 404) { + console.log(error) + } + } +} + async function updateReceiptToFailed(id) { const operations = @@ -131,6 +153,21 @@ async function deleteAllTestReceiptsError() { } } +async function deleteAllTestReceiptsMessage() { + let response = await receiptErrorContainer.items.query({ + query: 'SELECT * from c WHERE c.messageId LIKE @messageId', + parameters: [{ name: "@messageId", value: "%receipt-helpdesk-int-test-message%" }] + }).fetchNext(); + + let receiptErrorList = response.resources; + if (receiptErrorList.length > 0) { + receiptErrorList.forEach((receiptMessage) => { + console.log("\n Deleting receiptMessage with id " + receiptMessage.messageId); + deleteDocumentFromReceiptMessageDatastore(receiptMessage.id); + }) + } +} + module.exports = { createDocumentInReceiptsDatastore, getDocumentFromReceiptsDatastoreByEventId, @@ -138,8 +175,11 @@ module.exports = { deleteDocumentFromReceiptsDatastore, updateReceiptToFailed, deleteAllTestReceipts, + deleteAllTestReceiptsMessage, + createDocumentInReceiptIoMessageDatastore, deleteDocumentFromReceiptErrorDatastore, + deleteDocumentFromReceiptMessageDatastore, deleteMultipleDocumentFromReceiptErrorDatastoreByEventId, createDocumentInReceiptErrorDatastore, getDocumentFromReceiptsErrorDatastoreByBizEventId, diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptMessage.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptMessage.java new file mode 100644 index 0000000..294ba3b --- /dev/null +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptMessage.java @@ -0,0 +1,87 @@ +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.entity.receipt.IOMessage; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; +import it.gov.pagopa.receipt.pdf.helpdesk.model.ProblemJson; +import it.gov.pagopa.receipt.pdf.helpdesk.service.ReceiptCosmosService; +import it.gov.pagopa.receipt.pdf.helpdesk.service.impl.ReceiptCosmosServiceImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.LocalDateTime; +import java.util.Optional; + +/** + * Azure Functions with HTTP Trigger. + */ +public class GetReceiptMessage { + + private final Logger logger = LoggerFactory.getLogger(GetReceiptMessage.class); + + private final ReceiptCosmosService receiptCosmosService; + + public GetReceiptMessage() { + this.receiptCosmosService = new ReceiptCosmosServiceImpl(); + } + + GetReceiptMessage(ReceiptCosmosService receiptCosmosService) { + this.receiptCosmosService = receiptCosmosService; + } + + /** + * This function will be invoked when a Http Trigger occurs. + *

+ * It retrieves the receipt-message with the specified messageId + *

+ * + * @return response with {@link HttpStatus#OK} and the receipt notification message if found + */ + @FunctionName("GetReceiptMessage") + public HttpResponseMessage run( + @HttpTrigger(name = "GetReceiptMessageTrigger", + methods = {HttpMethod.GET}, + route = "/receipts/io-message/{message-id}", + authLevel = AuthorizationLevel.ANONYMOUS) + HttpRequestMessage> request, + @BindingName("message-id") String messageId, + final ExecutionContext context) { + logger.info("[{}] function called at {}", context.getFunctionName(), LocalDateTime.now()); + + if (messageId == null || messageId.isBlank()) { + return request + .createResponseBuilder(HttpStatus.BAD_REQUEST) + .body(ProblemJson.builder() + .title(HttpStatus.BAD_REQUEST.name()) + .detail("Please pass a valid messageId") + .status(HttpStatus.BAD_REQUEST.value()) + .build()) + .build(); + } + + try { + IOMessage receipt = this.receiptCosmosService.getReceiptMessage(messageId); + return request + .createResponseBuilder(HttpStatus.OK) + .body(receipt) + .build(); + } catch (IoMessageNotFoundException e) { + String responseMsg = String.format("Unable to retrieve the receipt message with messageId %s", messageId); + logger.error("[{}] {}", context.getFunctionName(), responseMsg, e); + 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/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptCosmosClient.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptCosmosClient.java index 7d12f55..da07eec 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptCosmosClient.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/ReceiptCosmosClient.java @@ -2,9 +2,11 @@ import com.azure.cosmos.models.CosmosItemResponse; import com.azure.cosmos.models.FeedResponse; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.IOMessage; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.ReceiptError; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptStatusType; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; public interface ReceiptCosmosClient { @@ -53,4 +55,6 @@ public interface ReceiptCosmosClient { * @return receipt documents */ Iterable> getIOErrorToNotifyReceiptDocuments(String continuationToken, Integer pageSize); + + IOMessage getIoMessage(String messageId) throws IoMessageNotFoundException; } diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImpl.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImpl.java index 527ba7d..69a0bf6 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImpl.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImpl.java @@ -9,10 +9,12 @@ import com.azure.cosmos.models.FeedResponse; import com.azure.cosmos.util.CosmosPagedIterable; import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptCosmosClient; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.IOMessage; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.ReceiptError; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptErrorStatusType; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptStatusType; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; import java.time.OffsetDateTime; @@ -28,6 +30,8 @@ public class ReceiptCosmosClientImpl implements ReceiptCosmosClient { private final String databaseId = System.getenv().getOrDefault("COSMOS_RECEIPT_DB_NAME", "db"); private final String containerId = System.getenv().getOrDefault("COSMOS_RECEIPT_CONTAINER_NAME", "receipt"); + private final String containerMessageId = System.getenv().getOrDefault("COSMOS_RECEIPT_MESSAGE_CONTAINER_NAME", "receipts-io-messages"); + private final String containerReceiptErrorId = System.getenv().getOrDefault("COSMOS_RECEIPT_ERROR_CONTAINER_NAME", "receipts-message-errors"); private final String millisDiff = System.getenv("MAX_DATE_DIFF_MILLIS"); @@ -233,4 +237,29 @@ public Iterable> getIOErrorToNotifyReceiptDocuments(String .queryItems(query, new CosmosQueryRequestOptions(), Receipt.class) .iterableByPage(continuationToken,pageSize); } + + /** + * Retrieve receipt document from CosmosDB database + * + * @param messageId IO Message id + * @return io message document + * @throws IoMessageNotFoundException in case no receipt has been found with the given messageId + */ + @Override + public IOMessage getIoMessage(String messageId) throws IoMessageNotFoundException { + CosmosDatabase cosmosDatabase = this.cosmosClient.getDatabase(databaseId); + CosmosContainer cosmosContainer = cosmosDatabase.getContainer(containerMessageId); + + //Build query + String query = String.format("SELECT * FROM c WHERE c.messageId = '%s'", messageId); + + //Query the container + CosmosPagedIterable queryResponse = cosmosContainer + .queryItems(query, new CosmosQueryRequestOptions(), IOMessage.class); + + if (queryResponse.iterator().hasNext()) { + return queryResponse.iterator().next(); + } + throw new IoMessageNotFoundException("Document not found in the defined container"); + } } diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/entity/receipt/IOMessage.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/entity/receipt/IOMessage.java new file mode 100644 index 0000000..a0f775e --- /dev/null +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/entity/receipt/IOMessage.java @@ -0,0 +1,14 @@ +package it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class IOMessage { + + String messageId; + String eventId; +} diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/IoMessageNotFoundException.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/IoMessageNotFoundException.java new file mode 100644 index 0000000..8c3ede9 --- /dev/null +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/exception/IoMessageNotFoundException.java @@ -0,0 +1,26 @@ +package it.gov.pagopa.receipt.pdf.helpdesk.exception; + +/** Thrown in case no receipt message to IO is found in the CosmosDB container */ +public class IoMessageNotFoundException extends Exception{ + + /** + * Constructs new exception with provided message and cause + * + * @param message Detail message + */ + public IoMessageNotFoundException(String message) { + super(message); + } + + /** + * Constructs new exception with provided message and cause + * + * @param message Detail message + * @param cause Exception thrown + */ + public IoMessageNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} + + diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/ReceiptCosmosService.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/ReceiptCosmosService.java index 3d37a86..2fd4840 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/ReceiptCosmosService.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/ReceiptCosmosService.java @@ -2,8 +2,10 @@ import com.azure.cosmos.models.FeedResponse; import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptCosmosClient; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.IOMessage; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptStatusType; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; /** @@ -47,4 +49,11 @@ Iterable> getFailedReceiptByStatus( Integer pageSize, ReceiptStatusType statusType ); + + /** + * + * @param messageId + * @return + */ + IOMessage getReceiptMessage(String messageId) throws IoMessageNotFoundException; } diff --git a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImpl.java b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImpl.java index 7778b9c..016b588 100644 --- a/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImpl.java +++ b/src/main/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImpl.java @@ -3,8 +3,10 @@ import com.azure.cosmos.models.FeedResponse; import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptCosmosClient; import it.gov.pagopa.receipt.pdf.helpdesk.client.impl.ReceiptCosmosClientImpl; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.IOMessage; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptStatusType; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.service.ReceiptCosmosService; @@ -83,4 +85,21 @@ public Iterable> getFailedReceiptByStatus( String errMsg = String.format("Unexpected status for retrieving failed receipt: %s", statusType); throw new IllegalStateException(errMsg); } + + @Override + public IOMessage getReceiptMessage(String messageId) throws IoMessageNotFoundException { + IOMessage message; + try { + message = this.receiptCosmosClient.getIoMessage(messageId); + } catch (IoMessageNotFoundException e) { + String errorMsg = String.format("Receipt Message to IO not found with the message id %s", messageId); + throw new IoMessageNotFoundException(errorMsg, e); + } + + if (message == null) { + String errorMsg = String.format("Receipt retrieved with the message id %s is null", messageId); + throw new IoMessageNotFoundException(errorMsg); + } + return message; + } } \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptMessageTest.java b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptMessageTest.java new file mode 100644 index 0000000..e564c54 --- /dev/null +++ b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/GetReceiptMessageTest.java @@ -0,0 +1,125 @@ +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.entity.receipt.IOMessage; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.ReasonError; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptStatusType; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; +import it.gov.pagopa.receipt.pdf.helpdesk.model.ProblemJson; +import it.gov.pagopa.receipt.pdf.helpdesk.service.ReceiptCosmosService; +import it.gov.pagopa.receipt.pdf.helpdesk.util.HttpResponseMessageMock; +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.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class GetReceiptMessageTest { + + private static final String MESSAGE_ID = "messageId"; + + private static final String EVENT_ID = "eventId"; + + @Mock + private ExecutionContext executionContextMock; + + @Mock + private ReceiptCosmosService receiptCosmosServiceMock; + + @Mock + private HttpRequestMessage> requestMock; + + private GetReceiptMessage sut; + + private AutoCloseable closeable; + + @BeforeEach + public void openMocks() { + closeable = MockitoAnnotations.openMocks(this); + sut = spy(new GetReceiptMessage(receiptCosmosServiceMock)); + } + + @AfterEach + public void releaseMocks() throws Exception { + closeable.close(); + } + + @Test + void getReceiptReceiptSuccess() throws IoMessageNotFoundException { + IOMessage receipt = buildMessage(); + when(receiptCosmosServiceMock.getReceiptMessage(MESSAGE_ID)).thenReturn(receipt); + + 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, MESSAGE_ID, executionContextMock); + + // test assertion + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatus()); + assertNotNull(response.getBody()); + assertEquals(receipt, response.getBody()); + } + + @Test + void getReceiptForMissingEventId() { + 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 + void getReceiptReceiptNotFound() throws IoMessageNotFoundException { + when(receiptCosmosServiceMock.getReceiptMessage(MESSAGE_ID)).thenThrow(IoMessageNotFoundException.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, MESSAGE_ID, executionContextMock); + + // test assertion + assertNotNull(response); + assertEquals(HttpStatus.NOT_FOUND, response.getStatus()); + assertNotNull(response.getBody()); + } + + private IOMessage buildMessage() { + return IOMessage.builder() + .messageId(MESSAGE_ID) + .eventId(EVENT_ID) + .build(); + } +} \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImplTest.java b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImplTest.java index 803d046..7b637ef 100644 --- a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImplTest.java +++ b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/client/impl/ReceiptCosmosClientImplTest.java @@ -4,7 +4,9 @@ import com.azure.cosmos.CosmosContainer; import com.azure.cosmos.CosmosDatabase; import com.azure.cosmos.util.CosmosPagedIterable; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.IOMessage; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -188,4 +190,57 @@ void getIOErrorToNotifyReceiptDocumentsSuccess() { assertDoesNotThrow(() -> client.getIOErrorToNotifyReceiptDocuments(null, 100)); } + + @Test + void getIoMessageReceiptDocumentSuccess() { + CosmosDatabase mockDatabase = mock(CosmosDatabase.class); + CosmosContainer mockContainer = mock(CosmosContainer.class); + + CosmosPagedIterable mockIterable = mock(CosmosPagedIterable.class); + + Iterator mockIterator = mock(Iterator.class); + IOMessage ioMessage = new IOMessage(); + ioMessage.setEventId(RECEIPT_ID); + ioMessage.setMessageId("MESSAGE_ID"); + + when(mockIterator.hasNext()).thenReturn(true); + when(mockIterator.next()).thenReturn(ioMessage); + + when(mockIterable.iterator()).thenReturn(mockIterator); + + when(mockContainer.queryItems(anyString(), any(), eq(IOMessage.class))).thenReturn( + mockIterable + ); + when(mockDatabase.getContainer(any())).thenReturn(mockContainer); + when(mockClient.getDatabase(any())).thenReturn(mockDatabase); + + IOMessage response = assertDoesNotThrow(() -> client.getIoMessage("MESSAGE_ID")); + + assertEquals("MESSAGE_ID", response.getMessageId()); + assertEquals(RECEIPT_ID, response.getEventId()); + + } + + @Test + void getIoMessageReceiptDocumentFail() { + CosmosDatabase mockDatabase = mock(CosmosDatabase.class); + CosmosContainer mockContainer = mock(CosmosContainer.class); + + CosmosPagedIterable mockIterable = mock(CosmosPagedIterable.class); + + Iterator mockIterator = mock(Iterator.class); + + when(mockIterator.hasNext()).thenReturn(false); + + when(mockIterable.iterator()).thenReturn(mockIterator); + + when(mockContainer.queryItems(anyString(), any(), eq(IOMessage.class))).thenReturn( + mockIterable + ); + when(mockDatabase.getContainer(any())).thenReturn(mockContainer); + when(mockClient.getDatabase(any())).thenReturn(mockDatabase); + + assertThrows(IoMessageNotFoundException.class, () -> client.getIoMessage("an invalid receipt id")); + } + } \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImplTest.java b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImplTest.java index 7a266c9..2dcbcec 100644 --- a/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImplTest.java +++ b/src/test/java/it/gov/pagopa/receipt/pdf/helpdesk/service/impl/ReceiptCosmosServiceImplTest.java @@ -2,8 +2,10 @@ import com.azure.cosmos.models.FeedResponse; import it.gov.pagopa.receipt.pdf.helpdesk.client.ReceiptCosmosClient; +import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.IOMessage; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.Receipt; import it.gov.pagopa.receipt.pdf.helpdesk.entity.receipt.enumeration.ReceiptStatusType; +import it.gov.pagopa.receipt.pdf.helpdesk.exception.IoMessageNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.exception.ReceiptNotFoundException; import it.gov.pagopa.receipt.pdf.helpdesk.service.ReceiptCosmosService; import org.junit.jupiter.api.BeforeEach; @@ -173,4 +175,28 @@ void getFailedReceiptByStatusFailUnexpectedStatus() { verify(receiptCosmosClientMock, never()).getFailedReceiptDocuments(anyString(), anyInt()); verify(receiptCosmosClientMock, never()).getInsertedReceiptDocuments(anyString(), anyInt()); } + + @Test + void getReceiptMessageSuccess() throws IoMessageNotFoundException { + when(receiptCosmosClientMock.getIoMessage(anyString())).thenReturn(new IOMessage()); + + IOMessage message = assertDoesNotThrow(() -> sut.getReceiptMessage(anyString())); + + assertNotNull(message); + } + + @Test + void getReceiptMessageFailClientThrowsReceiptNotFound() throws IoMessageNotFoundException { + when(receiptCosmosClientMock.getIoMessage(anyString())).thenThrow(IoMessageNotFoundException.class); + + assertThrows(IoMessageNotFoundException.class, () -> sut.getReceiptMessage(anyString())); + } + + @Test + void getReceiptMessageFailClientReturnNull() throws IoMessageNotFoundException { + when(receiptCosmosClientMock.getIoMessage(anyString())).thenReturn(null); + + assertThrows(IoMessageNotFoundException.class, () -> sut.getReceiptMessage(anyString())); + } + } \ No newline at end of file