From fc666fd2fa8fbf75dc208666f5b85f6eeb7aacfe Mon Sep 17 00:00:00 2001 From: not-mksv Date: Wed, 20 May 2026 10:05:47 +0200 Subject: [PATCH] fix: prevent duplicate parameter resolution for %subject, %encounter, and %practitioner in InputParameterResolver --- .../cr/common/InputParameterResolver.java | 6 +-- .../fhir/cr/common/InputParametersTest.java | 45 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/InputParameterResolver.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/InputParameterResolver.java index 5d40e74e1..3ec1c23e0 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/InputParameterResolver.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/InputParameterResolver.java @@ -94,7 +94,7 @@ protected final IParametersAdapter resolveParameters( if (baseParameters != null) { adapterFactory.createParameters(baseParameters).getParameter().forEach(p -> params.addParameter(p.get())); } - if (subjectId != null) { + if (subjectId != null && !params.hasParameter("%subject")) { var subjectClass = fhirContext() .getResourceDefinition(subjectId.getResourceType()) .getImplementingClass(); @@ -103,7 +103,7 @@ protected final IParametersAdapter resolveParameters( params.addParameter("%subject", subject); } } - if (encounterId != null) { + if (encounterId != null && !params.hasParameter("%encounter")) { var encounterClass = fhirContext() .getResourceDefinition(encounterId.getResourceType()) .getImplementingClass(); @@ -112,7 +112,7 @@ protected final IParametersAdapter resolveParameters( params.addParameter("%encounter", encounter); } } - if (practitionerId != null) { + if (practitionerId != null && !params.hasParameter("%practitioner")) { var practitionerClass = fhirContext() .getResourceDefinition(practitionerId.getResourceType()) .getImplementingClass(); diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/InputParametersTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/InputParametersTest.java index a903c4cb1..b300c31bd 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/InputParametersTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/InputParametersTest.java @@ -5,6 +5,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; import static org.opencds.cqf.fhir.utility.Parameters.newPart; import static org.opencds.cqf.fhir.utility.Parameters.newStringPart; @@ -25,6 +26,8 @@ import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Encounter; +import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.PractitionerRole; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; @@ -586,4 +589,46 @@ void testResolveInputParametersWithDataRequirements() { assertNotNull(parameter); assertEquals(obs, parameter.getResource()); } + + @Test + void testResolveParametersSkipsRefetchWhenAlreadyInBaseParametersR4() { + // When baseParameters already contains %subject/%encounter/%practitioner + // (e.g. passed in from a parent PlanDefinition's resolver into an ActivityDefinition's + // resolver), the resolver must not re-fetch and produce duplicate entries. + var patient = new Patient(); + patient.setIdElement(Ids.newId(fhirContextR4, "Patient", patientId)); + var encounter = new Encounter(); + encounter.setIdElement(Ids.newId(fhirContextR4, "Encounter", encounterId)); + var practitioner = new Practitioner(); + practitioner.setIdElement(Ids.newId(fhirContextR4, "Practitioner", practitionerId)); + + var baseParameters = new Parameters(); + baseParameters.addParameter().setName("%subject").setResource(patient); + baseParameters.addParameter().setName("%encounter").setResource(encounter); + baseParameters.addParameter().setName("%practitioner").setResource(practitioner); + + doReturn(fhirContextR4).when(repository).fhirContext(); + lenient().doReturn(patient).when(repository).read(Patient.class, patient.getIdElement()); + lenient().doReturn(encounter).when(repository).read(Encounter.class, encounter.getIdElement()); + lenient().doReturn(practitioner).when(repository).read(Practitioner.class, practitioner.getIdElement()); + + var resolver = IInputParameterResolver.createResolver( + repository, + patient.getIdElement(), + encounter.getIdElement(), + practitioner.getIdElement(), + baseParameters, + null, + null, + null); + var actual = (Parameters) resolver.getParameters(); + assertNotNull(actual); + assertEquals(3, actual.getParameter().size()); + assertEquals("%subject", actual.getParameter().get(0).getName()); + assertEquals(patient, actual.getParameter().get(0).getResource()); + assertEquals("%encounter", actual.getParameter().get(1).getName()); + assertEquals(encounter, actual.getParameter().get(1).getResource()); + assertEquals("%practitioner", actual.getParameter().get(2).getName()); + assertEquals(practitioner, actual.getParameter().get(2).getResource()); + } }