Skip to content

Commit e196bc8

Browse files
XML Escape text fields when mapping to MedicationStatement (#1081)
* XML Escape text fields when mapping to `MedicationStatement` * Update template used when mapping `MedicationStatements` to XML escape any user provided text field, * Add test for user provided text fields when mapping `MedicationRequest` to `MedicationStatement`. * Add XML / JSON for the above tests. * Update CHANGELOG.md
1 parent cdb0cea commit e196bc8

File tree

5 files changed

+147
-6
lines changed

5 files changed

+147
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
* When mapping a `DiagnosticReport` which contains at least one `Specimen`,
1111
any specimens which didn't belong to a test result were previously not sent to the requesting system.
1212
Now, a fake `Observation` is created in which any `Observation`-less `Specimen`s are placed.
13+
* When mapping a `MedicationRequest` to a `MedicationStatement`, user provided text fields are now correctly escaped in
14+
the produced XML.
1315

1416
## [2.2.1] - 2024-12-10
1517

service/src/main/resources/templates/ehr_medication_statement_authorise_template.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
</reversalOf>
6262
<pertinentInformation typeCode="PERT">
6363
<pertinentSupplyAnnotation classCode="OBS" moodCode="EVN"{{^ehrSupplyDiscontinueReasonText}} nullFlavor="UNK"{{/ehrSupplyDiscontinueReasonText}}>
64-
{{#ehrSupplyDiscontinueReasonText}}<text>{{{ehrSupplyDiscontinueReasonText}}}</text>{{/ehrSupplyDiscontinueReasonText}}{{^ehrSupplyDiscontinueReasonText}}<text>Stopped</text>{{/ehrSupplyDiscontinueReasonText}}
64+
{{#ehrSupplyDiscontinueReasonText}}<text>{{ehrSupplyDiscontinueReasonText}}</text>{{/ehrSupplyDiscontinueReasonText}}{{^ehrSupplyDiscontinueReasonText}}<text>Stopped</text>{{/ehrSupplyDiscontinueReasonText}}
6565
</pertinentSupplyAnnotation>
6666
</pertinentInformation>
6767
</ehrSupplyDiscontinue>

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ public class MedicationStatementMapperTest {
160160
+ "medication-request-empty-prescribing-agency-coding-array.json";
161161
private static final String INPUT_JSON_WITH_PRESCRIBING_AGENCY_ERROR_MISSING_CODEABLE_CONCEPT = TEST_FILE_DIRECTORY
162162
+ "medication-request-missing-prescribing-agency-codeable-concept.json";
163+
private static final String INPUT_JSON_WITH_SPECIAL_CHARACTERS_IN_TEXT_FIELDS = TEST_FILE_DIRECTORY
164+
+ "medication-request-special-character-in-code.json";
165+
private static final String OUTPUT_XML_WITH_ESCAPED_XML_TEXT_VALUES = TEST_FILE_DIRECTORY
166+
+ "medication-statement-with-xml-escaped-text-values.xml";
163167
private static final String CONFIDENTIALITY_CODE = """
164168
<confidentialityCode
165169
code="NOPAT"
@@ -234,7 +238,8 @@ private static Stream<Arguments> resourceFileParams() {
234238
Arguments.of(INPUT_JSON_WITH_PLAN_NO_INFO_PRESCRIPTION_TEXT, OUTPUT_XML_WITH_AUTHORISE_REPEAT_PRESCRIPTION),
235239
Arguments.of(INPUT_JSON_WITH_EXTENSION_STATUS_REASON_TEXT, OUTPUT_XML_WITH_STATUS_REASON_TEXT),
236240
Arguments.of(INPUT_JSON_WITH_NO_RECORDER_REFERENCE, OUTPUT_XML_WITH_NO_PARTICIPANT),
237-
Arguments.of(INPUT_JSON_WITH_INVALID_RECORDER_REFERENCE_TYPE, OUTPUT_XML_WITH_NO_PARTICIPANT)
241+
Arguments.of(INPUT_JSON_WITH_INVALID_RECORDER_REFERENCE_TYPE, OUTPUT_XML_WITH_NO_PARTICIPANT),
242+
Arguments.of(INPUT_JSON_WITH_SPECIAL_CHARACTERS_IN_TEXT_FIELDS, OUTPUT_XML_WITH_ESCAPED_XML_TEXT_VALUES)
238243
);
239244
}
240245

@@ -278,7 +283,7 @@ public void When_MappingBasedOnField_Expect_CorrectReferences() {
278283

279284
@ParameterizedTest
280285
@MethodSource("resourceFileExpectException")
281-
public void When_MappingMedicationRequestWithInvalidResource_Expect_Exception(String inputJson) throws IOException {
286+
public void When_MappingMedicationRequestWithInvalidResource_Expect_Exception(String inputJson) {
282287
var jsonInput = ResourceTestFileUtils.getFileContent(inputJson);
283288
MedicationRequest parsedMedicationRequest = new FhirParseService().parseResource(jsonInput, MedicationRequest.class);
284289

@@ -302,7 +307,7 @@ private static List<String> resourceFileExpectException() {
302307
}
303308

304309
@Test
305-
public void When_MappingMedicationRequestWithRequesterWithOnBehalfOf_Expect_ParticipantMappedToAgent() throws IOException {
310+
public void When_MappingMedicationRequestWithRequesterWithOnBehalfOf_Expect_ParticipantMappedToAgent() {
306311
when(mockRandomIdGeneratorService.createNewId()).thenReturn(TEST_ID);
307312
codeableConceptCdMapper = new CodeableConceptCdMapper();
308313
var bundleInput = ResourceTestFileUtils.getFileContent(INPUT_JSON_BUNDLE);
@@ -350,7 +355,7 @@ private static Stream<Arguments> resourceFilesWithParticipant() {
350355
@ParameterizedTest
351356
@MethodSource("resourceFilesWithParticipant")
352357
public void When_MappingMedicationRequestWithParticipant_Expect_ParticipantMappedToAgent(
353-
String inputJson, String agentId) throws IOException {
358+
String inputJson, String agentId) {
354359
when(mockRandomIdGeneratorService.createNewId()).thenReturn(TEST_ID);
355360
codeableConceptCdMapper = new CodeableConceptCdMapper();
356361
var bundleInput = ResourceTestFileUtils.getFileContent(INPUT_JSON_BUNDLE);
@@ -397,7 +402,7 @@ private static Stream<Arguments> resourceFilesWithMedicationStatement() {
397402
@ParameterizedTest
398403
@MethodSource("resourceFilesWithMedicationStatement")
399404
public void When_MappingMedicationRequest_WithMedicationStatement_Expect_PrescribingAgencyMappedToSupplyType(
400-
String inputJson, String outputXml) throws IOException {
405+
String inputJson, String outputXml) {
401406

402407
var expected = ResourceTestFileUtils.getFileContent(outputXml);
403408

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"resourceType":"MedicationRequest",
3+
"id":"3377543D-5B1B-4C4F-BFF6-9F7BC3A1C3B8",
4+
"meta":{
5+
"profile":[
6+
"https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-MedicationRequest-1"
7+
]
8+
},
9+
"extension":[
10+
{
11+
"url": "https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-MedicationStatusReason-1",
12+
"extension": [
13+
{
14+
"url": "statusReason",
15+
"valueCodeableConcept": {
16+
"text": "<<Test Code Text>>"
17+
}
18+
},
19+
{
20+
"url": "statusChangeDate",
21+
"valueDateTime": "2010-01-18T14:33:16.397+00:00"
22+
}
23+
]
24+
},
25+
{
26+
"url": "https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-PrescriptionType-1",
27+
"valueCodeableConcept": {
28+
"coding": [
29+
{
30+
"system": "https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-PrescriptionType-1",
31+
"code": "acute",
32+
"display": "Acute"
33+
}
34+
]
35+
}
36+
}
37+
],
38+
"authoredOn": "2021-01-01T00:00:00+00:00",
39+
"status": "stopped",
40+
"intent": "plan",
41+
"dispenseRequest":{
42+
"validityPeriod":{
43+
"start": "2021-01-01T00:00:00+00:00",
44+
"end": "2021-02-01T00:00:00+00:00"
45+
}
46+
},
47+
"medicationReference":{
48+
"reference": "Medication/20"
49+
},
50+
"dosageInstruction":[
51+
{
52+
"text": "<<Test Dosage Text>>",
53+
"patientInstruction": "<<Test Patient Instruction>>"
54+
}
55+
],
56+
"recorder":{
57+
"reference": "Organization/2"
58+
},
59+
"basedOn": [
60+
{
61+
"reference": "MedicationRequest/D66D84C9-C073-4EDF-8C2C-F309A83C3DC7"
62+
}
63+
]
64+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<component typeCode="COMP">
2+
<MedicationStatement classCode="SBADM" moodCode="INT">
3+
<id root="394559384658936"/>
4+
<statusCode code="COMPLETE"/>
5+
<effectiveTime>
6+
<low value="20210101000000"/><high value="20210201000000"/>
7+
</effectiveTime>
8+
<availabilityTime value="20210101000000"/>
9+
<consumable typeCode="CSM">
10+
<manufacturedProduct classCode="MANU">
11+
<manufacturedMaterial determinerCode="KIND" classCode="MMAT">
12+
<code code="430127000" codeSystem="2.16.840.1.113883.2.1.3.2.4.15" displayName="Oral Form Oxycodone (product)">
13+
</code>
14+
</manufacturedMaterial>
15+
</manufacturedProduct>
16+
</consumable>
17+
<component typeCode="COMP">
18+
<ehrSupplyAuthorise>
19+
<id root="394559384658936"/>
20+
<code code="394823007" displayName="NHS Prescription" codeSystem="2.16.840.1.113883.2.1.3.2.4.15"/>
21+
<statusCode code="COMPLETE"/>
22+
<effectiveTime>
23+
<low value="20210101000000"/><high value="20210201000000"/>
24+
</effectiveTime>
25+
<availabilityTime value="20210101000000"/>
26+
<repeatNumber value="0"/>
27+
<quantity value="1" unit="1">
28+
<translation value="1">
29+
<originalText>Unk UoM</originalText>
30+
</translation>
31+
</quantity>
32+
<pertinentInformation typeCode="PERT">
33+
<pertinentSupplyAnnotation>
34+
<text>Patient Instruction: &lt;&lt;Test Patient Instruction&gt;&gt;</text>
35+
</pertinentSupplyAnnotation>
36+
</pertinentInformation>
37+
</ehrSupplyAuthorise>
38+
</component>
39+
<component typeCode="COMP">
40+
<ehrSupplyDiscontinue classCode="SPLY" moodCode="RQO">
41+
<id root="394559384658936"/>
42+
<code nullFlavor="UNK">
43+
<originalText>&lt;&lt;Test Code Text&gt;&gt;</originalText>
44+
</code>
45+
<statusCode code="COMPLETE"/>
46+
<availabilityTime value="20100118143316"/>
47+
<reversalOf typeCode="REV">
48+
<priorMedicationRef moodCode="ORD" classCode="SBADM">
49+
<id root="394559384658936"/>
50+
</priorMedicationRef>
51+
</reversalOf>
52+
<pertinentInformation typeCode="PERT">
53+
<pertinentSupplyAnnotation classCode="OBS" moodCode="EVN">
54+
<text>&lt;&lt;Test Code Text&gt;&gt;</text>
55+
</pertinentSupplyAnnotation>
56+
</pertinentInformation>
57+
</ehrSupplyDiscontinue>
58+
</component>
59+
<pertinentInformation typeCode="PERT">
60+
<pertinentMedicationDosage classCode="SBADM" moodCode="RMD">
61+
<text>&lt;&lt;Test Dosage Text&gt;&gt;</text>
62+
</pertinentMedicationDosage>
63+
</pertinentInformation>
64+
<Participant typeCode="AUT" contextControlCode="OP">
65+
<agentRef classCode="AGNT">
66+
<id root="394559384658936"/>
67+
</agentRef>
68+
</Participant>
69+
</MedicationStatement>
70+
</component>

0 commit comments

Comments
 (0)