Skip to content

Commit feb411b

Browse files
authored
Niad-2840: Identifies no acknowledgment after 8 Days (#841)
* initial changes * covering code with tests * checkstyle * changelog * fixing mutation tests * fixing integration tests * addressing PR comments * code refactoring * adding test coverage * class renaming to avoid clashes
1 parent d903a92 commit feb411b

File tree

8 files changed

+211
-44
lines changed

8 files changed

+211
-44
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
* Added a scheduled delay checker to update EhrExtract to "Integration Failure" state if sentAt exceeds 192 hours and no acknowledgment is received.
10+
911
### Added
1012

1113
* When mapping `AllergyIntolerances` which contain a `NOPAT` `meta.security` tag the resultant XML for that resource

service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrContinueTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
import uk.nhs.adaptors.gp2gp.ehr.model.EhrExtractStatus;
1313
import uk.nhs.adaptors.gp2gp.ehr.request.EhrExtractRequestHandler;
1414
import uk.nhs.adaptors.gp2gp.mhs.InvalidInboundMessageException;
15-
import uk.nhs.adaptors.gp2gp.mhs.exception.NonExistingInteractionIdException;
15+
import uk.nhs.adaptors.gp2gp.mhs.exception.UnrecognisedInteractionIdException;
1616
import uk.nhs.adaptors.gp2gp.testcontainers.ActiveMQExtension;
1717
import uk.nhs.adaptors.gp2gp.testcontainers.MongoDBExtension;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
2021
import static org.junit.jupiter.api.Assertions.assertThrows;
2122
import static org.mockito.ArgumentMatchers.any;
2223
import static org.mockito.ArgumentMatchers.argThat;
@@ -35,7 +36,6 @@ public class EhrContinueTest {
3536

3637
private static final String CONTINUE_ACKNOWLEDGEMENT = "Continue Acknowledgement";
3738

38-
3939
@Autowired
4040
private EhrExtractRequestHandler ehrExtractRequestHandler;
4141
@Autowired
@@ -59,7 +59,7 @@ public void When_EhrContinueIsValid_Expect_TaskDispatcherCalledWithSameValues()
5959
}));
6060

6161
var ehrExtract = ehrExtractStatusRepository.findByConversationId(ehrExtractStatus.getConversationId());
62-
assertThat(ehrExtract.get().getEhrContinue().getReceived()).isNotNull();
62+
assertNotNull(ehrExtract.get().getEhrContinue().getReceived());
6363
}
6464

6565
@Test
@@ -79,11 +79,11 @@ public void When_EhrContinueHasNoContinueAcknowledgement_Expect_InvalidInboundEx
7979
public void When_EhrContinueThrowsException_Expect_EhrExtractStatusNotUpdated() {
8080
String conversationId = randomIdGeneratorService.createNewId();
8181

82-
Exception exception = assertThrows(NonExistingInteractionIdException.class,
83-
() -> ehrExtractRequestHandler.handleContinue(conversationId, CONTINUE_ACKNOWLEDGEMENT));
82+
Exception exception = assertThrows(UnrecognisedInteractionIdException.class,
83+
() -> ehrExtractRequestHandler.handleContinue(conversationId, CONTINUE_ACKNOWLEDGEMENT));
8484

85-
assertThat(exception.getMessage()).isEqualTo("Received a Continue message that is not recognised with conversation_id: '"
86-
+ conversationId + "'");
85+
assertThat(exception.getMessage()).isEqualTo("Received an unrecognized Continue message with conversation_id: "
86+
+ conversationId);
8787
verify(taskDispatcher, never()).createTask(any());
8888
}
8989

service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java renamed to service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
@ExtendWith({MongoDBExtension.class})
4848
@DirtiesContext
4949
@SpringBootTest
50-
public class EhrExtractStatusServiceTest {
50+
public class EhrExtractStatusServiceIT {
5151
private static final Instant NOW = Instant.now();
5252
private static final Instant FIVE_DAYS_AGO = NOW.minus(Duration.ofDays(5));
5353
private static final int DEFAULT_CONTENT_LENGTH = 244;

service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusService.java

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.springframework.util.CollectionUtils.isEmpty;
66
import static org.springframework.util.CollectionUtils.newHashMap;
77

8+
import java.time.Duration;
89
import java.time.Instant;
910
import java.util.Collections;
1011
import java.util.List;
@@ -32,7 +33,7 @@
3233
import uk.nhs.adaptors.gp2gp.ehr.model.EhrExtractStatus.EhrReceivedAcknowledgement.ErrorDetails;
3334
import uk.nhs.adaptors.gp2gp.gpc.GetGpcStructuredTaskDefinition;
3435
import uk.nhs.adaptors.gp2gp.mhs.exception.MessageOutOfOrderException;
35-
import uk.nhs.adaptors.gp2gp.mhs.exception.NonExistingInteractionIdException;
36+
import uk.nhs.adaptors.gp2gp.mhs.exception.UnrecognisedInteractionIdException;
3637

3738
@Service
3839
@Slf4j
@@ -64,7 +65,6 @@ public class EhrExtractStatusService {
6465
private static final String ROOT_ID = "rootId";
6566
private static final String CONVERSATION_CLOSED = "conversationClosed";
6667
private static final String MESSAGE_REF = "messageRef";
67-
private static final String EHR_EXTRACT_MESSAGE_REF = "ehrExtractMessageRef";
6868
private static final String ERRORS = "errors";
6969
private static final String ERROR = "error";
7070
private static final String SENT_TO_MHS = "sentToMhs";
@@ -114,6 +114,9 @@ public class EhrExtractStatusService {
114114
private static final String CONTENT_TYPE_PLACEHOLDER = "CONTENT_TYPE_PLACEHOLDER_ID=";
115115
private static final String FILENAME_TYPE_PLACEHOLDER = "FILENAME_PLACEHOLDER_ID=";
116116
private static final String ACKS_SET = ACK_HISTORY + DOT + ACKS;
117+
public static final int EHR_EXTRACT_SENT_DAYS_LIMIT = 8;
118+
private static final String REASON_ERROR_CODE = "04";
119+
public static final String REASON_ERROR_MESSAGE = "The acknowledgement has been received after 8 days";
117120

118121
private final MongoTemplate mongoTemplate;
119122
private final EhrExtractStatusRepository ehrExtractStatusRepository;
@@ -169,8 +172,8 @@ public Map<String, String> fetchDocumentObjectNameAndSize(String conversationId)
169172
}
170173

171174
public EhrExtractStatus updateEhrExtractStatusAccessStructured(
172-
GetGpcStructuredTaskDefinition structuredTaskDefinition, String structuredRecordJsonFilename
173-
) {
175+
GetGpcStructuredTaskDefinition structuredTaskDefinition, String structuredRecordJsonFilename) {
176+
174177
Query query = createQueryForConversationId(structuredTaskDefinition.getConversationId());
175178
Instant now = Instant.now();
176179

@@ -196,8 +199,8 @@ public EhrExtractStatus updateEhrExtractStatusAccessDocument(
196199
String storagePath,
197200
int base64ContentLength,
198201
String errorMessage,
199-
String filename
200-
) {
202+
String filename) {
203+
201204
Query query = new Query();
202205
query.addCriteria(Criteria
203206
.where(CONVERSATION_ID).is(documentTaskDefinition.getConversationId())
@@ -228,8 +231,8 @@ public EhrExtractStatus updateEhrExtractStatusAccessDocument(
228231

229232
public EhrExtractStatus updateEhrExtractStatusCore(
230233
SendEhrExtractCoreTaskDefinition sendEhrExtractCoreTaskDefinition,
231-
Instant requestSentAt
232-
) {
234+
Instant requestSentAt) {
235+
233236
Query query = createQueryForConversationId(sendEhrExtractCoreTaskDefinition.getConversationId());
234237

235238
Update update = createUpdateWithUpdatedAt();
@@ -250,8 +253,8 @@ public EhrExtractStatus updateEhrExtractStatusCore(
250253

251254
public void updateEhrExtractStatusCorePending(
252255
SendEhrExtractCoreTaskDefinition sendEhrExtractCoreTaskDefinition,
253-
Instant requestSentAt
254-
) {
256+
Instant requestSentAt) {
257+
255258
Query query = createQueryForConversationId(sendEhrExtractCoreTaskDefinition.getConversationId());
256259

257260
Update update = createUpdateWithUpdatedAt();
@@ -267,6 +270,7 @@ public void updateEhrExtractStatusCorePending(
267270
}
268271

269272
public Optional<EhrExtractStatus> updateEhrExtractStatusContinue(String conversationId) {
273+
270274
var isDuplicate = checkForContinueOutOfOrderAndDuplicate(conversationId);
271275
if (!isDuplicate) {
272276
Query query = createQueryForConversationId(conversationId);
@@ -305,6 +309,14 @@ public void updateEhrExtractStatusAck(String conversationId, EhrReceivedAcknowle
305309
return;
306310
}
307311

312+
if (hasAcknowledgementExceededEightDays(conversationId, ack.getReceived())) {
313+
updateEhrExtractStatusError(conversationId,
314+
REASON_ERROR_CODE,
315+
REASON_ERROR_MESSAGE,
316+
this.getClass().getSimpleName());
317+
return;
318+
}
319+
308320
Query query = createQueryForConversationId(conversationId);
309321

310322
Update update = createUpdateWithUpdatedAt();
@@ -356,8 +368,7 @@ public void saveAckForConversation(String conversationId, EhrReceivedAcknowledge
356368

357369
public void updateEhrExtractStatusAccessDocumentDocumentReferences(
358370
String conversationId,
359-
List<EhrExtractStatus.GpcDocument> documents
360-
) {
371+
List<EhrExtractStatus.GpcDocument> documents) {
361372
Query query = createQueryForConversationId(conversationId);
362373

363374
Update.AddToSetBuilder updateBuilder = createUpdateWithUpdatedAt().addToSet(GPC_DOCUMENTS);
@@ -371,8 +382,7 @@ private void getEhrExtractStatus(
371382
Query query,
372383
List<EhrExtractStatus.GpcDocument> docs,
373384
Update.AddToSetBuilder updateBuilder,
374-
FindAndModifyOptions returningUpdatedRecordOption
375-
) {
385+
FindAndModifyOptions returningUpdatedRecordOption) {
376386
Update update = updateBuilder.each(docs);
377387

378388
EhrExtractStatus ehrExtractStatus = mongoTemplate.findAndModify(query,
@@ -402,8 +412,8 @@ public void updateEhrExtractStatusAcknowledgement(
402412
public void updateEhrExtractStatusAcknowledgement(
403413
SendAcknowledgementTaskDefinition taskDefinition,
404414
String ackMessageId,
405-
String updatedAt
406-
) {
415+
String updatedAt) {
416+
407417
Update update = createUpdateWithUpdatedAt();
408418
update.set(ACK_PENDING_TASK_ID_PATH, taskDefinition.getTaskId());
409419
update.set(ACK_PENDING_MESSAGE_ID_PATH, ackMessageId);
@@ -417,8 +427,8 @@ public EhrExtractStatus updateEhrExtractStatusError(
417427
String conversationId,
418428
String errorCode,
419429
String errorMessage,
420-
String taskType
421-
) {
430+
String taskType) {
431+
422432
Update update = createUpdateWithUpdatedAt();
423433
Instant now = Instant.now();
424434
update.set(ERROR_OCCURRED_AT_PATH, now);
@@ -571,23 +581,29 @@ private Update createUpdateWithUpdatedAt() {
571581
return update;
572582
}
573583

574-
private boolean isEhrStatusWaitingForFinalAck(String conversationId) {
575-
var ehrExtractStatus = ehrExtractStatusRepository.findByConversationId(conversationId)
576-
.orElseThrow(() -> new NonExistingInteractionIdException("ACK", conversationId));
584+
protected boolean isEhrStatusWaitingForFinalAck(String conversationId) {
585+
var ehrExtractStatus = fetchEhrExtractStatus(conversationId, "ACK");
577586

578587
return ehrExtractStatus.getAckPending() != null;
579588
}
580589

581-
private boolean hasFinalAckBeenReceived(String conversationId) {
582-
var ehrExtractStatus = ehrExtractStatusRepository.findByConversationId(conversationId)
583-
.orElseThrow(() -> new NonExistingInteractionIdException("ACK", conversationId));
590+
protected boolean hasFinalAckBeenReceived(String conversationId) {
591+
var ehrExtractStatus = fetchEhrExtractStatus(conversationId, "ACK");
584592

585593
return ehrExtractStatus.getEhrReceivedAcknowledgement() != null;
586594
}
587595

596+
private boolean hasAcknowledgementExceededEightDays(String conversationId, Instant ackReceivedTimestamp) {
597+
var ehrExtractStatus = fetchEhrExtractStatus(conversationId, "ACK");
598+
599+
Duration duration = Duration.between(ehrExtractStatus.getUpdatedAt(), ackReceivedTimestamp);
600+
long daysSinceLastUpdate = duration.toDays();
601+
602+
return daysSinceLastUpdate > EHR_EXTRACT_SENT_DAYS_LIMIT;
603+
}
604+
588605
private boolean checkForContinueOutOfOrderAndDuplicate(String conversationId) {
589-
var ehrExtractStatus = ehrExtractStatusRepository.findByConversationId(conversationId)
590-
.orElseThrow(() -> new NonExistingInteractionIdException("Continue", conversationId));
606+
var ehrExtractStatus = fetchEhrExtractStatus(conversationId, "Continue");
591607

592608
if (ehrExtractStatus.getEhrExtractCorePending() == null) {
593609
throw new MessageOutOfOrderException("Continue", conversationId);
@@ -601,4 +617,9 @@ private boolean checkForContinueOutOfOrderAndDuplicate(String conversationId) {
601617

602618
return false;
603619
}
620+
621+
private EhrExtractStatus fetchEhrExtractStatus(String conversationId, String messageType) {
622+
return ehrExtractStatusRepository.findByConversationId(conversationId)
623+
.orElseThrow(() -> new UnrecognisedInteractionIdException(messageType, conversationId));
624+
}
604625
}

service/src/main/java/uk/nhs/adaptors/gp2gp/mhs/InboundMessageHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import uk.nhs.adaptors.gp2gp.common.service.XPathService;
1717
import uk.nhs.adaptors.gp2gp.ehr.request.EhrExtractRequestHandler;
1818
import uk.nhs.adaptors.gp2gp.mhs.exception.MessageOutOfOrderException;
19-
import uk.nhs.adaptors.gp2gp.mhs.exception.NonExistingInteractionIdException;
19+
import uk.nhs.adaptors.gp2gp.mhs.exception.UnrecognisedInteractionIdException;
2020
import uk.nhs.adaptors.gp2gp.mhs.exception.UnsupportedInteractionException;
2121

2222
import jakarta.jms.JMSException;
@@ -63,7 +63,7 @@ public boolean handle(Message message) throws DataAccessResourceFailureException
6363
}
6464
return true;
6565

66-
} catch (MessageOutOfOrderException | NonExistingInteractionIdException | UnsupportedInteractionException e) {
66+
} catch (MessageOutOfOrderException | UnrecognisedInteractionIdException | UnsupportedInteractionException e) {
6767
LOGGER.error("An error occurred while handing MHS inbound message id: {}", messageID, e);
6868
return false;
6969
} catch (DataAccessResourceFailureException e) {

service/src/main/java/uk/nhs/adaptors/gp2gp/mhs/exception/NonExistingInteractionIdException.java

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package uk.nhs.adaptors.gp2gp.mhs.exception;
2+
3+
public class UnrecognisedInteractionIdException extends RuntimeException {
4+
5+
private static final String EXCEPTION_MESSAGE = "Received an unrecognized %s message with conversation_id: %s";
6+
7+
public UnrecognisedInteractionIdException(String messageType, String conversationId) {
8+
super(EXCEPTION_MESSAGE.formatted(messageType, conversationId));
9+
}
10+
}

0 commit comments

Comments
 (0)