diff --git a/CHANGELOG.md b/CHANGELOG.md index d93751342..a4d1c5c86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added * Added support for mapping different EhrSupplyType (e.g. NHS prescription, OTC sale) into the Medication Statement Prescribing Agency extension -* Added fallback for Condition.asserter to use the EHRComposition / author / agent field when EHRComposition / participant2 is absent +* Added fallback for Condition.asserter to use the EHRComposition / author / agent field when EHRComposition / participant2 is absent. +* +### Fixed +* Improved error handling in SkeletonProcessingService to throw a meaningful IllegalArgumentException + when a payload node cannot be matched to a skeleton document ID, replacing an uninformative NullPointerException. ## [3.1.3] - 2025-09-19 diff --git a/gp2gp-translator/src/main/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingService.java b/gp2gp-translator/src/main/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingService.java index 48d5582c6..7d26af0c5 100644 --- a/gp2gp-translator/src/main/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingService.java +++ b/gp2gp-translator/src/main/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingService.java @@ -79,6 +79,12 @@ private InboundMessage insertSkeletonIntoInboundMessagePayload(PatientAttachment var payloadXml = xPathService.parseDocumentFromXml(inboundMessage.getPayload()); var valueNodes = xPathService.getNodes(payloadXml, "//*/@*[.='" + skeletonDocumentId + "']/parent::*/parent::*"); var payloadNodeToReplace = valueNodes.item(0); + + if (payloadNodeToReplace == null) { + throw new IllegalArgumentException( + "Could not find payload node matching skeleton document ID: " + skeletonDocumentId + ); + } var payloadNodeToReplaceParent = payloadNodeToReplace.getParentNode(); var skeletonExtractNodes = skeletonExtractDocument.getElementsByTagName("*"); diff --git a/gp2gp-translator/src/test/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingServiceTests.java b/gp2gp-translator/src/test/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingServiceTests.java index bfc2df0bc..186126ba9 100644 --- a/gp2gp-translator/src/test/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingServiceTests.java +++ b/gp2gp-translator/src/test/java/uk/nhs/adaptors/pss/translator/service/SkeletonProcessingServiceTests.java @@ -210,6 +210,30 @@ void When_SkeletonAsSectionMessageAndEBXMLSkeletonReferenceIsEmpty_Expect_Illega inboundMessage, migrationRequest.getConversationId())); } + @Test + void When_SkeletonAsSectionMessageAndPayloadNodeNotFound_Expect_IllegalArgumentException() + throws SAXException { + var inboundMessage = new InboundMessage(); + var attachmentLog = createSkeletonPatientAttachmentLog(); + + inboundMessage.setPayload(readInboundMessagePayloadFromFile()); + inboundMessage.setEbXML(readInboundMessageEbXmlFromFile()); + + var reference = new EbxmlReference("First instance is always a payload", "mid:1", "docId"); + var ebXmlAttachments = List.of(reference); + var fileAsBytes = readInboundMessageSkeletonPayloadFromFile().getBytes(StandardCharsets.UTF_8); + + when(attachmentHandlerService.getAttachment(any(), any())).thenReturn(fileAsBytes); + when(xmlParseUtilService.getEbxmlAttachmentsData(any())).thenReturn(ebXmlAttachments); + when(xPathService.parseDocumentFromXml(any())).thenReturn(ebXmlDocument); + when(xPathService.getNodes(any(), any())).thenReturn(nodeList); + when(nodeList.item(0)).thenReturn(null); + + assertThrows(IllegalArgumentException.class, () -> + skeletonProcessingService.updateInboundMessageWithSkeleton(attachmentLog, + inboundMessage, migrationRequest.getConversationId())); + } + PatientAttachmentLog createSkeletonPatientAttachmentLog() { return PatientAttachmentLog.builder()