Skip to content

Commit ecf9ca4

Browse files
Started implementation of new validation feature.
1 parent fb4bbde commit ecf9ca4

File tree

4 files changed

+114
-93
lines changed

4 files changed

+114
-93
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import uk.nhs.adaptors.gp2gp.gpc.GetGpcStructuredTaskDefinition;
2323

2424
import uk.nhs.adaptors.gp2gp.ehr.exception.EhrValidationException;
25+
import uk.nhs.adaptors.gp2gp.transformjsontoxmltool.XmlSchemaValidator;
2526

2627
import java.util.List;
2728
import java.util.Optional;
@@ -41,6 +42,7 @@ public class EhrExtractMapper {
4142
private final NonConsultationResourceMapper nonConsultationResourceMapper;
4243
private final AgentDirectoryMapper agentDirectoryMapper;
4344
private final MessageContext messageContext;
45+
private final XmlSchemaValidator xmlSchemaValidator;
4446

4547
@Value("${gp2gp.gpc.overrideNhsNumber}")
4648
private String overrideNhsNumber;
@@ -55,6 +57,18 @@ public EhrExtractTemplateParameters mapBundleToEhrFhirExtractParams(
5557

5658
var encounters = EncounterExtractor.extractEncounterReferencesFromEncounterList(bundle);
5759
var mappedComponents = mapEncounterToEhrComponents(encounters);
60+
61+
String ehrExtractXml = mapEhrExtractToXml(ehrExtractTemplateParameters);
62+
63+
try {
64+
xmlSchemaValidator.validateOutputToXmlSchema(bundle.getId(), ehrExtractXml);
65+
} catch (RuntimeException e) {
66+
final String message = "XML Schema validation failed";
67+
LOGGER.error(message, e);
68+
throw new RuntimeException(message, e);
69+
}
70+
71+
5872
mappedComponents.addAll(nonConsultationResourceMapper.mapRemainingResourcesToEhrCompositions(bundle));
5973
ehrExtractTemplateParameters.setComponents(mappedComponents);
6074

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

Lines changed: 68 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.mockito.junit.jupiter.MockitoExtension;
1717
import org.mockito.junit.jupiter.MockitoSettings;
1818
import org.mockito.quality.Strictness;
19+
import uk.nhs.adaptors.gp2gp.common.configuration.RedactionsContext;
1920
import uk.nhs.adaptors.gp2gp.common.service.ConfidentialityService;
2021
import uk.nhs.adaptors.gp2gp.common.service.FhirParseService;
2122
import uk.nhs.adaptors.gp2gp.common.service.RandomIdGeneratorService;
@@ -26,6 +27,7 @@
2627
import uk.nhs.adaptors.gp2gp.ehr.mapper.parameters.EhrExtractTemplateParameters;
2728
import uk.nhs.adaptors.gp2gp.ehr.utils.BloodPressureValidator;
2829
import uk.nhs.adaptors.gp2gp.gpc.GetGpcStructuredTaskDefinition;
30+
import uk.nhs.adaptors.gp2gp.transformjsontoxmltool.XmlSchemaValidator;
2931
import uk.nhs.adaptors.gp2gp.utils.CodeableConceptMapperMockUtil;
3032
import uk.nhs.adaptors.gp2gp.utils.ResourceTestFileUtils;
3133

@@ -36,8 +38,7 @@
3638
import static org.assertj.core.api.Assertions.assertThat;
3739
import static org.mockito.ArgumentMatchers.any;
3840
import static org.mockito.ArgumentMatchers.anyString;
39-
import static org.mockito.Mockito.lenient;
40-
import static org.mockito.Mockito.when;
41+
import static org.mockito.Mockito.*;
4142

4243
@ExtendWith(MockitoExtension.class)
4344
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -109,123 +110,100 @@ class EhrExtractMapperComponentTest {
109110
@BeforeEach
110111
public void setUp() {
111112
getGpcStructuredTaskDefinition = GetGpcStructuredTaskDefinition.builder()
112-
.nhsNumber(TEST_NHS_NUMBER)
113-
.conversationId(TEST_CONVERSATION_ID)
114-
.requestId(TEST_REQUEST_ID)
115-
.fromOdsCode(TEST_FROM_ODS_CODE)
116-
.toOdsCode(TEST_TO_ODS_CODE)
117-
.build();
113+
.nhsNumber(TEST_NHS_NUMBER)
114+
.conversationId(TEST_CONVERSATION_ID)
115+
.requestId(TEST_REQUEST_ID)
116+
.fromOdsCode(TEST_FROM_ODS_CODE)
117+
.toOdsCode(TEST_TO_ODS_CODE)
118+
.build();
118119

119120
when(randomIdGeneratorService.createNewId()).thenReturn(TEST_ID_1, TEST_ID_2, TEST_ID_3);
120121
lenient().when(randomIdGeneratorService.createNewOrUseExistingUUID(anyString()))
121-
.thenReturn(TEST_ID_3);
122+
.thenReturn(TEST_ID_3);
122123

123124
when(timestampService.now()).thenReturn(Instant.parse(TEST_DATE_TIME));
124-
when(codeableConceptCdMapper.mapCodeableConceptToCd(any(CodeableConcept.class)))
125-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
126-
when(codeableConceptCdMapper.mapCodeableConceptForMedication(any(CodeableConcept.class)))
127-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
128-
when(codeableConceptCdMapper.mapCodeableConceptToCdForTransformedActualProblemHeader(any(CodeableConcept.class)))
129-
.thenReturn(CodeableConceptMapperMockUtil.ACTUAL_PROBLEM_CODE);
130-
when(codeableConceptCdMapper.mapToNullFlavorCodeableConcept(any(CodeableConcept.class)))
131-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
132-
when(codeableConceptCdMapper.mapCodeableConceptToCdForAllergy(any(CodeableConcept.class),
133-
any(AllergyIntolerance.AllergyIntoleranceClinicalStatus.class)))
134-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
135-
when(codeableConceptCdMapper.mapToNullFlavorCodeableConceptForAllergy(any(CodeableConcept.class),
136-
any(AllergyIntolerance.AllergyIntoleranceClinicalStatus.class)))
137-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
138-
when(codeableConceptCdMapper.getDisplayFromCodeableConcept(any(CodeableConcept.class)))
139-
.thenCallRealMethod();
140-
when(codeableConceptCdMapper.mapCodeableConceptToCdForBloodPressure(any(CodeableConcept.class)))
141-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
142-
when(codeableConceptCdMapper.mapToCdForTopic(any(CodeableConcept.class)))
143-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
144-
when(codeableConceptCdMapper.mapToCdForTopic(any(CodeableConcept.class), any(String.class)))
145-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
146-
when(codeableConceptCdMapper.mapToCdForTopic(any(String.class)))
147-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
148-
when(codeableConceptCdMapper.getCdForTopic())
149-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
150-
when(codeableConceptCdMapper.mapToCdForCategory(any(String.class)))
151-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
152-
when(codeableConceptCdMapper.getCdForCategory())
153-
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
154-
155125

156126
messageContext = new MessageContext(randomIdGeneratorService);
157127

128+
RedactionsContext redactionsContext = mock(RedactionsContext.class); // mock if necessary
129+
130+
XmlSchemaValidator xmlSchemaValidator = new XmlSchemaValidator(redactionsContext);
131+
158132
ParticipantMapper participantMapper = new ParticipantMapper();
159133
StructuredObservationValueMapper structuredObservationValueMapper = new StructuredObservationValueMapper();
160134
ObservationMapper specimenObservationMapper = new ObservationMapper(
161-
messageContext, structuredObservationValueMapper, codeableConceptCdMapper,
162-
participantMapper, randomIdGeneratorService, confidentialityService);
135+
messageContext, structuredObservationValueMapper, codeableConceptCdMapper,
136+
participantMapper, randomIdGeneratorService, confidentialityService);
163137
SpecimenMapper specimenMapper = new SpecimenMapper(messageContext, specimenObservationMapper,
164-
randomIdGeneratorService, confidentialityService);
138+
randomIdGeneratorService, confidentialityService);
165139
DocumentReferenceToNarrativeStatementMapper documentReferenceToNarrativeStatementMapper
166-
= new DocumentReferenceToNarrativeStatementMapper(
140+
= new DocumentReferenceToNarrativeStatementMapper(
167141
messageContext, new SupportedContentTypes(), participantMapper, confidentialityService);
168142

169143
EncounterComponentsMapper encounterComponentsMapper = new EncounterComponentsMapper(
170-
messageContext,
171-
new AllergyStructureMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
172-
new BloodPressureMapper(
173-
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
174-
codeableConceptCdMapper, new ParticipantMapper(), confidentialityService),
175-
new ConditionLinkSetMapper(
176-
messageContext, randomIdGeneratorService, codeableConceptCdMapper, participantMapper, confidentialityService),
177-
new DiaryPlanStatementMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
178-
documentReferenceToNarrativeStatementMapper,
179-
new ImmunizationObservationStatementMapper(
180-
messageContext,
181-
codeableConceptCdMapper,
182-
participantMapper,
183-
confidentialityService
184-
),
185-
new MedicationStatementMapper(
186-
messageContext,
187-
codeableConceptCdMapper,
188-
participantMapper,
189-
randomIdGeneratorService,
190-
confidentialityService
191-
),
192-
new ObservationToNarrativeStatementMapper(messageContext, participantMapper, confidentialityService),
193-
new ObservationStatementMapper(
194144
messageContext,
195-
new StructuredObservationValueMapper(),
196-
new PertinentInformationObservationValueMapper(),
145+
new AllergyStructureMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
146+
new BloodPressureMapper(
147+
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
148+
codeableConceptCdMapper, new ParticipantMapper(), confidentialityService),
149+
new ConditionLinkSetMapper(
150+
messageContext, randomIdGeneratorService, codeableConceptCdMapper, participantMapper, confidentialityService),
151+
new DiaryPlanStatementMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
152+
documentReferenceToNarrativeStatementMapper,
153+
new ImmunizationObservationStatementMapper(
154+
messageContext,
155+
codeableConceptCdMapper,
156+
participantMapper,
157+
confidentialityService
158+
),
159+
new MedicationStatementMapper(
160+
messageContext,
161+
codeableConceptCdMapper,
162+
participantMapper,
163+
randomIdGeneratorService,
164+
confidentialityService
165+
),
166+
new ObservationToNarrativeStatementMapper(messageContext, participantMapper, confidentialityService),
167+
new ObservationStatementMapper(
168+
messageContext,
169+
new StructuredObservationValueMapper(),
170+
new PertinentInformationObservationValueMapper(),
171+
codeableConceptCdMapper,
172+
participantMapper,
173+
confidentialityService
174+
),
175+
new RequestStatementMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
176+
new DiagnosticReportMapper(
177+
messageContext, specimenMapper, participantMapper, randomIdGeneratorService, confidentialityService
178+
),
179+
new BloodPressureValidator(),
197180
codeableConceptCdMapper,
198-
participantMapper,
199181
confidentialityService
200-
),
201-
new RequestStatementMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
202-
new DiagnosticReportMapper(
203-
messageContext, specimenMapper, participantMapper, randomIdGeneratorService, confidentialityService
204-
),
205-
new BloodPressureValidator(),
206-
codeableConceptCdMapper,
207-
confidentialityService
208182
);
209183

210184
AgentDirectoryMapper agentDirectoryMapper = new AgentDirectoryMapper(
211-
messageContext,
212-
new AgentPersonMapper(messageContext)
185+
messageContext,
186+
new AgentPersonMapper(messageContext)
213187
);
214188

215189
nonConsultationResourceMapper = new NonConsultationResourceMapper(messageContext,
216-
randomIdGeneratorService,
217-
encounterComponentsMapper,
218-
new BloodPressureValidator()
190+
randomIdGeneratorService,
191+
encounterComponentsMapper,
192+
new BloodPressureValidator()
219193
);
220194

221-
ehrExtractMapper = new EhrExtractMapper(randomIdGeneratorService,
222-
timestampService,
223-
new EncounterMapper(messageContext, encounterComponentsMapper, confidentialityService),
224-
nonConsultationResourceMapper,
225-
agentDirectoryMapper,
226-
messageContext);
195+
ehrExtractMapper = new EhrExtractMapper(
196+
randomIdGeneratorService,
197+
timestampService,
198+
new EncounterMapper(messageContext, encounterComponentsMapper, confidentialityService),
199+
nonConsultationResourceMapper,
200+
agentDirectoryMapper,
201+
messageContext,
202+
xmlSchemaValidator
203+
);
227204
}
228205

206+
229207
@AfterEach
230208
public void tearDown() {
231209
messageContext.resetMessageContext();

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import static org.mockito.ArgumentMatchers.any;
55
import static org.mockito.ArgumentMatchers.eq;
66
import static org.junit.jupiter.api.Assertions.assertThrows;
7-
import static org.mockito.Mockito.when;
8-
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.*;
98

109

1110
import java.time.Instant;
@@ -25,6 +24,7 @@
2524
import uk.nhs.adaptors.gp2gp.common.service.TimestampService;
2625
import uk.nhs.adaptors.gp2gp.ehr.exception.EhrValidationException;
2726
import uk.nhs.adaptors.gp2gp.gpc.GetGpcStructuredTaskDefinition;
27+
import uk.nhs.adaptors.gp2gp.transformjsontoxmltool.XmlSchemaValidator;
2828

2929
@ExtendWith(MockitoExtension.class)
3030
class EhrExtractMapperTest {
@@ -53,6 +53,9 @@ class EhrExtractMapperTest {
5353
@InjectMocks
5454
private EhrExtractMapper ehrExtractMapper;
5555

56+
@Mock
57+
private XmlSchemaValidator xmlSchemaValidator;
58+
5659
@Test
5760
void When_NhsOverrideNumberProvided_Expect_OverrideToBeUsed() {
5861
when(nonConsultationResourceMapper.mapRemainingResourcesToEhrCompositions(any()))
@@ -155,4 +158,27 @@ void When_BuildEhrCompositionForSkeletonEhrExtract_Expect_ExpectedComponentBuilt
155158

156159
assertThat(actual).isEqualTo(expected);
157160
}
161+
162+
@Test
163+
void When_XmlFailsValidation_Expect_XmlSchemaValidationErrorIsThrown() {
164+
when(timestampService.now()).thenReturn(Instant.now().truncatedTo(ChronoUnit.MILLIS));
165+
String invalidXml = "<invalidXml />";
166+
var taskDef = GetGpcStructuredTaskDefinition.builder()
167+
.nhsNumber(NHS_NUMBER)
168+
.build();
169+
var bundle = mock(Bundle.class);
170+
171+
172+
when(nonConsultationResourceMapper.mapRemainingResourcesToEhrCompositions(any()))
173+
.thenReturn(Collections.singletonList(invalidXml));
174+
175+
176+
RuntimeException thrown = assertThrows(RuntimeException.class, () ->
177+
ehrExtractMapper.mapBundleToEhrFhirExtractParams(taskDef, bundle)
178+
);
179+
assertThat(thrown.getMessage()).isEqualTo("XML Schema validation failed");
180+
}
181+
182+
183+
158184
}

service/src/test/java/uk/nhs/adaptors/gp2gp/uat/EhrExtractUATTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import uk.nhs.adaptors.gp2gp.ehr.mapper.parameters.EhrExtractTemplateParameters;
5151
import uk.nhs.adaptors.gp2gp.ehr.utils.BloodPressureValidator;
5252
import uk.nhs.adaptors.gp2gp.gpc.GetGpcStructuredTaskDefinition;
53+
import uk.nhs.adaptors.gp2gp.transformjsontoxmltool.XmlSchemaValidator;
5354
import uk.nhs.adaptors.gp2gp.utils.ResourceTestFileUtils;
5455
import wiremock.org.custommonkey.xmlunit.XMLAssert;
5556

@@ -139,6 +140,8 @@ public void setUp() {
139140
.build();
140141

141142
final RandomIdGeneratorService randomIdGeneratorService = new RandomIdGeneratorServiceStub();
143+
final XmlSchemaValidator xmlSchemaValidator = new XmlSchemaValidator(redactionsContext);
144+
142145
when(timestampService.now()).thenReturn(Instant.parse("2020-01-01T01:01:01.01Z"));
143146

144147
outputMessageWrapperMapper = new OutputMessageWrapperMapper(randomIdGeneratorService, timestampService, redactionsContext);
@@ -158,7 +161,7 @@ public void setUp() {
158161
new NonConsultationResourceMapper(messageContext, randomIdGeneratorService, encounterComponentsMapper,
159162
new BloodPressureValidator());
160163
ehrExtractMapper = new EhrExtractMapper(randomIdGeneratorService, timestampService, encounterMapper,
161-
nonConsultationResourceMapper, agentDirectoryMapper, messageContext);
164+
nonConsultationResourceMapper, agentDirectoryMapper, messageContext, xmlSchemaValidator);
162165
lenient().when(confidentialityService.generateConfidentialityCode(any()))
163166
.thenReturn(Optional.empty());
164167
}

0 commit comments

Comments
 (0)