From 159cd5dc79f7d8d784d07aefdc7308161c98b05d Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Mon, 2 Dec 2024 14:01:33 -0500 Subject: [PATCH 1/2] Add tests for R4SubmitDataService and tweak docs for class itself. --- .../cr/measure/r4/R4SubmitDataService.java | 15 +- .../measure/r4/R4SubmitDataServiceTest.java | 139 ++++++++++++++++++ 2 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java index 7bed3d0d2..11e78e89c 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java @@ -8,6 +8,9 @@ import org.hl7.fhir.r4.model.Resource; import org.opencds.cqf.fhir.api.Repository; +/** + * See: https://www.hl7.org/fhir/R4/measure-operation-submit-data.html + */ public class R4SubmitDataService { private final Repository repository; @@ -16,18 +19,22 @@ public R4SubmitDataService(Repository repository) { this.repository = repository; } + // TODO: LD: Add description for measureId parameter once it's clear what this is: /** * Save measure report and resources to the local repository * - * @param id - * @param report - * @param resources + * @param measureId + * @param report The measure report being submitted + * @param resources The individual resources that make up the data-of-interest being submitted * @return Bundle transaction result */ - public Bundle submitData(IdType id, MeasureReport report, List resources) { + public Bundle submitData(IdType measureId, MeasureReport report, List resources) { /* * TODO - resource validation using $data-requirements operation (params are the provided id and * the measurement period from the MeasureReport) + */ + + /* * * TODO - profile validation ... not sure how that would work ... (get StructureDefinition from * URL or must it be stored in Ruler?) diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java new file mode 100644 index 000000000..951326e63 --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java @@ -0,0 +1,139 @@ +package org.opencds.cqf.fhir.cr.measure.r4; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import ca.uhn.fhir.context.FhirContext; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.*; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.search.Searches; + +class R4SubmitDataServiceTest { + + private static final FhirContext FHIR_CONTEXT = FhirContext.forR4(); + + private static final String OBSERVATION_VALUE = "ABC"; + private static final String MEASURE_ID_COMPONENT = "A123"; + private static final String MEASURE_FULL_ID = "Measure/A123"; + + private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + private static final String PATIENT_ID = "Practitioner-2178"; + private static final String ENCOUNTER_ID = "Encounter-62912"; + private static final String PROCEDURE_ID = "Procedure-89972"; + + private final Repository repository = new InMemoryFhirRepository(FhirContext.forR4Cached()); + private final R4SubmitDataService testSubject = new R4SubmitDataService(repository); + + @Test + public void submitDataSimple() { + + // create resources + var measureReport = newResource(MeasureReport.class).setMeasure(MEASURE_FULL_ID); + var observation = newResource(Observation.class).setValue(new StringType(OBSERVATION_VALUE)); + + // submit-data operation + var result = testSubject.submitData( + new IdType(ResourceType.Measure.toString(), MEASURE_ID_COMPONENT), measureReport, List.of(observation)); + + assertNotNull(result); + + var resultMeasureReport = getOnlyResourceFromSearch(MeasureReport.class); + assertThat(resultMeasureReport.getMeasure(), equalTo(MEASURE_FULL_ID)); + + var resultObservation = getOnlyResourceFromSearch(Observation.class); + assertThat(resultObservation.getValue().primitiveValue(), equalTo(OBSERVATION_VALUE)); + } + + @Test + public void submitDataMedium() throws ParseException { + + // create resources + var patient = newResource(Patient.class).setId(PATIENT_ID); + + var encounter = newResource(Encounter.class) + .setPeriod(new Period() + .setStart(DATE_FORMAT.parse("2018-05-29T11:00:00-04:00")) + .setEnd(DATE_FORMAT.parse("2018-05-29T11:00:00-04:00"))) + .setSubject(new Reference(patient.getId())) + .setId(ENCOUNTER_ID); + + var procedure = newResource(Procedure.class) + .setSubject(new Reference(patient.getId())) + .setPerformed(new Period() + .setStart(DATE_FORMAT.parse("2018-06-02T14:00:00-05:00")) + .setEnd(DATE_FORMAT.parse("2018-06-02T14:00:00-05:00"))) + .setId(PROCEDURE_ID); + + var measureReport = newResource(MeasureReport.class) + .setMeasure(MEASURE_FULL_ID) + .setPeriod(new Period() + .setStart(DATE_FORMAT.parse("2017-01-01T00:00:00+00:00")) + .setEnd(DATE_FORMAT.parse("2017-12-31T00:00:00+00:00"))) + .addEvaluatedResource(new Reference(patient.getId())) + .addEvaluatedResource(new Reference(encounter.getId())) + .addEvaluatedResource(new Reference(procedure.getId())); + + // submit-data operation + var result = testSubject.submitData( + new IdType(ResourceType.Measure.toString(), MEASURE_ID_COMPONENT), + measureReport, + List.of(patient, encounter, procedure)); + + assertNotNull(result); + + var resultMeasureReport = getOnlyResourceFromSearch(MeasureReport.class); + assertThat(resultMeasureReport.getMeasure(), equalTo(MEASURE_FULL_ID)); + assertThat( + resultMeasureReport.getEvaluatedResource().stream() + .map(Reference::getReference) + .toList(), + containsInAnyOrder(PATIENT_ID, ENCOUNTER_ID, PROCEDURE_ID)); + + var resultPatient = getOnlyResourceFromSearch(Patient.class); + assertThat(resultPatient.getId(), equalTo(PATIENT_ID)); + + var resultEncounter = getOnlyResourceFromSearch(Encounter.class); + assertThat(resultEncounter.getSubject().getReference(), equalTo(PATIENT_ID)); + + var resultProcedure = getOnlyResourceFromSearch(Procedure.class); + assertThat(resultProcedure.getSubject().getReference(), equalTo(PATIENT_ID)); + } + + private T getOnlyResourceFromSearch(Class clazz) { + final List resourcesFromSearch = getResourcesFromSearch(clazz); + + assertThat(resourcesFromSearch.size(), equalTo(1)); + + final T resource = resourcesFromSearch.get(0); + + assertThat(resource, notNullValue()); + + return resource; + } + + private List getResourcesFromSearch(Class clazz) { + var bundle = repository.search(Bundle.class, clazz, Searches.ALL); + + return bundle.getEntry().stream() + .map(Bundle.BundleEntryComponent::getResource) + .filter(clazz::isInstance) + .map(clazz::cast) + .toList(); + } + + @SuppressWarnings("unchecked") + private T newResource(Class theResourceClass) { + checkNotNull(theResourceClass); + + return (T) FHIR_CONTEXT.getResourceDefinition(theResourceClass).newInstance(); + } +} From 0e163931e64ab4dc165ddff17acf413999bd8bc1 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Tue, 3 Dec 2024 10:50:59 -0500 Subject: [PATCH 2/2] Tweaks based on code review feedback. --- .../cqf/fhir/cr/measure/r4/R4SubmitDataService.java | 5 +---- .../cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java | 9 ++------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java index 11e78e89c..2a934b151 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataService.java @@ -3,7 +3,6 @@ import java.util.List; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.MeasureReport; import org.hl7.fhir.r4.model.Resource; import org.opencds.cqf.fhir.api.Repository; @@ -19,16 +18,14 @@ public R4SubmitDataService(Repository repository) { this.repository = repository; } - // TODO: LD: Add description for measureId parameter once it's clear what this is: /** * Save measure report and resources to the local repository * - * @param measureId * @param report The measure report being submitted * @param resources The individual resources that make up the data-of-interest being submitted * @return Bundle transaction result */ - public Bundle submitData(IdType measureId, MeasureReport report, List resources) { + public Bundle submitData(MeasureReport report, List resources) { /* * TODO - resource validation using $data-requirements operation (params are the provided id and * the measurement period from the MeasureReport) diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java index 951326e63..4011b5c44 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4SubmitDataServiceTest.java @@ -22,7 +22,6 @@ class R4SubmitDataServiceTest { private static final FhirContext FHIR_CONTEXT = FhirContext.forR4(); private static final String OBSERVATION_VALUE = "ABC"; - private static final String MEASURE_ID_COMPONENT = "A123"; private static final String MEASURE_FULL_ID = "Measure/A123"; private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); @@ -41,8 +40,7 @@ public void submitDataSimple() { var observation = newResource(Observation.class).setValue(new StringType(OBSERVATION_VALUE)); // submit-data operation - var result = testSubject.submitData( - new IdType(ResourceType.Measure.toString(), MEASURE_ID_COMPONENT), measureReport, List.of(observation)); + var result = testSubject.submitData(measureReport, List.of(observation)); assertNotNull(result); @@ -83,10 +81,7 @@ public void submitDataMedium() throws ParseException { .addEvaluatedResource(new Reference(procedure.getId())); // submit-data operation - var result = testSubject.submitData( - new IdType(ResourceType.Measure.toString(), MEASURE_ID_COMPONENT), - measureReport, - List.of(patient, encounter, procedure)); + var result = testSubject.submitData(measureReport, List.of(patient, encounter, procedure)); assertNotNull(result);