Skip to content

Commit b1faad8

Browse files
NIAD-3227: ObservationValueQuantityMapper should escape any input from GP Connect Bundle (#981)
Add functionality to XML escape the `code` and `unit` when generating the XML string. `value` does not need to be escaped as it is a `BigDecimal` type. Add unit tests to test above functionality. Update CHANGELOG.md
1 parent 0d00ec6 commit b1faad8

File tree

3 files changed

+56
-10
lines changed

3 files changed

+56
-10
lines changed

CHANGELOG.md

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

77
## [Unreleased]
88

9+
## Fixed
10+
11+
* When mapping a `valueQuantity` contained in an `Observation`, the produced XML `<value>` element now correctly escapes
12+
any contained XML characters.
13+
914
## Added
1015

1116
* When mapping a `DocumentReference` which contains a `NOPAT` `meta.security` or `NOPAT` `securityLabel` tag the resultant XML for that resource

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package uk.nhs.adaptors.gp2gp.ehr.mapper;
22

33
import org.apache.commons.lang3.StringUtils;
4+
import org.apache.commons.text.StringEscapeUtils;
45
import org.hl7.fhir.dstu3.model.BooleanType;
56
import org.hl7.fhir.dstu3.model.Extension;
67
import org.hl7.fhir.dstu3.model.Quantity;
@@ -52,12 +53,15 @@ public static String processQuantity(Quantity valueQuantity) {
5253
}
5354

5455
private static String getPhysicalQuantityXml(Quantity valueQuantity) {
56+
var escapedCode = StringEscapeUtils.escapeXml11(valueQuantity.getCode());
57+
var escapedUnit = StringEscapeUtils.escapeXml11(valueQuantity.getUnit());
58+
5559
if (UNITS_OF_MEASURE_SYSTEM.equals(valueQuantity.getSystem()) && valueQuantity.hasCode()) {
5660
return """
5761
<value xsi:type="PQ" value="%s" unit="%s" />"""
5862
.formatted(
5963
valueQuantity.getValue(),
60-
valueQuantity.getCode()
64+
escapedCode
6165
);
6266
}
6367
if (hasValidSystem(valueQuantity) && valueQuantity.hasCode() && valueQuantity.hasUnit()) {
@@ -68,9 +72,9 @@ private static String getPhysicalQuantityXml(Quantity valueQuantity) {
6872
.formatted(
6973
valueQuantity.getValue(),
7074
valueQuantity.getValue(),
71-
valueQuantity.getCode(),
75+
escapedCode,
7276
getSystemWithoutPrefix(valueQuantity.getSystem()),
73-
valueQuantity.getUnit()
77+
escapedUnit
7478
);
7579
}
7680
if (hasValidSystem(valueQuantity) && valueQuantity.hasCode()) {
@@ -81,7 +85,7 @@ private static String getPhysicalQuantityXml(Quantity valueQuantity) {
8185
.formatted(
8286
valueQuantity.getValue(),
8387
valueQuantity.getValue(),
84-
valueQuantity.getCode(),
88+
escapedCode,
8589
getSystemWithoutPrefix(valueQuantity.getSystem())
8690
);
8791
}
@@ -95,7 +99,7 @@ private static String getPhysicalQuantityXml(Quantity valueQuantity) {
9599
.formatted(
96100
valueQuantity.getValue(),
97101
valueQuantity.getValue(),
98-
valueQuantity.getUnit()
102+
escapedUnit
99103
);
100104
}
101105

@@ -105,6 +109,9 @@ private static String getPhysicalQuantityXml(Quantity valueQuantity) {
105109
}
106110

107111
private static String getPhysicalQuantityIntervalXml(Quantity valueQuantity) {
112+
var escapedCode = StringEscapeUtils.escapeXml11(valueQuantity.getCode());
113+
var escapedUnit = StringEscapeUtils.escapeXml11(valueQuantity.getUnit());
114+
108115
if (UNITS_OF_MEASURE_SYSTEM.equals(valueQuantity.getSystem()) && valueQuantity.hasCode()) {
109116
return """
110117
<value xsi:type="IVL_PQ">%n\
@@ -113,7 +120,7 @@ private static String getPhysicalQuantityIntervalXml(Quantity valueQuantity) {
113120
.formatted(
114121
getHighOrLow(valueQuantity),
115122
valueQuantity.getValue(),
116-
valueQuantity.getCode(),
123+
escapedCode,
117124
isInclusive(valueQuantity)
118125
);
119126
}
@@ -129,9 +136,9 @@ private static String getPhysicalQuantityIntervalXml(Quantity valueQuantity) {
129136
valueQuantity.getValue(),
130137
isInclusive(valueQuantity),
131138
valueQuantity.getValue(),
132-
valueQuantity.getCode(),
139+
escapedCode,
133140
getSystemWithoutPrefix(valueQuantity.getSystem()),
134-
valueQuantity.getUnit(),
141+
escapedUnit,
135142
getHighOrLow(valueQuantity)
136143
);
137144
}
@@ -147,7 +154,7 @@ private static String getPhysicalQuantityIntervalXml(Quantity valueQuantity) {
147154
valueQuantity.getValue(),
148155
isInclusive(valueQuantity),
149156
valueQuantity.getValue(),
150-
valueQuantity.getCode(),
157+
escapedCode,
151158
getSystemWithoutPrefix(valueQuantity.getSystem()),
152159
getHighOrLow(valueQuantity)
153160
);
@@ -166,7 +173,7 @@ private static String getPhysicalQuantityIntervalXml(Quantity valueQuantity) {
166173
valueQuantity.getValue(),
167174
isInclusive(valueQuantity),
168175
valueQuantity.getValue(),
169-
valueQuantity.getUnit(),
176+
escapedUnit,
170177
getHighOrLow(valueQuantity)
171178
);
172179
}

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class ObservationValueQuantityMapperTest {
3838
public static final String SYSTEM_UUID = "e1423232-5d4f-472f-8c55-271de1d6f98d";
3939
public static final String SYSTEM_UUID_WITH_PREFIX = "urn:uuid:e1423232-5d4f-472f-8c55-271de1d6f98d";
4040
public static final String INVALID_SYSTEM = "not-a-valid-system";
41+
public static final String STRING_WITH_XML_TO_BE_ESCAPED = "\" ' & < >";
4142

4243
@Test
4344
public void When_MappingQuantityWithUncertaintyExtension_Expect_XmlContainsUncertaintyCode() {
@@ -523,6 +524,39 @@ public void When_MappingIntervalWithAnyOrInvalidOrNoSystemWithoutUnitOrCode_Expe
523524
assertThat(mappedQuantity).isEqualTo(expectedXml);
524525
}
525526

527+
@Test
528+
public void When_MappingWithXmlCharactersInCode_Expect_XmlCharactersAreEscaped() {
529+
var quantity = new Quantity()
530+
.setSystem(UOM_SYSTEM)
531+
.setValue(VALUE_37_1)
532+
.setCode(STRING_WITH_XML_TO_BE_ESCAPED);
533+
534+
var expectedXml = """
535+
<value xsi:type="PQ" value="37.1" unit="&quot; &apos; &amp; &lt; &gt;" />""";
536+
537+
var mappedQuantity = ObservationValueQuantityMapper.processQuantity(quantity);
538+
539+
assertThat(mappedQuantity).isEqualTo(expectedXml);
540+
}
541+
542+
@Test
543+
public void When_MappingWithXmlCharactersInUnit_Expect_XmlCharactersAreEscaped() {
544+
var quantity = new Quantity()
545+
.setSystem(UOM_SYSTEM)
546+
.setValue(VALUE_37_1)
547+
.setUnit(STRING_WITH_XML_TO_BE_ESCAPED);
548+
549+
var expectedXml = """
550+
<value xsi:type="PQ" value="37.1" unit="1">
551+
<translation value="37.1">
552+
<originalText>&quot; &apos; &amp; &lt; &gt;</originalText>
553+
</translation>
554+
</value>""";
555+
var mappedQuantity = ObservationValueQuantityMapper.processQuantity(quantity);
556+
557+
assertThat(mappedQuantity).isEqualTo(expectedXml);
558+
}
559+
526560
private static Stream<Arguments> provideComparatorsParameters() {
527561
return Stream.of(
528562
Arguments.of(LESS_THAN, "high", false),

0 commit comments

Comments
 (0)