From f3f759b8a0fbf2a323d7f55643c7f879863bfa31 Mon Sep 17 00:00:00 2001 From: Pietro Tota Date: Mon, 9 Dec 2024 17:32:35 +0100 Subject: [PATCH 1/3] feat: add closure error data --- .../v2/BaseTransactionRetriedData.java | 2 +- .../documents/v2/ClosureErrorData.java | 34 ++++ .../v2/TransactionClosureErrorEvent.java | 10 +- .../v2/TransactionClosureRetriedData.java | 40 +++++ .../v2/TransactionClosureRetriedEvent.java | 10 +- .../TransactionEventTypeResolverTest.java | 156 +++++++++++++++++- .../commons/domain/v2/TransactionTest.java | 3 - .../commons/v2/TransactionTestUtils.java | 18 +- 8 files changed, 256 insertions(+), 17 deletions(-) create mode 100644 src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java create mode 100644 src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedData.java diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/BaseTransactionRetriedData.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/BaseTransactionRetriedData.java index 30683ab2..ace9f9c6 100644 --- a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/BaseTransactionRetriedData.java +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/BaseTransactionRetriedData.java @@ -17,7 +17,7 @@ @AllArgsConstructor @NoArgsConstructor @Generated -public sealed class BaseTransactionRetriedData permits TransactionRefundRetriedData,TransactionRetriedData { +public sealed class BaseTransactionRetriedData permits TransactionClosureRetriedData,TransactionRefundRetriedData,TransactionRetriedData { /** * Retry event count */ diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java new file mode 100644 index 00000000..51ec4701 --- /dev/null +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java @@ -0,0 +1,34 @@ +package it.pagopa.ecommerce.commons.documents.v2; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Generated; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.http.HttpStatus; + +import javax.annotation.Nullable; + +/** + * Data related to closure error event + */ +@Data +@Document +@AllArgsConstructor +@NoArgsConstructor +@Generated +public class ClosureErrorData { + + /** + * Http error code received by Node in close payment response: This field is + * null when HTTP response error code cannot be detected (f.e. timeout) + */ + @Nullable + private HttpStatus httpErrorCode; + + /** + * Node error description taken from error response body, if any + */ + @Nullable + private String errorDescription; +} diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureErrorEvent.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureErrorEvent.java index 7ff6ae80..c38edd2c 100644 --- a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureErrorEvent.java +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureErrorEvent.java @@ -14,16 +14,18 @@ @Generated @NoArgsConstructor @ToString(callSuper = true) -public final class TransactionClosureErrorEvent extends TransactionEvent { +public final class TransactionClosureErrorEvent extends TransactionEvent { /** * Convenience constructor which sets the creation date to now * - * @param transactionId transaction unique id + * @param transactionId transaction unique id + * @param closureErrorData the closure error related data */ public TransactionClosureErrorEvent( - String transactionId + String transactionId, + ClosureErrorData closureErrorData ) { - super(transactionId, TransactionEventCode.TRANSACTION_CLOSURE_ERROR_EVENT, null); + super(transactionId, TransactionEventCode.TRANSACTION_CLOSURE_ERROR_EVENT, closureErrorData); } } diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedData.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedData.java new file mode 100644 index 00000000..dc8ce839 --- /dev/null +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedData.java @@ -0,0 +1,40 @@ +package it.pagopa.ecommerce.commons.documents.v2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Generated; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.mapping.Document; + +import javax.annotation.Nullable; + +/** + * Data related to retry event for a transaction closure operation + * + * @see BaseTransactionRetriedData + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@Document +@NoArgsConstructor +@Generated +public final class TransactionClosureRetriedData extends BaseTransactionRetriedData { + + @Nullable + private ClosureErrorData closureErrorData; + + /** + * Constructor + * + * @param closureErrorData node closure error data + * @param retryCount the retry event counter + */ + public TransactionClosureRetriedData( + @Nullable ClosureErrorData closureErrorData, + Integer retryCount + ) { + super(retryCount); + this.closureErrorData = closureErrorData; + } +} diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedEvent.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedEvent.java index 72b63098..5bbc10bb 100644 --- a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedEvent.java +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/TransactionClosureRetriedEvent.java @@ -14,18 +14,18 @@ @Generated @NoArgsConstructor @ToString(callSuper = true) -public final class TransactionClosureRetriedEvent extends TransactionEvent { +public final class TransactionClosureRetriedEvent extends TransactionEvent { /** * Convenience constructor which sets the creation date to now * - * @param transactionId transaction unique id - * @param transactionRetriedData retry count data + * @param transactionId transaction unique id + * @param closureRetriedData closure retry data */ public TransactionClosureRetriedEvent( String transactionId, - TransactionRetriedData transactionRetriedData + TransactionClosureRetriedData closureRetriedData ) { - super(transactionId, TransactionEventCode.TRANSACTION_CLOSURE_RETRIED_EVENT, transactionRetriedData); + super(transactionId, TransactionEventCode.TRANSACTION_CLOSURE_RETRIED_EVENT, closureRetriedData); } } diff --git a/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java b/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java index f4a9f88d..190cb031 100644 --- a/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java +++ b/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java @@ -31,6 +31,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; import org.testcontainers.shaded.org.apache.commons.io.FileUtils; import reactor.core.publisher.Hooks; import reactor.core.publisher.Mono; @@ -1483,7 +1484,11 @@ void canRoundTripQueueClosureRetriedEvent() { "transactionId": "bdb92a6577fb4aab9bba2ebb80cd8310", "creationDate": "2023-09-25T14:44:31.177776+02:00[Europe/Rome]", "data": { - "retryCount": 0 + "retryCount": 0, + "closureErrorData": { + "httpErrorCode":"BAD_REQUEST", + "errorDescription":"ERROR" + } }, "eventCode": "TRANSACTION_CLOSURE_RETRIED_EVENT" }, @@ -1497,7 +1502,11 @@ void canRoundTripQueueClosureRetriedEvent() { .replace("\n", "").replace(" ", ""); QueueEvent originalEvent = new QueueEvent<>( TransactionTestUtils.transactionClosureRetriedEvent( - 0 + 0, + new ClosureErrorData( + HttpStatus.BAD_REQUEST, + "ERROR" + ) ), MOCK_TRACING_INFO ); @@ -1815,4 +1824,147 @@ void shouldDeserializeAuthorizationRequestedEventWithNullIdBundle() { .expectNext(expectedEvent) .verifyComplete(); } + + @Test + void canDeserializeClosureRetriedEventWithoutClosureErrorData() { + String serializedEvent = """ + { + "event": { + "_class": "it.pagopa.ecommerce.commons.documents.v2.TransactionClosureRetriedEvent", + "id": "0660cd04-db3e-4b7e-858b-e8f75a29ac30", + "transactionId": "bdb92a6577fb4aab9bba2ebb80cd8310", + "creationDate": "2023-09-25T14:44:31.177776+02:00[Europe/Rome]", + "data": { + "retryCount": 0 + }, + "eventCode": "TRANSACTION_CLOSURE_RETRIED_EVENT" + }, + "tracingInfo": { + "traceparent": "mock_traceparent", + "tracestate": "mock_tracestate", + "baggage": "mock_baggage" + } + } + """ + .replace("\n", "").replace(" ", ""); + QueueEvent originalEvent = new QueueEvent<>( + TransactionTestUtils.transactionClosureRetriedEvent( + 0 + ), + MOCK_TRACING_INFO + ); + originalEvent.event().setTransactionId("bdb92a6577fb4aab9bba2ebb80cd8310"); + originalEvent.event().setId("0660cd04-db3e-4b7e-858b-e8f75a29ac30"); + originalEvent.event().setCreationDate("2023-09-25T14:44:31.177776+02:00[Europe/Rome]"); + originalEvent.event().getData().setClosureErrorData(null); + Hooks.onOperatorDebug(); + StepVerifier.create( + jsonSerializer + .deserializeFromBytesAsync( + serializedEvent.getBytes(StandardCharsets.UTF_8), + new TypeReference>() { + } + ) + ) + .expectNext(originalEvent) + .verifyComplete(); + } + + @Test + void canRoundTripQueueClosureErrorEventWithClosureErrorData() { + String expectedSerializedEvent = """ + { + "event": { + "_class": "it.pagopa.ecommerce.commons.documents.v2.TransactionClosureErrorEvent", + "id": "0660cd04-db3e-4b7e-858b-e8f75a29ac30", + "transactionId": "bdb92a6577fb4aab9bba2ebb80cd8310", + "creationDate": "2023-09-25T14:44:31.177776+02:00[Europe/Rome]", + "data": { + "httpErrorCode":"BAD_REQUEST", + "errorDescription":"ERROR" + }, + "eventCode": "TRANSACTION_CLOSURE_ERROR_EVENT" + }, + "tracingInfo": { + "traceparent": "mock_traceparent", + "tracestate": "mock_tracestate", + "baggage": "mock_baggage" + } + } + """ + .replace("\n", "").replace(" ", ""); + QueueEvent originalEvent = new QueueEvent<>( + TransactionTestUtils.transactionClosureErrorEvent( + new ClosureErrorData( + HttpStatus.BAD_REQUEST, + "ERROR" + ) + ), + MOCK_TRACING_INFO + ); + originalEvent.event().setTransactionId("bdb92a6577fb4aab9bba2ebb80cd8310"); + originalEvent.event().setId("0660cd04-db3e-4b7e-858b-e8f75a29ac30"); + originalEvent.event().setCreationDate("2023-09-25T14:44:31.177776+02:00[Europe/Rome]"); + byte[] serialized = jsonSerializer.serializeToBytes(originalEvent); + String serializedString = new String(serialized); + System.out.println("Serialized object: " + serializedString); + + assertTrue( + serializedString + .contains( + "\"_class\":\"it.pagopa.ecommerce.commons.documents.v2.TransactionClosureErrorEvent\"" + ) + ); + assertEquals(expectedSerializedEvent, serializedString); + Hooks.onOperatorDebug(); + StepVerifier.create( + jsonSerializer + .deserializeFromBytesAsync( + serialized, + new TypeReference>() { + } + ) + ) + .expectNext(originalEvent) + .verifyComplete(); + } + + @Test + void canDeserializeClosureErrorEventWithoutClosureErrorData() { + String serializedEvent = """ + { + "event": { + "_class": "it.pagopa.ecommerce.commons.documents.v2.TransactionClosureErrorEvent", + "id": "0660cd04-db3e-4b7e-858b-e8f75a29ac30", + "transactionId": "bdb92a6577fb4aab9bba2ebb80cd8310", + "creationDate": "2023-09-25T14:44:31.177776+02:00[Europe/Rome]", + "eventCode": "TRANSACTION_CLOSURE_ERROR_EVENT" + }, + "tracingInfo": { + "traceparent": "mock_traceparent", + "tracestate": "mock_tracestate", + "baggage": "mock_baggage" + } + } + """ + .replace("\n", "").replace(" ", ""); + QueueEvent originalEvent = new QueueEvent<>( + TransactionTestUtils.transactionClosureErrorEvent(), + MOCK_TRACING_INFO + ); + originalEvent.event().setTransactionId("bdb92a6577fb4aab9bba2ebb80cd8310"); + originalEvent.event().setId("0660cd04-db3e-4b7e-858b-e8f75a29ac30"); + originalEvent.event().setCreationDate("2023-09-25T14:44:31.177776+02:00[Europe/Rome]"); + Hooks.onOperatorDebug(); + StepVerifier.create( + jsonSerializer + .deserializeFromBytesAsync( + serializedEvent.getBytes(StandardCharsets.UTF_8), + new TypeReference>() { + } + ) + ) + .expectNext(originalEvent) + .verifyComplete(); + } } diff --git a/src/test/java/it/pagopa/ecommerce/commons/domain/v2/TransactionTest.java b/src/test/java/it/pagopa/ecommerce/commons/domain/v2/TransactionTest.java index ab0fd175..79d92fb6 100644 --- a/src/test/java/it/pagopa/ecommerce/commons/domain/v2/TransactionTest.java +++ b/src/test/java/it/pagopa/ecommerce/commons/domain/v2/TransactionTest.java @@ -4,7 +4,6 @@ import it.pagopa.ecommerce.commons.documents.v2.authorization.*; import it.pagopa.ecommerce.commons.domain.v2.pojos.*; import it.pagopa.ecommerce.commons.generated.npg.v1.dto.OperationResultDto; -import it.pagopa.ecommerce.commons.generated.server.model.AuthorizationResultDto; import it.pagopa.ecommerce.commons.generated.server.model.TransactionStatusDto; import it.pagopa.ecommerce.commons.v2.TransactionTestUtils; import org.junit.jupiter.api.Test; @@ -637,10 +636,8 @@ void shouldConstructTransactionFromClosureErrorEventStream() { .transactionWithClosureRequested(transactionAuthorizationCompleted); it.pagopa.ecommerce.commons.domain.v2.TransactionWithClosureError expected = TransactionTestUtils .transactionWithClosureError(transactionClosureErrorEvent, transactionWithClosureRequested); - Mono actual = events .reduce(transaction, it.pagopa.ecommerce.commons.domain.v2.Transaction::applyEvent); - StepVerifier.create(actual).expectNextMatches(e -> e.equals(expected)).verifyComplete(); } diff --git a/src/test/java/it/pagopa/ecommerce/commons/v2/TransactionTestUtils.java b/src/test/java/it/pagopa/ecommerce/commons/v2/TransactionTestUtils.java index 839eabc2..1fe049a3 100644 --- a/src/test/java/it/pagopa/ecommerce/commons/v2/TransactionTestUtils.java +++ b/src/test/java/it/pagopa/ecommerce/commons/v2/TransactionTestUtils.java @@ -388,8 +388,14 @@ public static TransactionClosedEvent transactionClosedEvent(TransactionClosureDa @Nonnull public static TransactionClosureErrorEvent transactionClosureErrorEvent() { + return transactionClosureErrorEvent(null); + } + + @Nonnull + public static TransactionClosureErrorEvent transactionClosureErrorEvent(ClosureErrorData closureErrorData) { return new TransactionClosureErrorEvent( - TRANSACTION_ID + TRANSACTION_ID, + closureErrorData ); } @@ -571,9 +577,17 @@ public static TransactionClosureFailedEvent transactionClosureFailedEvent(Transa @Nonnull public static TransactionClosureRetriedEvent transactionClosureRetriedEvent(int retryCount) { + return transactionClosureRetriedEvent(retryCount, null); + } + + @Nonnull + public static TransactionClosureRetriedEvent transactionClosureRetriedEvent( + int retryCount, + ClosureErrorData closureErrorData + ) { return new TransactionClosureRetriedEvent( TRANSACTION_ID, - new TransactionRetriedData(retryCount) + new TransactionClosureRetriedData(closureErrorData, retryCount) ); } From d3619a657b1be58c85082fa17b8cdddfde0b8aaa Mon Sep 17 00:00:00 2001 From: Pietro Tota Date: Tue, 10 Dec 2024 09:29:04 +0100 Subject: [PATCH 2/3] feat: add closure error data to view --- .../it/pagopa/ecommerce/commons/documents/v2/Transaction.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/Transaction.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/Transaction.java index abf9a327..55cf5272 100644 --- a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/Transaction.java +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/Transaction.java @@ -58,6 +58,9 @@ public class Transaction extends BaseTransactionView { @Nullable private String userId; + @Nullable + private ClosureErrorData closureErrorData; + /** * Enumeration of transaction client initiators */ From 9bb178ab2c0d3c31b91b7bbb023b42fc22d41f79 Mon Sep 17 00:00:00 2001 From: Pietro Tota Date: Tue, 10 Dec 2024 13:05:26 +0100 Subject: [PATCH 3/3] chore: add error cause --- .../documents/v2/ClosureErrorData.java | 19 +++++++++++++++++++ .../TransactionEventTypeResolverTest.java | 12 ++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java index 51ec4701..d97de325 100644 --- a/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java +++ b/src/main/java/it/pagopa/ecommerce/commons/documents/v2/ClosureErrorData.java @@ -8,6 +8,7 @@ import org.springframework.http.HttpStatus; import javax.annotation.Nullable; +import javax.validation.constraints.NotNull; /** * Data related to closure error event @@ -19,6 +20,20 @@ @Generated public class ClosureErrorData { + /** + * Enumeration of errors that can happen + */ + public enum ErrorType { + /** + * KO response received from Node + */ + KO_RESPONSE_RECEIVED, + /** + * Error happen during communication, no response have been received + */ + COMMUNICATION_ERROR, + } + /** * Http error code received by Node in close payment response: This field is * null when HTTP response error code cannot be detected (f.e. timeout) @@ -31,4 +46,8 @@ public class ClosureErrorData { */ @Nullable private String errorDescription; + + @NotNull + private ErrorType errorType; + } diff --git a/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java b/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java index 190cb031..abae78d1 100644 --- a/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java +++ b/src/test/java/it/pagopa/ecommerce/commons/documents/v2/serialization/TransactionEventTypeResolverTest.java @@ -1487,7 +1487,8 @@ void canRoundTripQueueClosureRetriedEvent() { "retryCount": 0, "closureErrorData": { "httpErrorCode":"BAD_REQUEST", - "errorDescription":"ERROR" + "errorDescription":"ERROR", + "errorType": "KO_RESPONSE_RECEIVED" } }, "eventCode": "TRANSACTION_CLOSURE_RETRIED_EVENT" @@ -1505,7 +1506,8 @@ void canRoundTripQueueClosureRetriedEvent() { 0, new ClosureErrorData( HttpStatus.BAD_REQUEST, - "ERROR" + "ERROR", + ClosureErrorData.ErrorType.KO_RESPONSE_RECEIVED ) ), MOCK_TRACING_INFO @@ -1881,7 +1883,8 @@ void canRoundTripQueueClosureErrorEventWithClosureErrorData() { "creationDate": "2023-09-25T14:44:31.177776+02:00[Europe/Rome]", "data": { "httpErrorCode":"BAD_REQUEST", - "errorDescription":"ERROR" + "errorDescription":"ERROR", + "errorType": "KO_RESPONSE_RECEIVED" }, "eventCode": "TRANSACTION_CLOSURE_ERROR_EVENT" }, @@ -1897,7 +1900,8 @@ void canRoundTripQueueClosureErrorEventWithClosureErrorData() { TransactionTestUtils.transactionClosureErrorEvent( new ClosureErrorData( HttpStatus.BAD_REQUEST, - "ERROR" + "ERROR", + ClosureErrorData.ErrorType.KO_RESPONSE_RECEIVED ) ), MOCK_TRACING_INFO