Skip to content

Commit

Permalink
Merge pull request #42 from pagopa/PRDP-302-feat-add-recover-message-…
Browse files Browse the repository at this point in the history
…to-helpdesk

[PRDP-302] feat: add recover message to helpdesk
  • Loading branch information
pasqualespica authored Dec 21, 2023
2 parents 6ce6f91 + 97c87c4 commit 344eb22
Show file tree
Hide file tree
Showing 18 changed files with 501 additions and 2 deletions.
1 change: 1 addition & 0 deletions helm/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
Expand Down
1 change: 1 addition & 0 deletions helm/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions helm/values-uat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
7 changes: 7 additions & 0 deletions integration-test/src/features/receipt_pdf_helpdesk.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 20 additions & 0 deletions integration-test/src/script/teardown_script.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
8 changes: 8 additions & 0 deletions integration-test/src/step_definitions/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ const {
deleteDocumentFromReceiptErrorDatastore,
getDocumentFromReceiptsErrorDatastoreByBizEventId,
getDocumentFromReceiptsDatastoreByEventId,
deleteMultipleDocumentFromReceiptErrorDatastoreByEventId
deleteMultipleDocumentFromReceiptErrorDatastoreByEventId,
deleteDocumentFromReceiptMessageDatastore,
createDocumentInReceiptIoMessageDatastore
} = require("./receipts_datastore_client");
const {
getReceipt,
getReceiptByOrganizationFiscalCodeAndIUV,
getReceiptError,
getReceiptMessage,
getReceiptPdf,
postReceiptToReviewed,
postRecoverFailedReceipt,
Expand All @@ -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 = [];

Expand Down Expand Up @@ -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);
});




Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -131,15 +153,33 @@ 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,
deleteMultipleDocumentsFromReceiptsDatastoreByEventId,
deleteDocumentFromReceiptsDatastore,
updateReceiptToFailed,
deleteAllTestReceipts,
deleteAllTestReceiptsMessage,
createDocumentInReceiptIoMessageDatastore,

deleteDocumentFromReceiptErrorDatastore,
deleteDocumentFromReceiptMessageDatastore,
deleteMultipleDocumentFromReceiptErrorDatastoreByEventId,
createDocumentInReceiptErrorDatastore,
getDocumentFromReceiptsErrorDatastoreByBizEventId,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* It retrieves the receipt-message with the specified messageId
* <p>
*
* @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<Optional<String>> 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -53,4 +55,6 @@ public interface ReceiptCosmosClient {
* @return receipt documents
*/
Iterable<FeedResponse<Receipt>> getIOErrorToNotifyReceiptDocuments(String continuationToken, Integer pageSize);

IOMessage getIoMessage(String messageId) throws IoMessageNotFoundException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -233,4 +237,29 @@ public Iterable<FeedResponse<Receipt>> 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<IOMessage> 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");
}
}
Loading

0 comments on commit 344eb22

Please sign in to comment.