diff --git a/docs/openapi/api-internal-v1.yaml b/docs/openapi/api-internal-v1.yaml index d0029d57..b319c02a 100644 --- a/docs/openapi/api-internal-v1.yaml +++ b/docs/openapi/api-internal-v1.yaml @@ -562,6 +562,9 @@ components: type: string message: type: string + additionalDetails: + type: object + additionalProperties: true flowThrow: type: string enum: diff --git a/src/main/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandler.java b/src/main/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandler.java index ec47d339..ea5501f5 100644 --- a/src/main/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandler.java +++ b/src/main/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandler.java @@ -35,7 +35,7 @@ public Mono handleRetryError(PaperTrackingsErrors paperTrackingsErrors) { } public Mono handleError(PaperTrackingsErrors paperTrackingsErrors, Long messageReceiveCount, PnPaperTrackerValidationException ex) { - boolean isStatusCodeError = ErrorCategory.STATUS_CODE_ERROR.equals(paperTrackingsErrors.getErrorCategory()); + boolean isStatusCodeError = Objects.nonNull(paperTrackingsErrors.getDetails()) && ErrorCause.VALUES_NOT_FOUND.equals(paperTrackingsErrors.getDetails().getCause()); if (isStatusCodeError) { if (messageReceiveCount < 5) { diff --git a/src/main/java/it/pagopa/pn/papertracker/mapper/PaperTrackingsErrorsMapper.java b/src/main/java/it/pagopa/pn/papertracker/mapper/PaperTrackingsErrorsMapper.java index f2dc5852..4219cfd4 100644 --- a/src/main/java/it/pagopa/pn/papertracker/mapper/PaperTrackingsErrorsMapper.java +++ b/src/main/java/it/pagopa/pn/papertracker/mapper/PaperTrackingsErrorsMapper.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import java.time.Instant; +import java.util.Map; @RequiredArgsConstructor(access = AccessLevel.NONE) public class PaperTrackingsErrorsMapper { @@ -15,6 +16,7 @@ public static PaperTrackingsErrors buildPaperTrackingsError(PaperTrackings paper ErrorCategory errorCategory, ErrorCause errorCause, String errorMessage, + Map additionalDetails, FlowThrow flowThrow, ErrorType errorType, String eventIdThrow) { @@ -25,6 +27,7 @@ public static PaperTrackingsErrors buildPaperTrackingsError(PaperTrackings paper .details(ErrorDetails.builder() .cause(errorCause) .message(errorMessage) + .additionalDetails(additionalDetails) .build()) .flowThrow(flowThrow) .eventThrow(statusCode) diff --git a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/AdditionalDetailsConverter.java b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/AdditionalDetailsConverter.java new file mode 100644 index 00000000..e753ace2 --- /dev/null +++ b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/AdditionalDetailsConverter.java @@ -0,0 +1,84 @@ +package it.pagopa.pn.papertracker.middleware.dao.dynamo; + +import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter; +import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; +import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +public class AdditionalDetailsConverter implements AttributeConverter> { + + @Override + public AttributeValue transformFrom(Map value) { + if (Objects.isNull(value)) { + return AttributeValue.builder().nul(true).build(); + } + return AttributeValue.builder().m(value.entrySet().stream() + .collect(java.util.stream.Collectors.toMap( + Map.Entry::getKey, + e -> toAttr(e.getValue()) + ))).build(); + } + + @Override + public Map transformTo(AttributeValue attributeValue) { + if (Boolean.TRUE.equals(attributeValue.nul())) { + return null; + } + if (Objects.isNull(attributeValue.m())) { + return Map.of(); + } + return attributeValue.m().entrySet().stream().collect(java.util.stream.Collectors.toMap( + Map.Entry::getKey, + e -> Optional.ofNullable(fromAttr(e.getValue())).orElse("") + )); + } + + @Override + public AttributeValueType attributeValueType() { + return AttributeValueType.M; + } + + @Override + public EnhancedType> type() { + return EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(Object.class)); + } + + /* -------- conversioni -------- */ + + private AttributeValue toAttr(Object v) { + return switch (v) { + case String s -> AttributeValue.builder().s(s).build(); + case Number n -> AttributeValue.builder().n(n.toString()).build(); + case Boolean b -> AttributeValue.builder().bool(b).build(); + case Map m -> AttributeValue.builder().m(m.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), e -> toAttr(e.getValue())))) + .build(); + case List l -> AttributeValue.builder().l(l.stream().map(this::toAttr).toList()).build(); + case null -> AttributeValue.builder().nul(true).build(); + default -> throw new IllegalArgumentException("Unsupported type in additionalDetails: " + v.getClass()); + }; + } + + private Object fromAttr(AttributeValue attributeValue) { + if (Objects.nonNull(attributeValue.nul()) && attributeValue.nul()) return null; + if (Objects.nonNull(attributeValue.s())) return attributeValue.s(); + if (Objects.nonNull(attributeValue.n())) return attributeValue.n(); + if (Objects.nonNull(attributeValue.bool())) return attributeValue.bool(); + if (Objects.nonNull((attributeValue.m()))) { + return attributeValue.m().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> Optional.ofNullable(fromAttr(e.getValue())).orElse(""))); + } + if (Objects.nonNull(attributeValue.l())) { + return attributeValue.l().stream() + .map(this::fromAttr) + .toList(); + } + return null; + } +} diff --git a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCategory.java b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCategory.java index e6f3d5cd..cd067bc7 100644 --- a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCategory.java +++ b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCategory.java @@ -9,7 +9,6 @@ public enum ErrorCategory { NOT_RETRYABLE_EVENT_ERROR("Evento not retryable ricevuto"), RENDICONTAZIONE_SCARTATA("Rendicontazione scartata"), DATE_ERROR("Errore nella validazione delle date della sequenza."), - STATUS_CODE_ERROR("Errore nella validazione della presenza degli elementi della sequenza."), LAST_EVENT_EXTRACTION_ERROR("Errore nell'estrazione della sequenza dall'ultimo evento."), REGISTERED_LETTER_CODE_ERROR("Errore nella validazione del registered letter code"), REGISTERED_LETTER_CODE_NOT_FOUND("Registered letter code non trovato"), @@ -21,7 +20,12 @@ public enum ErrorCategory { DEMAT_ATTACHMENT_NUMBER_ERROR("Errore nel numero di attachment trovati per demat"), DUPLICATED_EVENT("Errore nella validazione della presenza di eventi duplicati"), INVALID_STATE_FOR_STOCK_890("Stato non valido per giacenza 890"), - INCONSISTENT_STATE("Tracking completato o in attesa validazione OCR"); + INCONSISTENT_STATE("Tracking completato o in attesa validazione OCR"), + /** + * @deprecated Non utilizzare. Campo mantenuto solo per retrocompatibilità. + */ + @Deprecated + STATUS_CODE_ERROR("Errore nella validazione della presenza degli elementi della sequenza."); private final String value; diff --git a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCause.java b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCause.java index 45145f17..5329066f 100644 --- a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCause.java +++ b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorCause.java @@ -13,7 +13,11 @@ public enum ErrorCause { OCR_KO( "Errore nella validazione dell'OCR"), OCR_UNSUPPORTED_PRODUCT("Prodotto non supportato per la validazione OCR"), STOCK_890_REFINEMENT_MISSING("Spedizione 890 non perfezionata"), - STOCK_890_REFINEMENT_ERROR("Errore nel perfezionamento della spedizione 890"),; + STOCK_890_REFINEMENT_ERROR("Errore nel perfezionamento della spedizione 890"), + VALUE_AFTER_REFINEMENT("Evento arrivato dopo la conclusione della spedizione o mentre si stava aspettando l’OCR (DONE o AWAITING_OCR)"), + VALUES_NOT_MATCHING("Errore nella validazione degli allegati della sequenza: mancano degli allegati"), + INVALID_VALUES("Errore nella validazione"), + VALUES_NOT_FOUND("Errore nella validazione dei statusCode della sequenza: non sono presenti tutti gli statusCode previsti dalla macchina a stati"); private final String description; diff --git a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorDetails.java b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorDetails.java index f6bc873d..88936d37 100644 --- a/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorDetails.java +++ b/src/main/java/it/pagopa/pn/papertracker/middleware/dao/dynamo/entity/ErrorDetails.java @@ -1,7 +1,11 @@ package it.pagopa.pn.papertracker.middleware.dao.dynamo.entity; +import it.pagopa.pn.papertracker.middleware.dao.dynamo.AdditionalDetailsConverter; import lombok.*; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; +import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbConvertedBy; + +import java.util.Map; @Setter @Getter @@ -13,7 +17,10 @@ public class ErrorDetails { public static final String COL_CAUSE = "cause"; public static final String COL_MESSAGE = "message"; + public static final String COL_ADDITIONAL_DETAILS = "additionalDetails"; private ErrorCause cause; private String message; + @Getter(onMethod = @__({ @DynamoDbConvertedBy(AdditionalDetailsConverter.class) })) + private Map additionalDetails; } diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/AR/FinalEventBuilderAr.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/AR/FinalEventBuilderAr.java index 918bd8a0..e9f8a3e7 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/AR/FinalEventBuilderAr.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/AR/FinalEventBuilderAr.java @@ -20,6 +20,7 @@ import java.time.*; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Map; import static it.pagopa.pn.papertracker.model.EventStatusCodeEnum.*; @@ -91,6 +92,9 @@ private Mono handleNoDifferenceGreater(HandlerContext context, "The difference between RECRN005A and RECRN010 is greater than the configured duration", PaperTrackingsErrorsMapper.buildPaperTrackingsError(paperTrackings, finalEvent.getStatusCode(), ErrorCategory.RENDICONTAZIONE_SCARTATA, ErrorCause.GIACENZA_DATE_ERROR, String.format("RECRN005A getStatusTimestamp: %s, RECRN010 getStatusTimestamp: %s", eventRECRN00XA.getStatusTimestamp(), eventRECRN010.getStatusTimestamp()), + Map.of("recrn005aTimestamp", eventRECRN00XA.getStatusTimestamp().toString(), + "recrn010Timestamp", eventRECRN010.getStatusTimestamp().toString() + ), FlowThrow.FINAL_EVENT_BUILDING, ErrorType.ERROR, finalEvent.getId()))); } return addEventToSend(context, finalEvent, EventStatusCodeEnum.fromKey(statusCode).getStatus().name()); diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890.java index 4f73afdc..e3533240 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; +import java.util.Map; import java.util.Objects; @Component @@ -62,6 +63,9 @@ private boolean checkState(HandlerContext context) { ErrorCategory.INCONSISTENT_STATE, ErrorCause.STOCK_890_REFINEMENT_MISSING, "invalid AWAITING_REFINEMENT state for stock 890", + Map.of("statusCode", context.getPaperProgressStatusEvent().getStatusCode(), + "statusTimestamp", context.getPaperProgressStatusEvent().getStatusDateTime().toString() + ), FlowThrow.SEQUENCE_VALIDATION, ErrorType.ERROR, context.getEventId() @@ -81,6 +85,9 @@ private boolean checkState(HandlerContext context) { ErrorCategory.INCONSISTENT_STATE, ErrorCause.STOCK_890_REFINEMENT_ERROR, "Refinement process reached KO state, cannot proceed with final event validation", + Map.of("statusCode", context.getPaperProgressStatusEvent().getStatusCode(), + "statusTimestamp", context.getPaperProgressStatusEvent().getStatusDateTime().toString() + ), FlowThrow.SEQUENCE_VALIDATION, ErrorType.ERROR, context.getEventId() @@ -94,6 +101,7 @@ private boolean checkState(HandlerContext context) { ErrorCategory.INVALID_STATE_FOR_STOCK_890, ErrorCause.STOCK_890_REFINEMENT_ERROR, String.format("Invalid state %s for processing stock 890 final event",state), + null, FlowThrow.SEQUENCE_VALIDATION, ErrorType.ERROR, context.getEventId() diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponse.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponse.java index 303f8fc6..ec9aa664 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponse.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponse.java @@ -1,5 +1,7 @@ package it.pagopa.pn.papertracker.service.handler_step.generic; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.sngular.apigenerator.asyncapi.business_model.model.event.Data; import com.sngular.apigenerator.asyncapi.business_model.model.event.OcrDataResultPayload; import it.pagopa.pn.papertracker.exception.PnPaperTrackerValidationException; @@ -15,6 +17,7 @@ import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; +import java.util.Map; import java.util.Objects; @Component @@ -23,6 +26,7 @@ public class CheckOcrResponse implements HandlerStep { private final PaperTrackingsDAO paperTrackingsDAO; + private final ObjectMapper objectMapper; /** * Step che effettua un controllo sul messaggio ricevuto dal servizio OCR.
@@ -71,11 +75,21 @@ private Mono handleFinalStateError(Event event, PaperTrackings paperTracki PaperTrackingsErrorsMapper.buildPaperTrackingsError( paperTrackings, event.getStatusCode(), ErrorCategory.OCR_VALIDATION, cause, "CommandId: " + ocrResultMessage.getCommandId(), + Map.of("ocrDataResultPayload", transformToMap(ocrResultMessage.getData())), FlowThrow.DEMAT_VALIDATION, type, event.getId() ) )); } + private Map transformToMap(Data data) { + return objectMapper.convertValue( + data, + new TypeReference<>() { + } + ); + } + + private Mono handleValidationStatus(Data.ValidationStatus status, PaperTrackings paperTrackings, Event event, OcrDataResultPayload ocrResultMessage, HandlerContext context) { return switch (status) { @@ -84,6 +98,7 @@ private Mono handleValidationStatus(Data.ValidationStatus status, PaperTra PaperTrackingsErrorsMapper.buildPaperTrackingsError( paperTrackings, event.getStatusCode(), ErrorCategory.OCR_VALIDATION, ErrorCause.OCR_KO, ocrResultMessage.getData().getDescription(), + Map.of("ocrDataResultPayload", transformToMap(ocrResultMessage.getData())), FlowThrow.DEMAT_VALIDATION, ErrorType.ERROR, event.getId() ) )); diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingState.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingState.java index 40b45511..29cd6740 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingState.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingState.java @@ -3,6 +3,7 @@ import it.pagopa.pn.papertracker.exception.PnPaperTrackerValidationException; import it.pagopa.pn.papertracker.mapper.PaperTrackingsErrorsMapper; import it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.ErrorCategory; +import it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.ErrorCause; import it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.ErrorType; import it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.FlowThrow; import it.pagopa.pn.papertracker.model.HandlerContext; @@ -13,6 +14,9 @@ import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; +import java.util.Map; +import java.util.Objects; + import static it.pagopa.pn.papertracker.utils.TrackerUtility.isInvalidState; @Component @@ -53,6 +57,9 @@ private Mono createValidationError(HandlerContext ctx, String statusCode) : ctx.getPaperTrackings().getState().name(); String errorMsg = String.format("Tracking in state %s, statusCode %s: %s", state, statusCode, ctx.getTrackingId()); + Map additionalDetails = Map.of("statusCode", statusCode, + "statusTimestamp", Objects.toString(ctx.getPaperProgressStatusEvent().getStatusDateTime(), null) + ); return Mono.error(new PnPaperTrackerValidationException( errorMsg, @@ -60,8 +67,9 @@ private Mono createValidationError(HandlerContext ctx, String statusCode) ctx.getPaperTrackings(), statusCode, ErrorCategory.INCONSISTENT_STATE, - null, + ErrorCause.VALUE_AFTER_REFINEMENT, errorMsg, + additionalDetails, FlowThrow.CHECK_TRACKING_STATE, ErrorType.WARNING, ctx.getEventId() diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/DuplicatedEventFiltering.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/DuplicatedEventFiltering.java index 48727671..db7e4f61 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/DuplicatedEventFiltering.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/DuplicatedEventFiltering.java @@ -19,6 +19,7 @@ import java.time.ZoneOffset; import java.util.List; +import java.util.Map; import java.util.Objects; import static java.time.temporal.ChronoUnit.SECONDS; @@ -65,6 +66,10 @@ private PnPaperTrackerValidationException createValidationException(PaperTrackin PaperProgressStatusEvent event, HandlerContext context) { String statusCode = event.getStatusCode(); + Map additionalDetails = Map.of( + "statusCode", statusCode, + "statusTimestamp", Objects.toString(event.getStatusDateTime(), null) + ); return new PnPaperTrackerValidationException( "Duplicated event found: " + statusCode, PaperTrackingsErrorsMapper.buildPaperTrackingsError( @@ -73,6 +78,7 @@ private PnPaperTrackerValidationException createValidationException(PaperTrackin ErrorCategory.DUPLICATED_EVENT, null, "Duplicated event found for statusCode: " + statusCode, + additionalDetails, FlowThrow.DUPLICATED_EVENT_VALIDATION, ErrorType.WARNING, context.getEventId() diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/GenericSequenceValidator.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/GenericSequenceValidator.java index e31577d5..101fd1b3 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/GenericSequenceValidator.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/GenericSequenceValidator.java @@ -68,7 +68,7 @@ public Mono validateSequence(PaperTrackings paperTrackings, Hand log.info("Starting validation for sequence for paper tracking : {}", paperTrackings); return extractSequenceFromEvents(paperTrackings.getEvents(), sequenceConfig.sequenceStatusCodes(), context.getReworkId()) .filter(events -> !CollectionUtils.isEmpty(events)) - .switchIfEmpty(Mono.defer(() -> getErrorOrSaveWarning("Invalid lastEvent for sequence validation", context, paperTrackings, ErrorCategory.LAST_EVENT_EXTRACTION_ERROR, strictFinalEventValidation, Collections.emptyList()))) + .switchIfEmpty(Mono.defer(() -> getErrorOrSaveWarning("Invalid lastEvent for sequence validation", context, paperTrackings, ErrorCategory.LAST_EVENT_EXTRACTION_ERROR,null,null, strictFinalEventValidation, Collections.emptyList()))) .flatMap(this::getOnlyLatestEvents) .flatMap(events -> validatePresenceOfStatusCodes(events, paperTrackings, context, sequenceConfig.requiredStatusCodes(),strictFinalEventValidation)) .flatMap(events -> validateBusinessTimestamps(events, paperTrackings, context, sequenceConfig,strictFinalEventValidation, paperTrackingsToUpdate)) @@ -147,8 +147,9 @@ private Mono> verifyRequiredAttachments(List events, PaperTra if (missingDocs.isEmpty() || isPresentArcadOrCadForStock890(missingDocs)) { return Mono.just(events); } - return getErrorOrSaveWarning("Missed required attachments for the sequence validation: " + missingDocs, context, paperTrackings, ErrorCategory.ATTACHMENTS_ERROR, strictFinalEventValidation, events); - } + Map additionalDetails = Map.of("missingAttachments", String.join(",", missingDocs)); + return getErrorOrSaveWarning + ("Missed required attachments for the sequence validation: " + missingDocs, context, paperTrackings, ErrorCategory.ATTACHMENTS_ERROR, ErrorCause.VALUES_NOT_MATCHING, additionalDetails, strictFinalEventValidation, events); } private boolean isPresentArcadOrCadForStock890(HashSet missingDocs) { return missingDocs.size() == 1 && (missingDocs.contains(ARCAD.getValue()) || missingDocs.contains(CAD.getValue())); @@ -168,7 +169,9 @@ private Mono> verifyValidAttachments(List events, PaperTracki .collect(Collectors.toSet()); if (!CollectionUtils.isEmpty(invalidDocs)) { - return getErrorOrSaveWarning("Event " + e.getStatusCode() + " contains invalid attachments: " + invalidDocs, context, paperTrackings, ErrorCategory.ATTACHMENTS_ERROR, strictFinalEventValidation, events); + Map additionalDetails = Map.of("invalidAttachments", String.join(",", invalidDocs)); + + return getErrorOrSaveWarning("Event " + e.getStatusCode() + " contains invalid attachments: " + invalidDocs, context, paperTrackings, ErrorCategory.ATTACHMENTS_ERROR, ErrorCause.INVALID_VALUES, additionalDetails, strictFinalEventValidation, events); } } return Mono.just(events); @@ -268,11 +271,24 @@ private Mono> validateDeliveryFailureCause(List events, Paper return Mono.just(event); } + List> affectedEventsList = events.stream() + .map(ev -> Map.of( + "statusCode", Optional.ofNullable(ev.getStatusCode()).orElse(""), + "statusTimestamp", Objects.nonNull(ev.getStatusTimestamp()) ? ev.getStatusTimestamp().toString() : "", + "deliveryFailureCause", Optional.ofNullable(ev.getDeliveryFailureCause()).orElse("") + )) + .toList(); + + Map additionalDetails = Map.of( + "affectedEvents", affectedEventsList + ); return getErrorOrSaveWarning( "Invalid deliveryFailureCause: " + deliveryFailureCause, context, paperTrackings, ErrorCategory.DELIVERY_FAILURE_CAUSE_ERROR, + ErrorCause.VALUES_NOT_MATCHING, + additionalDetails, strictFinalEventValidation, event ); @@ -296,15 +312,31 @@ private Mono> validateRegisteredLetterCode(List events, Paper String firstRegisteredLetterCode = filteredEvents.getFirst().getRegisteredLetterCode(); if (filteredEvents.stream().anyMatch(event -> !StringUtils.hasText(event.getRegisteredLetterCode()))) { + Map statusInfoMap = events.stream() + .collect(Collectors.toMap( + Event::getStatusCode, + event -> event.getStatusTimestamp().toString(), + (existing, replacement) -> existing + )); + + Map additionalDetails = Map.of("affectedEvents", statusInfoMap); return getErrorOrSaveWarning("Registered letter code is null or empty in one or more events: " + filteredEvents.stream().map(Event::getRegisteredLetterCode).toList(), - context, paperTrackings, ErrorCategory.REGISTERED_LETTER_CODE_NOT_FOUND, strictFinalEventValidation, filteredEvents); + context, paperTrackings, ErrorCategory.REGISTERED_LETTER_CODE_NOT_FOUND, ErrorCause.INVALID_VALUES, additionalDetails, strictFinalEventValidation, filteredEvents); } if (filteredEvents.stream().anyMatch(event -> !event.getRegisteredLetterCode().equals(firstRegisteredLetterCode))) { + Map statusInfoMap = events.stream() + .collect(Collectors.toMap( + Event::getStatusCode, + event -> event.getStatusTimestamp().toString(), + (existing, replacement) -> existing + )); + + Map additionalDetails = Map.of("affectedEvents", statusInfoMap); return getErrorOrSaveWarning("Registered letter codes do not match in sequence: " + filteredEvents.stream().map(Event::getRegisteredLetterCode).toList(), - context, paperTrackings, ErrorCategory.REGISTERED_LETTER_CODE_ERROR, strictFinalEventValidation, filteredEvents); + context, paperTrackings, ErrorCategory.REGISTERED_LETTER_CODE_ERROR, ErrorCause.INVALID_VALUES, additionalDetails, strictFinalEventValidation, filteredEvents); } paperTrackingsToUpdate.getPaperStatus().setRegisteredLetterCode(firstRegisteredLetterCode); @@ -339,11 +371,17 @@ private Mono> validatePresenceOfStatusCodes(List events, Pape Set missingStatusCodes = new HashSet<>(requiredStatusCodes); missingStatusCodes.removeAll(eventStatusCodes); if (!missingStatusCodes.isEmpty()) { + + Map additionalDetails = Map.of( + "missingStatusCodes", String.join(",",missingStatusCodes) + ); return getErrorOrSaveWarning( "Necessary status code not found in events: " + missingStatusCodes, context, paperTrackings, - ErrorCategory.STATUS_CODE_ERROR, + ErrorCategory.INCONSISTENT_STATE, + ErrorCause.VALUES_NOT_FOUND, + additionalDetails, strictFinalEventValidation, events ); @@ -372,7 +410,15 @@ private Mono> validateBusinessTimestamps(List events, PaperTr paperTrackingsToUpdate.getPaperStatus().setValidatedSequenceTimestamp(validFinal); return Mono.just(events); } - return getErrorOrSaveWarning("Invalid business timestamps", context, paperTrackings, ErrorCategory.DATE_ERROR, strictFinalEventValidation, events); + Map statusInfoMap = events.stream() + .collect(Collectors.toMap( + Event::getStatusCode, + event -> event.getStatusTimestamp().toString(), + (existing, replacement) -> existing + )); + + Map additionalDetails = Map.of("affectedEvents", statusInfoMap); + return getErrorOrSaveWarning("Invalid business timestamps", context, paperTrackings, ErrorCategory.DATE_ERROR, ErrorCause.INVALID_VALUES, additionalDetails, strictFinalEventValidation, events); } private boolean checkPredictedRefinementTypeIfStock890(PaperTrackings paperTrackings, Instant validFinal) { @@ -403,15 +449,16 @@ private boolean allStockStatusTimestampAreEquals(List events, Set return timestamps.size() <= 1 || timestamps.stream().allMatch(t -> t.equals(timestamps.getFirst())); } - private Mono getErrorOrSaveWarning(String message, HandlerContext context, PaperTrackings paperTrackings, ErrorCategory errorCategory, Boolean strictFinalEventValidation, T returnValue) { + private Mono getErrorOrSaveWarning(String message, HandlerContext context, PaperTrackings paperTrackings, ErrorCategory errorCategory, ErrorCause errorCause, Map additionalDetails, Boolean strictFinalEventValidation, T returnValue) { log.info("getErrorOrSaveWarning for trackingId {}: {} | strictFinalEventValidation: {}", context.getTrackingId(), message, strictFinalEventValidation); var error = PaperTrackingsErrorsMapper.buildPaperTrackingsError( paperTrackings, context.getPaperProgressStatusEvent().getStatusCode(), errorCategory, - null, + errorCause, message, + additionalDetails, FlowThrow.SEQUENCE_VALIDATION, strictFinalEventValidation ? ErrorType.ERROR : ErrorType.WARNING, context.getEventId() diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/NotRetryableErrorInserting.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/NotRetryableErrorInserting.java index 6a6191ef..bf97a1b4 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/NotRetryableErrorInserting.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/NotRetryableErrorInserting.java @@ -36,6 +36,7 @@ public Mono execute(HandlerContext context) { ErrorCategory.NOT_RETRYABLE_EVENT_ERROR, null, EventStatusCodeEnum.fromKey(statusCode).getStatusCodeDescription(), + null, FlowThrow.NOT_RETRYABLE_EVENT_HANDLER, ErrorType.WARNING, context.getEventId() diff --git a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/PcRetryService.java b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/PcRetryService.java index aeb5cd8d..62538e2e 100644 --- a/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/PcRetryService.java +++ b/src/main/java/it/pagopa/pn/papertracker/service/handler_step/generic/PcRetryService.java @@ -51,6 +51,7 @@ private PaperTrackingsErrors buildErrorForGeneric(HandlerContext context) { ErrorCategory.MAX_RETRY_REACHED_ERROR, null, "Retry not found for trackingId: " + context.getPaperTrackings().getTrackingId(), + null, FlowThrow.RETRY_PHASE, ErrorType.ERROR, context.getEventId()); @@ -63,6 +64,7 @@ private PaperTrackingsErrors buildErrorForCON996(HandlerContext context) { ErrorCategory.NOT_RETRYABLE_EVENT_ERROR, null, EventStatusCodeEnum.fromKey(statusCode).getStatusCodeDescription(), + null, FlowThrow.NOT_RETRYABLE_EVENT_HANDLER, ErrorType.WARNING, context.getEventId()); diff --git a/src/test/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandlerTest.java b/src/test/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandlerTest.java index a9e336f8..a8f747b0 100644 --- a/src/test/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandlerTest.java +++ b/src/test/java/it/pagopa/pn/papertracker/exception/PaperTrackerExceptionHandlerTest.java @@ -51,9 +51,10 @@ void handleStatusCodeErrorFirstREtry(){ PnPaperTrackerValidationException exception = new PnPaperTrackerValidationException("Validation error", PaperTrackingsErrorsMapper.buildPaperTrackingsError( new PaperTrackings(), "RECRN001C", - ErrorCategory.STATUS_CODE_ERROR, - null, + ErrorCategory.INCONSISTENT_STATE, + ErrorCause.VALUES_NOT_FOUND, "message", + null, FlowThrow.SEQUENCE_VALIDATION, ErrorType.ERROR, "eventId")); @@ -74,9 +75,10 @@ void handleStatusCodeErrorSixthRetry(){ PnPaperTrackerValidationException exception = new PnPaperTrackerValidationException("Validation error", PaperTrackingsErrorsMapper.buildPaperTrackingsError( new PaperTrackings(), "RECRN001C", - ErrorCategory.STATUS_CODE_ERROR, - null, + ErrorCategory.INCONSISTENT_STATE, + ErrorCause.VALUES_NOT_FOUND, "message", + null, FlowThrow.SEQUENCE_VALIDATION, ErrorType.ERROR, "eventId")); @@ -100,6 +102,7 @@ void handleOtherError(){ ErrorCategory.ATTACHMENTS_ERROR, null, "message", + null, FlowThrow.SEQUENCE_VALIDATION, ErrorType.ERROR, "eventId")); diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/HandlerFactoryArIT.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/HandlerFactoryArIT.java index 7a854f4e..7fceecd9 100644 --- a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/HandlerFactoryArIT.java +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/HandlerFactoryArIT.java @@ -59,13 +59,19 @@ public class HandlerFactoryArIT extends BaseTest.WithLocalStack { @ParameterizedTest @EnumSource(value = TestSequenceAREnum.class) - void testARSequence(TestSequenceAREnum seq) throws InterruptedException { + void testARSequence(TestSequenceAREnum seq) { //Arrange when(safeStorageClient.getSafeStoragePresignedUrl(any())).thenReturn(Mono.just("url")); String iun = UUID.randomUUID().toString(); String requestId = "PREPARE_ANALOG_DOMICILE.IUN_" + iun + ".RECINDEX_0.ATTEMPT_0.PCRETRY_0"; paperTrackingsDAO.putIfAbsent(getPaperTrackings(requestId, it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.ProductType.AR)).block(); List eventsToSend = prepareTest(seq, requestId); + if(seq.equals(FAIL_AR_ERROR_CAUSE)){ + eventsToSend.forEach(singleStatusUpdate -> { + assertNotNull(singleStatusUpdate.getAnalogMail()); + singleStatusUpdate.getAnalogMail().setDeliveryFailureCause(null); + }); + } PcRetryResponse r1 = buildPcRetryResponse(requestId, iun, 1); PcRetryResponse r2 = buildPcRetryResponse(r1.getRequestId(), iun, 2); @@ -751,6 +757,8 @@ private void verifyErrors(List errs, TestSequenceAREnum se FAIL_COMPIUTA_GIACENZA_AR -> assertEquals(0, errs.size()); case OK_AR_NOT_ORDERED -> assertSingleWarning(errs, ErrorCategory.DUPLICATED_EVENT, FlowThrow.DUPLICATED_EVENT_VALIDATION, "RECRN001A"); + case FAIL_AR_ERROR_CAUSE -> + assertSingleError(errs, ErrorCategory.DELIVERY_FAILURE_CAUSE_ERROR, FlowThrow.SEQUENCE_VALIDATION, "Invalid deliveryFailureCause:"); case OK_GIACENZA_AR_2 -> assertSingleWarning(errs, ErrorCategory.DUPLICATED_EVENT, FlowThrow.DUPLICATED_EVENT_VALIDATION, "RECRN010"); case OK_GIACENZA_AR_3 -> diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/TestSequenceAREnum.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/TestSequenceAREnum.java index 00c07009..44c116be 100644 --- a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/TestSequenceAREnum.java +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/AR/TestSequenceAREnum.java @@ -12,6 +12,7 @@ public enum TestSequenceAREnum { OK_RETRY_AR_2(List.of("CON080", "CON020", "RECRN010", "RECRN011", "RECRN006", "CON080", "CON020", "RECRN001A", "RECRN001B", "RECRN001C"), List.of("RECRN001B-AR","CON020-7Z","CON020-7Z")), FAIL_DISCOVERY_AR(List.of("CON080", "CON020", "RECRN002D", "RECRN002E", "RECRN002F", "CON080", "CON020","RECRN001A", "RECRN001B", "RECRN001C"), List.of("RECRN002E-Plico#Indagine", "CON020-7Z", "CON020-7Z","RECRN001B-AR" )), FAIL_AR(List.of("CON080", "CON020", "RECRN002A", "RECRN002B", "RECRN002C"), List.of("RECRN002B-Plico", "CON020-7Z")), + FAIL_AR_ERROR_CAUSE(List.of("CON080", "CON020", "RECRN002A", "RECRN002B", "RECRN002C"), List.of("RECRN002B-Plico", "CON020-7Z")), FAIL_IRREPERIBILE_AR(List.of("CON080", "CON020", "RECRN002D", "RECRN002E", "RECRN002F"), List.of("RECRN002E-Plico", "CON020-7Z")), OK_GIACENZA_AR(List.of("CON080", "CON020", "RECRN010", "RECRN011", "RECRN003A", "RECRN003B", "RECRN003C"), List.of("RECRN003B-AR", "CON020-7Z")), FAIL_GIACENZA_AR(List.of("CON080", "CON020", "RECRN010", "RECRN011", "RECRN004A", "RECRN004B", "RECRN004C"), List.of("RECRN004B-Plico", "CON020-7Z")), diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/TestUtils.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/TestUtils.java index ebc554ef..f16d80fe 100644 --- a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/TestUtils.java +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/TestUtils.java @@ -142,7 +142,7 @@ public static void assertError(PaperTrackingsErrors e, ErrorCategory cat, FlowTh assertNotNull(e.getTrackingId()); assertNotNull(e.getProductType()); assertTrue(e.getDetails().getMessage().contains(msgContains)); - assertNull(e.getDetails().getCause()); + assertNotNull(e.getDetails().getCause()); } @@ -156,7 +156,7 @@ public static void assertSingleError(List errs, ErrorCateg assertNotNull(e.getTrackingId()); assertNotNull(e.getProductType()); assertTrue(e.getDetails().getMessage().contains(msgContains)); - assertNull(e.getDetails().getCause()); + assertNotNull(e.getDetails().getCause()); } public static void assertSingleWarning(List errs, ErrorCategory cat, FlowThrow flow, String msgContains) { diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/HandlerFactory890IT.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/HandlerFactory890IT.java index 530a1b27..81f71ccf 100644 --- a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/HandlerFactory890IT.java +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/HandlerFactory890IT.java @@ -29,6 +29,7 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import static it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.ErrorCause.VALUES_NOT_MATCHING; import static it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.PaperTrackingsState.AWAITING_REFINEMENT; import static it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.PaperTrackingsState.DONE; import static it.pagopa.pn.papertracker.service.handler_step.TestUtils.*; @@ -443,7 +444,7 @@ private void verifyErrors(List errs, TestSequence890Enum s assertEquals(1, errs.size()); PaperTrackingsErrors errors = errs.getFirst(); assertEquals(ErrorCategory.ATTACHMENTS_ERROR, errors.getErrorCategory()); - assertNull(errors.getDetails().getCause()); + assertEquals(VALUES_NOT_MATCHING,errors.getDetails().getCause()); assertEquals(FlowThrow.SEQUENCE_VALIDATION, errors.getFlowThrow()); assertEquals("RECAG007C", errors.getEventThrow()); assertEquals(ErrorType.ERROR, errors.getType()); diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890Test.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890Test.java index b59b3dad..1dea6f05 100644 --- a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890Test.java +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/_890/SequenceValidator890Test.java @@ -19,6 +19,7 @@ import reactor.test.StepVerifier; import java.time.Instant; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -47,7 +48,9 @@ class SequenceValidator890Test { void setUp() { sequenceValidator890 = new SequenceValidator890(paperTrackingsDAO, paperTrackingsErrorsDAO); context = new HandlerContext(); - context.setPaperProgressStatusEvent(new PaperProgressStatusEvent()); + PaperProgressStatusEvent paperProgressStatusEvent = new PaperProgressStatusEvent(); + paperProgressStatusEvent.setStatusDateTime(OffsetDateTime.now()); + context.setPaperProgressStatusEvent(paperProgressStatusEvent); } @Test diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponseIT.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponseIT.java new file mode 100644 index 00000000..ad58b628 --- /dev/null +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckOcrResponseIT.java @@ -0,0 +1,95 @@ +package it.pagopa.pn.papertracker.service.handler_step.generic; + +import com.sngular.apigenerator.asyncapi.business_model.model.event.Data; +import com.sngular.apigenerator.asyncapi.business_model.model.event.OcrDataResultPayload; +import it.pagopa.pn.papertracker.BaseTest; +import it.pagopa.pn.papertracker.middleware.dao.PaperTrackingsDAO; +import it.pagopa.pn.papertracker.middleware.dao.PaperTrackingsErrorsDAO; +import it.pagopa.pn.papertracker.middleware.dao.dynamo.entity.*; +import it.pagopa.pn.papertracker.middleware.queue.consumer.internal.OcrEventHandler; +import it.pagopa.pn.papertracker.model.DocumentTypeEnum; +import it.pagopa.pn.papertracker.model.OcrStatusEnum; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class CheckOcrResponseIT extends BaseTest.WithLocalStack { + + @Autowired + OcrEventHandler ocrEventHandler; + + @Autowired + PaperTrackingsDAO paperTrackingsDAO; + + @Autowired + PaperTrackingsErrorsDAO paperTrackingsErrorsDAO; + + @Test + void checkOcrResponseHandler(){ + OcrDataResultPayload payload = OcrDataResultPayload.builder() + .CommandId("PREPARE#id1#TEST") + .data(Data.builder().description("description").predictedRefinementType(Data.PredictedRefinementType.POST10) + .validationType(Data.ValidationType.AI).validationStatus(Data.ValidationStatus.KO).build()) + .version("version") + .build(); + + String requestId = "PREPARE"; + PaperTrackings paperTrackings = new PaperTrackings(); + paperTrackings.setTrackingId(requestId); + paperTrackings.setProductType(ProductType.AR.getValue()); + paperTrackings.setUnifiedDeliveryDriver("POSTE"); + paperTrackings.setState(PaperTrackingsState.AWAITING_REFINEMENT); + ValidationConfig validationConfig = new ValidationConfig(); + validationConfig.setOcrEnabled(OcrStatusEnum.DRY); + paperTrackings.setValidationConfig(validationConfig); + Instant timestamp = Instant.now(); + Event event1 = buildEvent("id1","RECRN001A", timestamp, timestamp, "REG123", "", null); + Event event2 = buildEvent("id2", "RECRN001B", timestamp, timestamp.plusSeconds(1), "REG123", "", List.of(DocumentTypeEnum.AR.getValue())); + Event event3 = buildEvent("id3", "RECRN001C", timestamp, timestamp.plusSeconds(2), "REG123", "", null); + + + paperTrackings.setEvents(List.of(event1, event2,event3)); + + paperTrackingsDAO.putIfAbsent(paperTrackings).block(); + + Assertions.assertDoesNotThrow(()-> ocrEventHandler.handleOcrMessage(payload)); + PaperTrackingsErrors paperTrackingsErrors = paperTrackingsErrorsDAO.retrieveErrors(paperTrackings.getTrackingId()).blockFirst(); + Assertions.assertNotNull(paperTrackingsErrors); + Object additionalDetailsField = paperTrackingsErrors.getDetails().getAdditionalDetails().get("ocrDataResultPayload"); + if (additionalDetailsField instanceof HashMap map) { + Assertions.assertEquals("description", map.get("description")); + Assertions.assertEquals("POST10", map.get("predictedRefinementType")); + Assertions.assertEquals("ai", map.get("validationType")); + Assertions.assertEquals("KO", map.get("validationStatus")); + } else { + Assertions.fail("Expected additionalDetailsField to be a Map"); + } + } + + private Event buildEvent(String id, String statusCode, Instant statusTimestamp, Instant requestTimestamp, String registeredLetterCode, String deliveryFailureCause, List attachmentTypes) { + Event event = new Event(); + event.setId(id); + event.setAttachments(new ArrayList<>()); + event.setStatusCode(statusCode); + event.setRegisteredLetterCode(registeredLetterCode); + event.setStatusTimestamp(statusTimestamp); + event.setRequestTimestamp(requestTimestamp); + event.setDeliveryFailureCause(deliveryFailureCause); + + if (!CollectionUtils.isEmpty(attachmentTypes)) { + for (String type : attachmentTypes) { + Attachment attachment = new Attachment(); + attachment.setDocumentType(type); + event.getAttachments().add(attachment); + } + } + + return event; + } +} diff --git a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingStateTest.java b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingStateTest.java index 2451bc9e..f88b756b 100644 --- a/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingStateTest.java +++ b/src/test/java/it/pagopa/pn/papertracker/service/handler_step/generic/CheckTrackingStateTest.java @@ -14,6 +14,9 @@ import org.mockito.junit.jupiter.MockitoExtension; import reactor.test.StepVerifier; +import java.time.Instant; +import java.time.OffsetDateTime; + @ExtendWith(MockitoExtension.class) class CheckTrackingStateTest { @@ -25,6 +28,7 @@ class CheckTrackingStateTest { @BeforeEach void setUp() { PaperProgressStatusEvent paperProgressStatusEvent = new PaperProgressStatusEvent(); + paperProgressStatusEvent.setStatusDateTime(OffsetDateTime.now()); PaperTrackings paperTrackings = new PaperTrackings(); Event event = new Event(); event.setId("id");