Skip to content

Commit 55af8cb

Browse files
authored
fix NPE on replace-references with logical reference (#7164)
* fix replace-references NPE on logical reference * addressed code review comments
1 parent 8bb65a6 commit 55af8cb

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: fix
3+
issue: 7163
4+
title: "Previously, the `$hapi.fhir.replace-references` operation was failing with a NPE when a referencing resource
5+
had a logical reference. This has now been fixed."

hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ReplaceReferencesR4Test.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.hl7.fhir.instance.model.api.IIdType;
1818
import org.hl7.fhir.r4.model.Bundle;
1919
import org.hl7.fhir.r4.model.Coding;
20+
import org.hl7.fhir.r4.model.Identifier;
2021
import org.hl7.fhir.r4.model.Observation;
2122
import org.hl7.fhir.r4.model.Parameters;
2223
import org.hl7.fhir.r4.model.Patient;
@@ -450,6 +451,38 @@ void testReplaceReferences_SourceResourceNotReferencedByAnyResource_SucceedAndSh
450451
assertThat(provenances).isEmpty();
451452
}
452453

454+
@ParameterizedTest
455+
@CsvSource (value = {
456+
"false",
457+
"true"
458+
})
459+
void testReplaceReferences_ReferencingResourceHasAnAdditionalLogicalReference_Succeeds(boolean theIsAsync) {
460+
461+
IIdType sourcePatientId = myPatientDao.create(new Patient(), mySrd).getId().toUnqualifiedVersionless();
462+
IIdType targetPatientId = myPatientDao.create(new Patient(), mySrd).getId().toUnqualifiedVersionless();
463+
464+
// Create an Observation resource that references the source patient
465+
// and has an additional logical reference.
466+
Observation observation = new Observation();
467+
observation.setSubject(new Reference(sourcePatientId));
468+
Reference logicalReference = new Reference();
469+
logicalReference.setIdentifier(new Identifier().setSystem("performerSys").setValue("drWho"));
470+
observation.addPerformer(logicalReference);
471+
472+
IIdType obsId = myObservationDao.create(observation, mySrd).getId().toUnqualifiedVersionless();
473+
474+
executeReplaceReferences(sourcePatientId, targetPatientId, theIsAsync);
475+
476+
Observation obsAfter = myObservationDao.read(obsId, mySrd);
477+
assertThat(obsAfter.getSubject().getReference()).isEqualTo(targetPatientId.toString());
478+
// The logical reference should not have been replaced
479+
assertThat(obsAfter.getPerformer()).hasSize(1);
480+
assertThat(obsAfter.getPerformerFirstRep().getIdentifier().getSystem()).isEqualTo("performerSys");
481+
assertThat(obsAfter.getPerformerFirstRep().getIdentifier().getValue()).isEqualTo("drWho");
482+
483+
myTestHelper.assertReplaceReferencesProvenance(sourcePatientId.withVersion("1"), targetPatientId.withVersion("1"), 1, Set.of(obsId.withVersion("2").toString()), null);
484+
}
485+
453486
@Override
454487
protected boolean verboseClientLogging() {
455488
return true;

hapi-fhir-storage/src/main/java/ca/uhn/fhir/replacereferences/ReplaceReferencesPatchBundleSvc.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import ca.uhn.fhir.util.BundleBuilder;
2929
import ca.uhn.fhir.util.ResourceReferenceInfo;
3030
import jakarta.annotation.Nonnull;
31+
import org.hl7.fhir.instance.model.api.IBaseReference;
3132
import org.hl7.fhir.instance.model.api.IBaseResource;
3233
import org.hl7.fhir.instance.model.api.IIdType;
3334
import org.hl7.fhir.r4.model.Bundle;
@@ -111,11 +112,14 @@ private Bundle buildPatchBundle(
111112
}
112113

113114
private static boolean matches(ResourceReferenceInfo refInfo, IIdType theSourceId) {
114-
return refInfo.getResourceReference()
115-
.getReferenceElement()
116-
.toUnqualified()
117-
.getValueAsString()
118-
.equals(theSourceId.getValueAsString());
115+
116+
IBaseReference iBaseRef = refInfo.getResourceReference();
117+
if (iBaseRef == null || (iBaseRef instanceof Reference ref && !ref.hasReferenceElement())) {
118+
// the reference doesn't have a reference element, it is probably just a logical reference (using an
119+
// identifier). so this is not a match.
120+
return false;
121+
}
122+
return iBaseRef.getReferenceElement().toUnqualified().getValueAsString().equals(theSourceId.getValueAsString());
119123
}
120124

121125
private String getFhirPathForPatch(IBaseResource theReferencingResource, ResourceReferenceInfo theRefInfo) {

0 commit comments

Comments
 (0)