Skip to content

Commit 676774a

Browse files
authored
NIAD-1573: New reference validation for condition references. (#1335)
* Added method for testing if references exist and fixed tests. * Fixed Checkstyle warnings. * Updated the changelog with the latest changes. * Added Logging for reference checking
1 parent 4b2daaf commit 676774a

File tree

9 files changed

+701
-14
lines changed

9 files changed

+701
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [Unreleased]
88

99
### Added
10+
* The GP2GP Adaptor now validates references inside condition objects to test whether they actually exist. Otherwise, the bundle is rejected.
1011
* The GP2GP Adaptor now adds the EhrComposition / confidentialityCode field when Encounter.meta.security contains NOPAT security entry
1112
* The GP2GP Adaptor now populates the ObservationStatement / confidentialityCode field when the .meta.security field of an Uncategorized Data Observation contains NOPAT
1213
* When List.meta.security field contains NOPAT, the GP2GP Adaptor will now populate the CompoundStatement.confidentialityCode

service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/mapper/ConditionLinkSetMapper.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public String mapConditionToLinkSet(Condition condition, boolean isNested) {
6565
.isNested(isNested)
6666
.linkSetId(idMapper.getOrNew(ResourceType.Condition, condition.getIdElement()));
6767

68+
testForValidReferences(condition);
69+
6870
buildEffectiveTimeLow(condition).ifPresent(builder::effectiveTimeLow);
6971
buildEffectiveTimeHigh(condition).ifPresent(builder::effectiveTimeHigh);
7072
buildAvailabilityTime(condition).ifPresent(builder::availabilityTime);
@@ -104,6 +106,21 @@ public String mapConditionToLinkSet(Condition condition, boolean isNested) {
104106
return TemplateUtils.fillTemplate(OBSERVATION_STATEMENT_TEMPLATE, builder.build());
105107
}
106108

109+
public void testForValidReferences(Condition condition) {
110+
for (Extension extension : condition.getExtension()) {
111+
if (!(extension.getValue() instanceof Reference)) {
112+
return;
113+
}
114+
IdType idType = (IdType) ((Reference) extension.getValue()).getReferenceElement();
115+
try {
116+
messageContext.getInputBundleHolder().getResource(idType);
117+
} catch (Exception e) {
118+
LOGGER.warn("Bundle mapping aborted. Reason: " + e.getMessage());
119+
throw e;
120+
}
121+
}
122+
}
123+
107124
private void setQualifierProperties(ConditionLinkSetMapperParameters.ConditionLinkSetMapperParametersBuilder builder,
108125
String qualifier) {
109126
builder.qualifierDisplay(qualifier);

service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/mapper/ConditionLinkSetMapperTest.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import static org.mockito.ArgumentMatchers.any;
4141
import static org.mockito.Mockito.lenient;
4242
import static org.mockito.Mockito.when;
43+
import static org.mockito.Mockito.spy;
4344
import static uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility.NOPAT;
4445
import static uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility.NOPAT_HL7_CONFIDENTIALITY_CODE;
4546
import static uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility.NOSCRUB;
@@ -126,6 +127,8 @@ class ConditionLinkSetMapperTest {
126127
private ArgumentCaptor<Condition> conditionArgumentCaptor;
127128

128129
private InputBundle inputBundle;
130+
131+
private ConditionLinkSetMapper conditionLinkSetMapperSpy;
129132
private ConditionLinkSetMapper conditionLinkSetMapper;
130133

131134
@BeforeEach
@@ -155,6 +158,10 @@ void setUp() {
155158

156159
conditionLinkSetMapper = new ConditionLinkSetMapper(messageContext, randomIdGeneratorService, codeableConceptCdMapper,
157160
new ParticipantMapper(), confidentialityService);
161+
162+
conditionLinkSetMapperSpy = spy(conditionLinkSetMapper);
163+
164+
lenient().doNothing().when(conditionLinkSetMapperSpy).testForValidReferences(any());
158165
}
159166

160167
@AfterEach
@@ -170,15 +177,15 @@ void When_MappingParsedConditionWithoutMappedAgent_Expect_EhrMapperException() {
170177
when(agentDirectory.getAgentId(any(Reference.class)))
171178
.thenThrow(propagatedException);
172179

173-
assertThatThrownBy(() -> conditionLinkSetMapper.mapConditionToLinkSet(condition, false))
180+
assertThatThrownBy(() -> conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, false))
174181
.isSameAs(propagatedException);
175182
}
176183

177184
@Test
178185
void When_MappingParsedConditionWithAsserterNotPractitioner_Expect_EhrMapperException() {
179186
final Condition condition = getConditionResourceFromJson(INPUT_JSON_ASSERTER_NOT_PRACTITIONER);
180187

181-
assertThatThrownBy(() -> conditionLinkSetMapper.mapConditionToLinkSet(condition, false))
188+
assertThatThrownBy(() -> conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, false))
182189
.isExactlyInstanceOf(EhrMapperException.class)
183190
.hasMessage("Condition.asserter must be a Practitioner");
184191
}
@@ -189,7 +196,7 @@ void When_MappingParsedCondition_With_RealProblem_Expect_LinkSetXml(String condi
189196
final Condition condition = getConditionResourceFromJson(conditionJson);
190197
final String expectedXml = getXmlStringFromFile(outputXml);
191198

192-
final String actualXml = conditionLinkSetMapper.mapConditionToLinkSet(condition, isNested);
199+
final String actualXml = conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, isNested);
193200

194201
assertThat(actualXml).isEqualTo(expectedXml);
195202
}
@@ -201,7 +208,7 @@ void When_MappingParsedCondition_With_ActualProblemContentAndIsObservation_Expec
201208
final Condition condition = getConditionResourceFromJson(conditionJson);
202209
final String expectedXml = getXmlStringFromFile(outputXml);
203210

204-
final String actualXml = conditionLinkSetMapper.mapConditionToLinkSet(condition, isNested);
211+
final String actualXml = conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, isNested);
205212

206213
assertThat(actualXml).isEqualTo(expectedXml);
207214
}
@@ -211,7 +218,7 @@ void When_MappingParsedCondition_With_NoRelatedClinicalContent_Expect_LinkSetXml
211218
final Condition condition = getConditionResourceFromJson(INPUT_JSON_NO_RELATED_CLINICAL_CONTENT);
212219
final String expectedXml = getXmlStringFromFile(OUTPUT_XML_WITH_NO_RELATED_CLINICAL_CONTENT);
213220

214-
final String actualXml = conditionLinkSetMapper.mapConditionToLinkSet(condition, false);
221+
final String actualXml = conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, false);
215222

216223
assertThat(actualXml).isEqualTo(expectedXml);
217224
}
@@ -223,7 +230,7 @@ void When_MappingParsedCondition_With_NonExistentReferenceInRelatedClinicalConte
223230
when(messageContext.getInputBundleHolder()).thenReturn(inputBundle);
224231

225232
// TODO: workaround for NIAD-1409 should throw an exception but demonstrator include invalid references
226-
assumeThatThrownBy(() -> conditionLinkSetMapper.mapConditionToLinkSet(parsedObservation, false))
233+
assumeThatThrownBy(() -> conditionLinkSetMapperSpy.mapConditionToLinkSet(parsedObservation, false))
227234
.isExactlyInstanceOf(EhrMapperException.class)
228235
.hasMessage("Could not resolve Condition Related Medical Content reference");
229236
}
@@ -232,7 +239,7 @@ void When_MappingParsedCondition_With_NonExistentReferenceInRelatedClinicalConte
232239
void When_MappingParsedConditionCodeIsMissing_Expect_MapperException() {
233240
final Condition condition = getConditionResourceFromJson(INPUT_JSON_MISSING_CONDITION_CODE);
234241

235-
assertThatThrownBy(() -> conditionLinkSetMapper.mapConditionToLinkSet(condition, false))
242+
assertThatThrownBy(() -> conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, false))
236243
.isExactlyInstanceOf(EhrMapperException.class)
237244
.hasMessage("Condition code not present");
238245
}
@@ -242,7 +249,7 @@ void When_MappingCondition_With_SuppressedMedReqAsRelatedClinicalContent_Expect_
242249
final Condition condition = getConditionResourceFromJson(INPUT_JSON_SUPPRESSED_RELATED_MEDICATION_REQUEST);
243250
final String expectedXml = getXmlStringFromFile(OUTPUT_XML_SUPPRESSED_RELATED_MEDICATION_REQUEST);
244251

245-
final String actualXml = conditionLinkSetMapper.mapConditionToLinkSet(condition, false);
252+
final String actualXml = conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, false);
246253

247254
XMLAssert.assertXMLEqual(expectedXml, actualXml);
248255
}
@@ -258,7 +265,7 @@ void When_MappingCondition_With_NopatMetaSecurity_Expect_ConfidentialityCodeInBo
258265
when(confidentialityService.generateConfidentialityCode(conditionArgumentCaptor.capture()))
259266
.thenReturn(Optional.of(NOPAT_HL7_CONFIDENTIALITY_CODE));
260267

261-
final String actualXml = wrapXmlInRootElement(conditionLinkSetMapper
268+
final String actualXml = wrapXmlInRootElement(conditionLinkSetMapperSpy
262269
.mapConditionToLinkSet(condition, false));
263270
final String conditionSecurityCode = ConfidentialityCodeUtility
264271
.getSecurityCodeFromResource(conditionArgumentCaptor.getValue());
@@ -278,7 +285,7 @@ void When_MappingCondition_With_NoscrubMetaSecurity_Expect_ConfidentialityCodeNo
278285
when(confidentialityService.generateConfidentialityCode(conditionArgumentCaptor.capture()))
279286
.thenReturn(Optional.empty());
280287

281-
final String actualXml = conditionLinkSetMapper.mapConditionToLinkSet(condition, false);
288+
final String actualXml = conditionLinkSetMapperSpy.mapConditionToLinkSet(condition, false);
282289
final String conditionSecurityCode = ConfidentialityCodeUtility
283290
.getSecurityCodeFromResource(conditionArgumentCaptor.getValue());
284291

service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/mapper/EncounterComponentsMapperTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class EncounterComponentsMapperTest {
6464
private static final String INPUT_BUNDLE_WITH_RESOURCES_NOT_IN_BUNDLE = TEST_DIRECTORY + "input-bundle-7.json";
6565
private static final String INPUT_BUNDLE_WITH_NON_TOPIC_CONSULTATION_LIST_ENTRY = TEST_DIRECTORY + "input-bundle-8.json";
6666
private static final String INPUT_BUNDLE_WITH_UNSUPPORTED_RESOURCES = TEST_DIRECTORY + "input-bundle-9-unsupported-resource.json";
67+
private static final String INPUT_BUNDLE_WITH_INVALID_REFERENCE = TEST_DIRECTORY + "input-bundle-9-missing-reference.json";
6768
private static final String INPUT_BUNDLE_WITH_IGNORED_RESOURCE = TEST_DIRECTORY + "input-bundle-10-ignored-resource.json";
6869
private static final String INPUT_BUNDLE_WITH_META_SECURITY = TEST_DIRECTORY + "input-bundle-10-with-meta.json";
6970
private static final String INPUT_BUNDLE_WITH_NON_CATEGORY_TOPIC_LIST_ENTRY = TEST_DIRECTORY
@@ -315,6 +316,16 @@ void When_MappingEncounterUnsupportedResource_Expect_ExceptionThrown() {
315316
.isInstanceOf(EhrMapperException.class);
316317
}
317318

319+
@Test
320+
void When_MappingEncounterInvalidReference_Expect_ExceptionThrown() {
321+
var bundle = initializeMessageContext(INPUT_BUNDLE_WITH_INVALID_REFERENCE);
322+
var encounter = extractEncounter(bundle);
323+
324+
assertThatThrownBy(() -> encounterComponentsMapper.mapComponents(encounter))
325+
.hasMessageContaining("Resource not found: Observation/7E277DF1-6F1C-47CD-84F7-E9B7BF4105DB")
326+
.isInstanceOf(EhrMapperException.class);
327+
}
328+
318329
@Test
319330
void When_MappingConsultation_WithNonTopicList_Expect_ExceptionThrown() {
320331
var bundle = initializeMessageContext(INPUT_BUNDLE_WITH_NON_TOPIC_CONSULTATION_LIST_ENTRY);

service/src/test/resources/ehr/mapper/encountercomponents/input-bundle-1.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@
823823
{
824824
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-ActualProblem-1",
825825
"valueReference": {
826-
"reference": "Observation/7E277DF1-6F1C-47CD-84F7-E9B7BF4105DB"
826+
"reference": "Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2"
827827
}
828828
},
829829
{

service/src/test/resources/ehr/mapper/encountercomponents/input-bundle-10-ignored-resource.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@
833833
{
834834
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-ActualProblem-1",
835835
"valueReference": {
836-
"reference": "Observation/7E277DF1-6F1C-47CD-84F7-E9B7BF4105DB"
836+
"reference": "Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2"
837837
}
838838
},
839839
{

service/src/test/resources/ehr/mapper/encountercomponents/input-bundle-10-with-meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@
838838
{
839839
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-ActualProblem-1",
840840
"valueReference": {
841-
"reference": "Observation/7E277DF1-6F1C-47CD-84F7-E9B7BF4105DB"
841+
"reference": "Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2"
842842
}
843843
},
844844
{

0 commit comments

Comments
 (0)