Skip to content

Commit ab49be5

Browse files
joanne-mJoanne MendozaJoanne Mendoza
authored
Support patient parameter of type reference for Patient-level bulk export (#7140)
* Support string and reference for patient parameter in Patient/$export * Support IIdType patient param(used by Patient/[id]/$export) * Changelog * Refactor, add tests * Code review comments --------- Co-authored-by: Joanne Mendoza <[email protected]> Co-authored-by: Joanne Mendoza <[email protected]>
1 parent fcc0d45 commit ab49be5

File tree

4 files changed

+107
-7
lines changed

4 files changed

+107
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: add
3+
issue: 7128
4+
title: "Patient level Bulk export (`/Patient/$export`) now accepts the `patient` parameter of type reference to match the FHIR Bulk Data Access IG.
5+
For backwards compatibility with earlier implementations, `patient` may also be of string type, and will be interpreted as a reference."

hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderR4Test.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.hl7.fhir.r4.model.IdType;
4949
import org.hl7.fhir.r4.model.InstantType;
5050
import org.hl7.fhir.r4.model.Parameters;
51+
import org.hl7.fhir.r4.model.Reference;
5152
import org.hl7.fhir.r4.model.StringType;
5253
import org.junit.jupiter.api.BeforeEach;
5354
import org.junit.jupiter.api.Test;
@@ -871,6 +872,8 @@ public void testInitiatePatientExportRequest() throws IOException {
871872
input.addParameter(JpaConstants.PARAM_EXPORT_SINCE, now);
872873
input.addParameter(JpaConstants.PARAM_EXPORT_UNTIL, later);
873874
input.addParameter(JpaConstants.PARAM_EXPORT_TYPE_FILTER, new StringType("Immunization?vaccine-code=foo"));
875+
input.addParameter(JpaConstants.PARAM_EXPORT_PATIENT, new Reference("Patient/123"));
876+
input.addParameter(JpaConstants.PARAM_EXPORT_PATIENT, new StringType("Patient/456"));
874877

875878
ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input));
876879

@@ -892,6 +895,7 @@ public void testInitiatePatientExportRequest() throws IOException {
892895
assertNotNull(bp.getSince());
893896
assertNotNull(bp.getUntil());
894897
assertThat(bp.getFilters()).containsExactlyInAnyOrder("Immunization?vaccine-code=foo");
898+
assertThat(bp.getPatientIds()).containsExactlyInAnyOrder("Patient/123", "Patient/456");
895899
}
896900

897901
@Test

hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
import com.google.common.annotations.VisibleForTesting;
4949
import jakarta.annotation.Nonnull;
5050
import jakarta.servlet.http.HttpServletResponse;
51+
import org.hl7.fhir.instance.model.api.IBase;
5152
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
53+
import org.hl7.fhir.instance.model.api.IBaseReference;
5254
import org.hl7.fhir.instance.model.api.IIdType;
5355
import org.hl7.fhir.instance.model.api.IPrimitiveType;
5456
import org.hl7.fhir.r4.model.IdType;
@@ -58,6 +60,7 @@
5860
import org.springframework.beans.factory.annotation.Autowired;
5961

6062
import java.io.IOException;
63+
import java.lang.reflect.Method;
6164
import java.util.ArrayList;
6265
import java.util.Date;
6366
import java.util.List;
@@ -248,17 +251,14 @@ public void patientExport(
248251
max = OperationParam.MAX_UNLIMITED,
249252
typeName = "string")
250253
List<IPrimitiveType<String>> theTypePostFetchFilterUrl,
251-
@OperationParam(
252-
name = JpaConstants.PARAM_EXPORT_PATIENT,
253-
min = 0,
254-
max = OperationParam.MAX_UNLIMITED,
255-
typeName = "string")
256-
List<IPrimitiveType<String>> thePatient,
254+
@OperationParam(name = JpaConstants.PARAM_EXPORT_PATIENT, min = 0, max = OperationParam.MAX_UNLIMITED)
255+
List<IBase> thePatient,
257256
@OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string")
258257
IPrimitiveType<String> theExportIdentifier,
259258
ServletRequestDetails theRequestDetails) {
260259

261-
List<IPrimitiveType<String>> patientIds = thePatient != null ? thePatient : new ArrayList<>();
260+
final List<IPrimitiveType<String>> patientIds =
261+
thePatient != null ? parsePatientList(thePatient) : new ArrayList<>();
262262

263263
doPatientExport(
264264
theRequestDetails,
@@ -319,6 +319,35 @@ public void patientInstanceExport(
319319
theRequestDetails);
320320
}
321321

322+
static List<IPrimitiveType<String>> parsePatientList(@Nonnull List<IBase> thePatient) {
323+
return thePatient.stream()
324+
.map(patient -> {
325+
if (patient instanceof IIdType patientIIdType) {
326+
return patientIIdType;
327+
} else {
328+
return getStringPrimitiveFromParametersParameter(patient);
329+
}
330+
})
331+
.toList();
332+
}
333+
334+
private static IPrimitiveType<String> getStringPrimitiveFromParametersParameter(IBase patient) {
335+
try {
336+
Method getValueMethod = patient.getClass().getMethod("getValue");
337+
Object value = getValueMethod.invoke(patient);
338+
if (value instanceof IPrimitiveType<?> patientPrimitiveType
339+
&& patientPrimitiveType.getValue() instanceof String) {
340+
return (IPrimitiveType<String>) patientPrimitiveType;
341+
} else if (value instanceof IBaseReference patientReference) {
342+
return patientReference.getReferenceElement();
343+
} else {
344+
throw new InvalidRequestException("Unsupported type.");
345+
}
346+
} catch (Exception e) {
347+
throw new InvalidRequestException("Invalid patient parameter.", e);
348+
}
349+
}
350+
322351
private void doPatientExport(
323352
ServletRequestDetails theRequestDetails,
324353
IPrimitiveType<String> theOutputFormat,

hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProviderTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
import ca.uhn.fhir.context.FhirVersionEnum;
66
import ca.uhn.fhir.jpa.api.model.BulkExportJobResults;
77
import ca.uhn.fhir.jpa.bulk.export.model.BulkExportResponseJson;
8+
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
89
import ca.uhn.fhir.util.JsonUtil;
10+
import org.hl7.fhir.instance.model.api.IBase;
11+
import org.hl7.fhir.instance.model.api.IIdType;
12+
import org.hl7.fhir.instance.model.api.IPrimitiveType;
13+
import org.hl7.fhir.r4.model.DateType;
14+
import org.hl7.fhir.r4.model.IdType;
15+
import org.hl7.fhir.r4.model.Parameters;
16+
import org.hl7.fhir.r4.model.Reference;
17+
import org.hl7.fhir.r4.model.StringType;
918
import org.junit.jupiter.api.Test;
1019
import org.junit.jupiter.api.extension.ExtendWith;
1120
import org.junit.jupiter.params.ParameterizedTest;
@@ -21,6 +30,8 @@
2130

2231
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
2332
import static org.junit.jupiter.api.Assertions.assertEquals;
33+
import static org.junit.jupiter.api.Assertions.assertSame;
34+
import static org.junit.jupiter.api.Assertions.assertThrows;
2435

2536
@ExtendWith(MockitoExtension.class)
2637
class BulkDataExportProviderTest {
@@ -85,4 +96,55 @@ void testCompleteStatusDocument() {
8596

8697
}
8798

99+
@Test
100+
void parsePatientParamsWhenPatientIsIIdType() {
101+
// given
102+
IIdType idType = new IdType("Patient/123");
103+
List<IBase> input = List.of(idType);
104+
// when
105+
List<IPrimitiveType<String>> result = BulkDataExportProvider.parsePatientList(input);
106+
// then
107+
assertEquals(1, result.size());
108+
assertSame(idType, result.get(0));
109+
}
110+
111+
@Test
112+
void parsePatientListWhenParameterValueIsStringType() {
113+
// given
114+
StringType stringType = new StringType("abc");
115+
Parameters.ParametersParameterComponent param = new Parameters.ParametersParameterComponent();
116+
param.setValue(stringType);
117+
List<IBase> input = List.of(param);
118+
// when
119+
List<IPrimitiveType<String>> result = BulkDataExportProvider.parsePatientList(input);
120+
// then
121+
assertEquals(1, result.size());
122+
assertSame(stringType, result.get(0));
123+
}
124+
125+
@Test
126+
void parsePatientListWhenParameterValueIsReference() {
127+
// given
128+
Reference reference = new Reference("Patient/456");
129+
Parameters.ParametersParameterComponent param = new Parameters.ParametersParameterComponent();
130+
param.setValue(reference);
131+
List<IBase> input = List.of(param);
132+
// when
133+
List<IPrimitiveType<String>> result = BulkDataExportProvider.parsePatientList(input);
134+
// then
135+
assertEquals(1, result.size());
136+
assertEquals("Patient/456", result.get(0).getValue());
137+
}
138+
139+
@Test
140+
void parsePatientListThrowsExceptionForInvalidParameterType() {
141+
// given
142+
Parameters.ParametersParameterComponent param = new Parameters.ParametersParameterComponent();
143+
param.setValue(new DateType());
144+
List<IBase> input = List.of(param);
145+
// when/then
146+
assertThrows(InvalidRequestException.class, () -> BulkDataExportProvider.parsePatientList(input));
147+
}
148+
149+
88150
}

0 commit comments

Comments
 (0)