ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.
*
ca.uhn.fhir.rest.server.TransactionLogMessages - This parameter is for informational messages provided by the MDM module during MDM processing.
+ *
ca.uhn.fhir.mdm.api.MdmLinkChangeEvent - Contains information about the change event, including target and golden resource IDs and the operation type.
*
*
*
* Hooks should return void.
*
*/
- MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"),
+ MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class,
+ "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage",
+ "ca.uhn.fhir.rest.server.TransactionLogMessages",
+ "ca.uhn.fhir.mdm.api.MdmLinkChangeEvent"),
/**
* Performance Tracing Hook:
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
index 3a84eebb083..b80d2caafc7 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
@@ -30,12 +30,15 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
import ca.uhn.fhir.mdm.api.IMdmSettings;
+import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
@@ -89,11 +92,11 @@ public class MdmMessageHandler implements MessageHandler {
try {
switch (theMsg.getOperationType()) {
case CREATE:
- handleCreatePatientOrPractitioner(theMsg, mdmContext);
+ handleCreateResource(theMsg, mdmContext);
break;
case UPDATE:
case MANUALLY_TRIGGERED:
- handleUpdatePatientOrPractitioner(theMsg, mdmContext);
+ handleUpdateResource(theMsg, mdmContext);
break;
case DELETE:
default:
@@ -105,12 +108,15 @@ public class MdmMessageHandler implements MessageHandler {
} finally {
// Interceptor call: MDM_AFTER_PERSISTED_RESOURCE_CHECKED
- ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, theMsg.getPayload(myFhirContext), theMsg.getOperationType());
+ IBaseResource targetResource = theMsg.getPayload(myFhirContext);
+ ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType());
outgoingMsg.setTransactionId(theMsg.getTransactionId());
HookParams params = new HookParams()
.add(ResourceOperationMessage.class, outgoingMsg)
- .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages());
+ .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages())
+ .add(MdmLinkChangeEvent.class, mdmContext.getMdmLinkChangeEvent());
+
myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params);
}
}
@@ -142,7 +148,7 @@ public class MdmMessageHandler implements MessageHandler {
}
}
- private void handleCreatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
+ private void handleCreateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext);
}
@@ -150,7 +156,7 @@ public class MdmMessageHandler implements MessageHandler {
return (IAnyResource) theMsg.getNewPayload(myFhirContext);
}
- private void handleUpdatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
+ private void handleUpdateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext);
}
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java
index 94db6849073..d23c27db9f1 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java
@@ -85,6 +85,8 @@ public class MdmEidUpdateService {
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theTargetResource, updateContext.getMatchedGoldenResource(), theMdmTransactionContext);
myMdmResourceDaoSvc.upsertGoldenResource(updateContext.getMatchedGoldenResource(), theMdmTransactionContext.getResourceType());
}
+
+ theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(updateContext.getExistingGoldenResource());
}
private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) {
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java
index fdd04914646..90020ffc259 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java
@@ -112,11 +112,14 @@ public class MdmMatchLinkSvc {
//Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource.
IAnyResource firstGoldenResource = goldenResources.get(0);
+ theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(firstGoldenResource);
+
goldenResources.subList(1, goldenResources.size())
.forEach(possibleDuplicateGoldenResource -> {
MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE;
outcome.setEidMatch(theCandidateList.isEidMatch());
myMdmLinkSvc.updateLink(firstGoldenResource, possibleDuplicateGoldenResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
+ theMdmTransactionContext.getMdmLinkChangeEvent().addDuplicateGoldenResourceId(possibleDuplicateGoldenResource);
});
}
}
@@ -129,6 +132,8 @@ public class MdmMatchLinkSvc {
// 2. Create source resource for the MDM source
// 3. UPDATE MDM LINK TABLE
myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
+
+ theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(newGoldenResource);
}
private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) {
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
index 1389c4c5ea1..601b3ce2920 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
@@ -106,5 +106,8 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
return channel.getQueueSizeForUnitTest();
}
+ public PointcutLatch getAfterMdmLatch() {
+ return myAfterMdmLatch;
+ }
}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
index c722b23dab2..7d24395852d 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
@@ -7,13 +7,16 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
+import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
+import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@@ -21,16 +24,21 @@ import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Pageable;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED;
@@ -67,6 +75,24 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
assertLinkCount(1);
}
+ @Test
+ public void testCreateLinkChangeEvent() throws InterruptedException {
+ Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC");
+ myMdmHelper.createWithLatch(pr);
+
+ ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class);
+ assertNotNull(resourceOperationMessage);
+ assertEquals(pr.getId(), resourceOperationMessage.getId());
+
+ MdmLink example = new MdmLink();
+ example.setSourcePid(pr.getIdElement().getIdPartAsLong());
+ MdmLink link = myMdmLinkDao.findAll(Example.of(example)).get(0);
+
+ MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class);
+ assertNotNull(linkChangeEvent);
+ assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong());
+ }
+
@Test
public void testSearchExpandingInterceptorWorks() {
SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(true);
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
new file mode 100644
index 00000000000..374d955aba8
--- /dev/null
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
@@ -0,0 +1,55 @@
+package ca.uhn.fhir.mdm.api;
+
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class MdmLinkChangeEvent {
+
+ private String myGoldenResourceId;
+ private Set myDuplicateGoldenResourceIds = new HashSet<>();
+
+ public String getGoldenResourceId() {
+ return myGoldenResourceId;
+ }
+
+ public void setGoldenResourceId(IBaseResource theGoldenResourceId) {
+ setGoldenResourceId(getIdAsString(theGoldenResourceId));
+ }
+
+ public void setGoldenResourceId(String theGoldenResourceId) {
+ myGoldenResourceId = theGoldenResourceId;
+ }
+
+ private String getIdAsString(IBaseResource theResource) {
+ if (theResource == null) {
+ return null;
+ }
+ IIdType idElement = theResource.getIdElement();
+ if (idElement == null) {
+ return null;
+ }
+ return idElement.getValueAsString();
+ }
+
+ public Set getDuplicateGoldenResourceIds() {
+ return myDuplicateGoldenResourceIds;
+ }
+
+ public void setDuplicateGoldenResourceIds(Set theDuplicateGoldenResourceIds) {
+ myDuplicateGoldenResourceIds = theDuplicateGoldenResourceIds;
+ }
+
+ public MdmLinkChangeEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) {
+ String id = getIdAsString(theDuplicateGoldenResourceId);
+ if (id != null) {
+ getDuplicateGoldenResourceIds().add(id);
+ }
+ return this;
+ }
+
+}
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
index a2fb07fe200..db699fd602b 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
@@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.model;
* #L%
*/
+import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
public class MdmTransactionContext {
@@ -45,6 +46,8 @@ public class MdmTransactionContext {
private String myResourceType;
+ private MdmLinkChangeEvent myMdmLinkChangeEvent = new MdmLinkChangeEvent();
+
public TransactionLogMessages getTransactionLogMessages() {
return myTransactionLogMessages;
}
@@ -92,4 +95,12 @@ public class MdmTransactionContext {
public void setResourceType(String myResourceType) {
this.myResourceType = myResourceType;
}
+
+ public MdmLinkChangeEvent getMdmLinkChangeEvent() {
+ return myMdmLinkChangeEvent;
+ }
+
+ public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) {
+ myMdmLinkChangeEvent = theMdmLinkChangeEvent;
+ }
}
From 0d34fe61c81da55d5d641a496cef957de1c5fd4b Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Fri, 30 Jul 2021 11:17:56 -0400
Subject: [PATCH 002/143] Adding tests
---
.../jpa/mdm/broker/MdmMessageHandler.java | 1 +
.../interceptor/MdmStorageInterceptorIT.java | 36 +++++++++++++++++++
.../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 16 +++++++--
3 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
index b80d2caafc7..967349c0823 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
@@ -111,6 +111,7 @@ public class MdmMessageHandler implements MessageHandler {
IBaseResource targetResource = theMsg.getPayload(myFhirContext);
ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType());
outgoingMsg.setTransactionId(theMsg.getTransactionId());
+ mdmContext.getMdmLinkChangeEvent().setTargetResourceId(targetResource);
HookParams params = new HookParams()
.add(ResourceOperationMessage.class, outgoingMsg)
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
index 7d24395852d..919911a3e25 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
@@ -91,6 +91,42 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class);
assertNotNull(linkChangeEvent);
assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong());
+ assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong());
+ }
+
+ @Test
+ public void testUpdateLinkChangeEvent() throws InterruptedException {
+ Patient patient1 = addExternalEID(buildJanePatient(), "eid-1");
+ patient1 = createPatientAndUpdateLinks(patient1);
+
+ Patient patient2 = addExternalEID(buildJanePatient(), "eid-2");
+ patient2 = createPatientAndUpdateLinks(patient2);
+
+ MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class);
+ assertNotNull(linkChangeEvent);
+// assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong());
+// assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong());
+ }
+
+ @Test
+ public void testDuplicateLinkChangeEvent() throws InterruptedException {
+ fail();
+
+ Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC");
+ myMdmHelper.createWithLatch(pr);
+
+ ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class);
+ assertNotNull(resourceOperationMessage);
+ assertEquals(pr.getId(), resourceOperationMessage.getId());
+
+ MdmLink example = new MdmLink();
+ example.setSourcePid(pr.getIdElement().getIdPartAsLong());
+ MdmLink link = myMdmLinkDao.findAll(Example.of(example)).get(0);
+
+ MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class);
+ assertNotNull(linkChangeEvent);
+ assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong());
+ assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong());
}
@Test
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
index 374d955aba8..c0593b2929d 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
@@ -1,15 +1,15 @@
package ca.uhn.fhir.mdm.api;
+import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
public class MdmLinkChangeEvent {
+ private String myTargetResourceId;
private String myGoldenResourceId;
private Set myDuplicateGoldenResourceIds = new HashSet<>();
@@ -36,6 +36,18 @@ public class MdmLinkChangeEvent {
return idElement.getValueAsString();
}
+ public String getTargetResourceId() {
+ return myTargetResourceId;
+ }
+
+ public void setTargetResourceId(IBaseResource theTargetResource) {
+ setTargetResourceId(getIdAsString(theTargetResource));
+ }
+
+ public void setTargetResourceId(String theTargetResourceId) {
+ myTargetResourceId = theTargetResourceId;
+ }
+
public Set getDuplicateGoldenResourceIds() {
return myDuplicateGoldenResourceIds;
}
From 891a6304d0e021021c540282e8ebf8b7adf6c0f4 Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Wed, 4 Aug 2021 11:06:14 -0400
Subject: [PATCH 003/143] WIP
---
.../dao/r4/FhirResourceDaoCodeSystemR4.java | 2 +-
.../jpa/mdm/broker/MdmMessageHandler.java | 1 -
.../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 6 +++
.../interceptor/MdmStorageInterceptorIT.java | 54 ++++++++-----------
.../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 36 ++++++++++++-
.../fhir/mdm/model/MdmTransactionContext.java | 1 +
6 files changed, 65 insertions(+), 35 deletions(-)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
index 2ff1ee51c70..335cef64375 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
@@ -139,7 +139,7 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao myDuplicateGoldenResourceIds = new HashSet<>();
public String getGoldenResourceId() {
@@ -64,4 +88,12 @@ public class MdmLinkChangeEvent {
return this;
}
+ @Override
+ public String toString() {
+ return "MdmLinkChangeEvent{" +
+ "myTargetResourceId='" + myTargetResourceId + '\'' +
+ ", myGoldenResourceId='" + myGoldenResourceId + '\'' +
+ ", myDuplicateGoldenResourceIds=" + myDuplicateGoldenResourceIds +
+ '}';
+ }
}
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
index db699fd602b..ce35ea3f150 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
@@ -103,4 +103,5 @@ public class MdmTransactionContext {
public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) {
myMdmLinkChangeEvent = theMdmLinkChangeEvent;
}
+
}
From 4be3334f6ff29f13629d65e67957475547de0c46 Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Wed, 4 Aug 2021 16:33:59 -0400
Subject: [PATCH 004/143] WIP after persistence checked
---
.../dao/r4/FhirResourceDaoCodeSystemR4.java | 2 +-
.../jpa/mdm/broker/MdmMessageHandler.java | 30 ++++++++++++++++---
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
index 6a01ab94c70..5f2aa32a2ec 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
@@ -138,7 +138,7 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao mdmLinkBySource = myMdmLinkDaoSvc.findMdmLinkBySource(targetResource);
+ if (!mdmLinkBySource.isPresent()) {
+ ourLog.warn("Unable to find link by source for {}", targetResource.getIdElement());
+ }
+
+ mdmLinkBySource.ifPresent(link -> {
+ linkChangeEvent.setMdmMatchResult(link.getMatchResult());
+ linkChangeEvent.setMdmLinkSource(link.getLinkSource());
+ linkChangeEvent.setEidMatch(link.isEidMatchPresent());
+ linkChangeEvent.setNewGoldenResource(link.getHadToCreateNewGoldenResource());
+ linkChangeEvent.setScore(link.getScore());
+ linkChangeEvent.setRuleCount(link.getRuleCount());
+ });
+
HookParams params = new HookParams()
.add(ResourceOperationMessage.class, outgoingMsg)
.add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages())
From 8670f107b5118c274b00c92327478f26f55bc9ad Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Tue, 17 Aug 2021 13:17:44 -0400
Subject: [PATCH 005/143] Updated link expansion
---
.../ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java | 9 +++
.../fhir/jpa/dao/mdm/MdmLinkExpandSvc.java | 22 +++++-
.../fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java | 9 +++
.../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 72 ++++++++++++++++++-
4 files changed, 106 insertions(+), 6 deletions(-)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java
index 16834facbe2..be0a6289a4e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java
@@ -67,4 +67,13 @@ public interface IMdmLinkDao extends JpaRepository {
"AND ml.myMatchResult=:matchResult")
List expandPidsBySourcePidAndMatchResult(@Param("sourcePid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum);
+ @Query("SELECT DISTINCT ml.myGoldenResourcePid as goldenPid, ml.mySourcePid as sourcePid " +
+ "FROM MdmLink ml " +
+ "INNER JOIN MdmLink ml2 " +
+ "ON ml.myGoldenResourcePid = ml2.myGoldenResourcePid " +
+ "WHERE (ml2.mySourcePid = :sourceOrGoldenPid OR ml2.myGoldenResourcePid = :sourceOrGoldenPid) " +
+ "AND ml2.myMatchResult=:matchResult " +
+ "AND ml.myMatchResult=:matchResult")
+ List expandPidsBySourceOrGoldenResourcePidAndMatchResult(@Param("sourceOrGoldenPid") Long theSourceOrGoldenPid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum);
+
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java
index 66af7f5a706..b03e22d04b8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java
@@ -72,6 +72,19 @@ public class MdmLinkExpandSvc {
return expandMdmBySourceResourcePid(pidOrThrowException);
}
+ /**
+ * Given a resource ID of a source resource or golden resource, perform MDM expansion and return all the resource
+ * IDs of all resources that are MDM-Matched to this resource.
+ *
+ * @param theId The Resource ID of the resource to MDM-Expand
+ * @return A set of strings representing the FHIR ids of the expanded resources.
+ */
+ public Set expandMdmBySourceOrGoldenResourceId(IIdType theId) {
+ ourLog.debug("About to expand source resource with resource id {}", theId);
+ Long pidOrThrowException = myIdHelperService.getPidOrThrowException(theId);
+ return flatten(myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(pidOrThrowException, MdmMatchResultEnum.MATCH));
+ }
+
/**
* Given a PID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are
* MDM-Matched to this resource.
@@ -81,14 +94,17 @@ public class MdmLinkExpandSvc {
*/
public Set expandMdmBySourceResourcePid(Long theSourceResourcePid) {
ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid);
- List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH);
+ return flatten(myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH));
+ }
+
+ protected Set flatten(List thePidTuples) {
Set flattenedPids = new HashSet<>();
- goldenPidSourcePidTuples.forEach(tuple -> {
+ thePidTuples.forEach(tuple -> {
flattenedPids.add(tuple.getSourcePid());
flattenedPids.add(tuple.getGoldenPid());
});
Set resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids);
- ourLog.debug("Pid {} has been expanded to [{}]", theSourceResourcePid, String.join(",", resourceIds));
+ ourLog.debug("Expanded pids are [{}]", String.join(",", resourceIds));
return resourceIds;
}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java
index ae034af6fa7..b5deb25f0c6 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java
@@ -83,6 +83,15 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
assertThat(lists, hasSize(10));
+ lists.stream()
+ .forEach(tuple -> {
+ assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong())));
+ assertThat(tuple.getSourcePid(), is(in(expectedExpandedPids)));
+ });
+
+ lists = myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(mdmLinks.get(0).getGoldenResourcePid(), MdmMatchResultEnum.MATCH);
+ assertThat(lists, hasSize(10));
+
lists.stream()
.forEach(tuple -> {
assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong())));
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
index e2d6b199812..8e306c16e6e 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
@@ -30,9 +30,21 @@ import java.util.Set;
public class MdmLinkChangeEvent implements IModelJson {
- @JsonProperty(value = "targetResourceId")
+ @JsonProperty(value = "matchResult")
+ private MdmMatchResultEnum myMdmMatchResult;
+ @JsonProperty(value = "linkSource")
+ private MdmLinkSourceEnum myMdmLinkSource;
+ @JsonProperty(value = "eidMatch")
+ private Boolean myEidMatch;
+ @JsonProperty(value = "newGoldenResource")
+ private Boolean myNewGoldenResource;
+ @JsonProperty(value = "score")
+ private Double myScore;
+ @JsonProperty(value = "ruleCount")
+ private Long myRuleCount;
+ @JsonProperty(value = "targetResourceId", required = true)
private String myTargetResourceId;
- @JsonProperty(value = "goldenResourceId")
+ @JsonProperty(value = "goldenResourceId", required = true)
private String myGoldenResourceId;
@JsonProperty(value = "duplicateResourceIds")
private Set myDuplicateGoldenResourceIds = new HashSet<>();
@@ -88,10 +100,64 @@ public class MdmLinkChangeEvent implements IModelJson {
return this;
}
+ public MdmMatchResultEnum getMdmMatchResult() {
+ return myMdmMatchResult;
+ }
+
+ public void setMdmMatchResult(MdmMatchResultEnum theMdmMatchResult) {
+ myMdmMatchResult = theMdmMatchResult;
+ }
+
+ public MdmLinkSourceEnum getMdmLinkSource() {
+ return myMdmLinkSource;
+ }
+
+ public void setMdmLinkSource(MdmLinkSourceEnum theMdmLinkSource) {
+ myMdmLinkSource = theMdmLinkSource;
+ }
+
+ public Boolean getEidMatch() {
+ return myEidMatch;
+ }
+
+ public void setEidMatch(Boolean theEidMatch) {
+ myEidMatch = theEidMatch;
+ }
+
+ public Boolean getNewGoldenResource() {
+ return myNewGoldenResource;
+ }
+
+ public void setNewGoldenResource(Boolean theNewGoldenResource) {
+ myNewGoldenResource = theNewGoldenResource;
+ }
+
+ public Double getScore() {
+ return myScore;
+ }
+
+ public void setScore(Double theScore) {
+ myScore = theScore;
+ }
+
+ public Long getRuleCount() {
+ return myRuleCount;
+ }
+
+ public void setRuleCount(Long theRuleCount) {
+ myRuleCount = theRuleCount;
+ }
+
@Override
public String toString() {
return "MdmLinkChangeEvent{" +
- "myTargetResourceId='" + myTargetResourceId + '\'' +
+ "myMdmMatchResult=" + myMdmMatchResult +
+ ", myMdmLinkSource=" + myMdmLinkSource +
+ ", myEidMatch=" + myEidMatch +
+ ", myNewGoldenResource=" + myNewGoldenResource +
+ ", myScore=" + myScore +
+ ", myRuleCount=" + myRuleCount +
+ ", myTargetResourceId='" + myTargetResourceId + '\'' +
", myGoldenResourceId='" + myGoldenResourceId + '\'' +
", myDuplicateGoldenResourceIds=" + myDuplicateGoldenResourceIds +
'}';
From 07937e4431024bca5e58261af65a8405743c24d3 Mon Sep 17 00:00:00 2001
From: Tadgh
Date: Thu, 19 Aug 2021 10:35:14 -0400
Subject: [PATCH 006/143] Start with failing test
---
...irResourceDaoR4VersionedReferenceTest.java | 25 ++
.../test/resources/npe-causing-bundle.json | 402 ++++++++++++++++++
2 files changed, 427 insertions(+)
create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
index 76ebb3e0b74..d682d9a6304 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
@@ -1,8 +1,10 @@
package ca.uhn.fhir.jpa.dao.r4;
+import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
+import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam;
@@ -20,8 +22,10 @@ import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Task;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
@@ -782,4 +786,25 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
}
+ @Test
+ @DisplayName("GH-2901 Test no NPE is thrown on autoversioned references")
+ public void testNoNpeOnEoBBundle() {
+ myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
+ List strings = Arrays.asList(
+ "ExplanationOfBenefit.patient",
+ "ExplanationOfBenefit.insurer",
+ "ExplanationOfBenefit.provider",
+ "ExplanationOfBenefit.careTeam.provider",
+ "ExplanationOfBenefit.insurance.coverage",
+ "ExplanationOfBenefit.payee.party"
+ );
+ myModelConfig.setAutoVersionReferenceAtPaths(new HashSet(strings));
+
+ Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, new InputStreamReader(FhirResourceDaoR4VersionedReferenceTest.class.getResourceAsStream("/npe-causing-bundle.json")));
+
+ Bundle transaction = mySystemDao.transaction(new SystemRequestDetails(), bundle);
+
+ }
+
+
}
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json b/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json
new file mode 100644
index 00000000000..ef1d7645e5b
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json
@@ -0,0 +1,402 @@
+{
+ "resourceType": "Bundle",
+ "type": "transaction",
+ "entry": [ {
+ "resource": {
+ "resourceType": "ExplanationOfBenefit",
+ "id": "26d4cebd-95c6-39ea-855c-dc819bc68d08",
+ "meta": {
+ "lastUpdated": "2016-01-01T00:56:00.000-05:00",
+ "profile": [ "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-ExplanationOfBenefit-Inpatient-Institutional" ]
+ },
+ "identifier": [ {
+ "type": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType",
+ "code": "uc"
+ } ]
+ },
+ "system": "fhir/CodeSystem/sid/eob-inpatient-claim-id",
+ "value": "20550047"
+ } ],
+ "status": "active",
+ "type": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/claim-type",
+ "code": "institutional"
+ } ]
+ },
+ "use": "claim",
+ "patient": {
+ "reference": "Patient/123"
+ },
+ "billablePeriod": {
+ "start": "2015-12-14T00:00:00-05:00"
+ },
+ "created": "2021-08-16T13:54:10-04:00",
+ "insurer": {
+ "reference": "Organization/1"
+ },
+ "provider": {
+ "reference": "Organization/b9d22776-1ee9-3843-bc48-b4bf67861483"
+ },
+ "outcome": "complete",
+ "careTeam": [ {
+ "sequence": 1,
+ "provider": {
+ "reference": "Practitioner/1"
+ },
+ "role": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/claimcareteamrole",
+ "code": "primary"
+ } ]
+ }
+ }, {
+ "sequence": 2,
+ "provider": {
+ "reference": "Practitioner/2"
+ },
+ "role": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBClaimCareTeamRole",
+ "code": "attending"
+ } ]
+ }
+ }, {
+ "sequence": 3,
+ "provider": {
+ "reference": "Practitioner/3"
+ },
+ "role": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBClaimCareTeamRole",
+ "code": "performing"
+ } ]
+ }
+ } ],
+ "supportingInfo": [ {
+ "sequence": 1,
+ "category": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType",
+ "code": "admissionperiod"
+ } ]
+ },
+ "timingPeriod": {
+ "start": "2015-12-14T00:00:00-05:00",
+ "end": "2016-01-11T14:11:00-05:00"
+ }
+ }, {
+ "sequence": 2,
+ "category": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType",
+ "code": "clmrecvddate"
+ } ]
+ },
+ "timingDate": "2018-12-23"
+ }, {
+ "sequence": 3,
+ "category": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType",
+ "code": "admtype"
+ } ]
+ },
+ "code": {
+ "coding": [ {
+ "system": "https://www.nubc.org/CodeSystem/PriorityTypeOfAdmitOrVisit",
+ "code": "4"
+ } ]
+ }
+ } ],
+ "diagnosis": [ {
+ "sequence": 1,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "Z38.00"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ } ],
+ "insurance": [ {
+ "focal": true,
+ "coverage": {
+ "reference": "Coverage/10"
+ }
+ } ],
+ "total": [ {
+ "category": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/adjudication",
+ "code": "benefit"
+ } ]
+ },
+ "amount": {
+ "value": 1039.28
+ }
+ }, {
+ "category": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/adjudication",
+ "code": "submitted"
+ } ]
+ },
+ "amount": {
+ "value": 2011
+ }
+ } ]
+ },
+ "request": {
+ "method": "PUT",
+ "url": "ExplanationOfBenefit/26d4cebd-95c6-39ea-855c-dc819bc68d08"
+ }
+ }, {
+ "resource": {
+ "resourceType": "ExplanationOfBenefit",
+ "id": "a25f1c3a-09b9-3f17-8f1b-0fbbf6391fce",
+ "meta": {
+ "lastUpdated": "2016-01-01T00:58:00.000-05:00",
+ "profile": [ "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-ExplanationOfBenefit-Inpatient-Institutional" ]
+ },
+ "identifier": [ {
+ "type": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType",
+ "code": "uc"
+ } ]
+ },
+ "system": "fhir/CodeSystem/sid/eob-inpatient-claim-id",
+ "value": "20586901"
+ } ],
+ "status": "active",
+ "type": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/claim-type",
+ "code": "institutional"
+ } ]
+ },
+ "use": "claim",
+ "patient": {
+ "reference": "Patient/123"
+ },
+ "billablePeriod": {
+ "start": "2015-12-18T00:00:00-05:00"
+ },
+ "created": "2021-08-16T13:54:10-04:00",
+ "insurer": {
+ "reference": "Organization/1"
+ },
+ "provider": {
+ "reference": "Organization/d10823cf-ee15-3a0e-a12e-1509cd18cda4"
+ },
+ "outcome": "complete",
+ "careTeam": [ {
+ "sequence": 1,
+ "provider": {
+ "reference": "Practitioner/4"
+ },
+ "role": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/claimcareteamrole",
+ "code": "primary"
+ } ]
+ }
+ }, {
+ "sequence": 2,
+ "provider": {
+ "reference": "Practitioner/5"
+ },
+ "role": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBClaimCareTeamRole",
+ "code": "attending"
+ } ]
+ }
+ }, {
+ "sequence": 3,
+ "provider": {
+ "reference": "Practitioner/6"
+ },
+ "role": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBClaimCareTeamRole",
+ "code": "performing"
+ } ]
+ }
+ } ],
+ "supportingInfo": [ {
+ "sequence": 1,
+ "category": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType",
+ "code": "admissionperiod"
+ } ]
+ },
+ "timingPeriod": {
+ "start": "2015-12-18T00:00:00-05:00",
+ "end": "2016-01-11T14:12:00-05:00"
+ }
+ }, {
+ "sequence": 2,
+ "category": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType",
+ "code": "clmrecvddate"
+ } ]
+ },
+ "timingDate": "2018-12-29"
+ }, {
+ "sequence": 3,
+ "category": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType",
+ "code": "admtype"
+ } ]
+ },
+ "code": {
+ "coding": [ {
+ "system": "https://www.nubc.org/CodeSystem/PriorityTypeOfAdmitOrVisit",
+ "code": "4"
+ } ]
+ }
+ } ],
+ "diagnosis": [ {
+ "sequence": 1,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "Z38.00"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ }, {
+ "sequence": 2,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "P96.89"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ }, {
+ "sequence": 3,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "R25.8"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ }, {
+ "sequence": 4,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "P08.21"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ }, {
+ "sequence": 5,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "P92.5"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ }, {
+ "sequence": 6,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "P92.6"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ }, {
+ "sequence": 7,
+ "diagnosisCodeableConcept": {
+ "coding": [ {
+ "system": "http://hl7.org/fhir/sid/icd-10-cm",
+ "code": "Z23"
+ } ]
+ },
+ "type": [ {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/ex-diagnosistype",
+ "code": "principal"
+ } ]
+ } ]
+ } ],
+ "insurance": [ {
+ "focal": true,
+ "coverage": {
+ "reference": "Coverage/10"
+ }
+ } ],
+ "total": [ {
+ "category": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/adjudication",
+ "code": "benefit"
+ } ]
+ },
+ "amount": {
+ "value": 1421.31
+ }
+ }, {
+ "category": {
+ "coding": [ {
+ "system": "http://terminology.hl7.org/CodeSystem/adjudication",
+ "code": "submitted"
+ } ]
+ },
+ "amount": {
+ "value": 2336
+ }
+ } ]
+ },
+ "request": {
+ "method": "PUT",
+ "url": "ExplanationOfBenefit/a25f1c3a-09b9-3f17-8f1b-0fbbf6391fce"
+ }
+ } ] }
From 19d181989c317a81b2a5a1eed49229419ed37649 Mon Sep 17 00:00:00 2001
From: Tadgh
Date: Thu, 19 Aug 2021 14:52:48 -0400
Subject: [PATCH 007/143] Add another failing test
---
.../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 1 +
.../dao/index/DaoResourceLinkResolver.java | 1 -
...irResourceDaoR4VersionedReferenceTest.java | 26 +++++++++++++++++++
.../test/resources/npe-causing-bundle.json | 24 ++++++++---------
4 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
index bd2e964e523..1fc0bb8f9d2 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
@@ -1206,6 +1206,7 @@ public abstract class BaseHapiFhirDao extends BaseStora
if (thePerformIndexing || ((ResourceTable) theEntity).getVersion() == 1) {
newParams = new ResourceIndexedSearchParams();
+ //FIX ME GGG: This is where the placeholder references end up getting created, deeeeeep down the stakc.
mySearchParamWithInlineReferencesExtractor.populateFromResource(newParams, theTransactionDetails, entity, theResource, existingParams, theRequest, thePerformIndexing);
changed = populateResourceIntoEntity(theTransactionDetails, theRequest, theResource, entity, true);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
index a219f39f292..07daf83c2da 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
@@ -94,7 +94,6 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
throw new InvalidRequestException("Resource " + resName + "/" + idPart + " not found, specified in path: " + theSourcePath);
}
-
resolvedResource = createdTableOpt.get();
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
index d682d9a6304..7476befb401 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
@@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
+import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
@@ -34,7 +35,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -803,7 +806,30 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, new InputStreamReader(FhirResourceDaoR4VersionedReferenceTest.class.getResourceAsStream("/npe-causing-bundle.json")));
Bundle transaction = mySystemDao.transaction(new SystemRequestDetails(), bundle);
+ }
+ @Test
+ public void testAutoVersionPathsWithAutoCreatePlaceholders() {
+ myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
+
+ Observation obs = new Observation();
+ obs.setId("Observation/CDE");
+ obs.setSubject(new Reference("Patient/ABC"));
+ DaoMethodOutcome update = myObservationDao.create(obs);
+ Observation resource = (Observation)update.getResource();
+ String versionedPatientReference = resource.getSubject().getReference();
+ assertThat(versionedPatientReference, is(equalTo("Patient/ABC")));
+
+ myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
+
+ obs = new Observation();
+ obs.setId("Observation/DEF");
+ obs.setSubject(new Reference("Patient/RED"));
+ update = myObservationDao.create(obs);
+ resource = (Observation)update.getResource();
+ versionedPatientReference = resource.getSubject().getReference();
+
+ assertThat(versionedPatientReference, is(equalTo("Patient/RED/_history/1")));
}
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json b/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json
index ef1d7645e5b..a645b167d35 100644
--- a/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json
+++ b/hapi-fhir-jpaserver-base/src/test/resources/npe-causing-bundle.json
@@ -28,14 +28,14 @@
},
"use": "claim",
"patient": {
- "reference": "Patient/123"
+ "reference": "Patient/ABC"
},
"billablePeriod": {
"start": "2015-12-14T00:00:00-05:00"
},
"created": "2021-08-16T13:54:10-04:00",
"insurer": {
- "reference": "Organization/1"
+ "reference": "Organization/A"
},
"provider": {
"reference": "Organization/b9d22776-1ee9-3843-bc48-b4bf67861483"
@@ -44,7 +44,7 @@
"careTeam": [ {
"sequence": 1,
"provider": {
- "reference": "Practitioner/1"
+ "reference": "Practitioner/H"
},
"role": {
"coding": [ {
@@ -55,7 +55,7 @@
}, {
"sequence": 2,
"provider": {
- "reference": "Practitioner/2"
+ "reference": "Practitioner/I"
},
"role": {
"coding": [ {
@@ -66,7 +66,7 @@
}, {
"sequence": 3,
"provider": {
- "reference": "Practitioner/3"
+ "reference": "Practitioner/J"
},
"role": {
"coding": [ {
@@ -129,7 +129,7 @@
"insurance": [ {
"focal": true,
"coverage": {
- "reference": "Coverage/10"
+ "reference": "Coverage/G"
}
} ],
"total": [ {
@@ -185,14 +185,14 @@
},
"use": "claim",
"patient": {
- "reference": "Patient/123"
+ "reference": "Patient/ABC"
},
"billablePeriod": {
"start": "2015-12-18T00:00:00-05:00"
},
"created": "2021-08-16T13:54:10-04:00",
"insurer": {
- "reference": "Organization/1"
+ "reference": "Organization/A"
},
"provider": {
"reference": "Organization/d10823cf-ee15-3a0e-a12e-1509cd18cda4"
@@ -201,7 +201,7 @@
"careTeam": [ {
"sequence": 1,
"provider": {
- "reference": "Practitioner/4"
+ "reference": "Practitioner/D"
},
"role": {
"coding": [ {
@@ -212,7 +212,7 @@
}, {
"sequence": 2,
"provider": {
- "reference": "Practitioner/5"
+ "reference": "Practitioner/E"
},
"role": {
"coding": [ {
@@ -223,7 +223,7 @@
}, {
"sequence": 3,
"provider": {
- "reference": "Practitioner/6"
+ "reference": "Practitioner/F"
},
"role": {
"coding": [ {
@@ -370,7 +370,7 @@
"insurance": [ {
"focal": true,
"coverage": {
- "reference": "Coverage/10"
+ "reference": "Coverage/G"
}
} ],
"total": [ {
From 1e41621eca369ceb8e3a116db4002d1b334cc33f Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Thu, 19 Aug 2021 16:43:51 -0400
Subject: [PATCH 008/143] Rolled back changes
---
.../ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java | 9 --------
.../fhir/jpa/dao/mdm/MdmLinkExpandSvc.java | 22 +++----------------
.../fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java | 9 --------
3 files changed, 3 insertions(+), 37 deletions(-)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java
index be0a6289a4e..16834facbe2 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java
@@ -67,13 +67,4 @@ public interface IMdmLinkDao extends JpaRepository {
"AND ml.myMatchResult=:matchResult")
List expandPidsBySourcePidAndMatchResult(@Param("sourcePid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum);
- @Query("SELECT DISTINCT ml.myGoldenResourcePid as goldenPid, ml.mySourcePid as sourcePid " +
- "FROM MdmLink ml " +
- "INNER JOIN MdmLink ml2 " +
- "ON ml.myGoldenResourcePid = ml2.myGoldenResourcePid " +
- "WHERE (ml2.mySourcePid = :sourceOrGoldenPid OR ml2.myGoldenResourcePid = :sourceOrGoldenPid) " +
- "AND ml2.myMatchResult=:matchResult " +
- "AND ml.myMatchResult=:matchResult")
- List expandPidsBySourceOrGoldenResourcePidAndMatchResult(@Param("sourceOrGoldenPid") Long theSourceOrGoldenPid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum);
-
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java
index b03e22d04b8..66af7f5a706 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java
@@ -72,19 +72,6 @@ public class MdmLinkExpandSvc {
return expandMdmBySourceResourcePid(pidOrThrowException);
}
- /**
- * Given a resource ID of a source resource or golden resource, perform MDM expansion and return all the resource
- * IDs of all resources that are MDM-Matched to this resource.
- *
- * @param theId The Resource ID of the resource to MDM-Expand
- * @return A set of strings representing the FHIR ids of the expanded resources.
- */
- public Set expandMdmBySourceOrGoldenResourceId(IIdType theId) {
- ourLog.debug("About to expand source resource with resource id {}", theId);
- Long pidOrThrowException = myIdHelperService.getPidOrThrowException(theId);
- return flatten(myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(pidOrThrowException, MdmMatchResultEnum.MATCH));
- }
-
/**
* Given a PID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are
* MDM-Matched to this resource.
@@ -94,17 +81,14 @@ public class MdmLinkExpandSvc {
*/
public Set expandMdmBySourceResourcePid(Long theSourceResourcePid) {
ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid);
- return flatten(myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH));
- }
-
- protected Set flatten(List thePidTuples) {
+ List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH);
Set flattenedPids = new HashSet<>();
- thePidTuples.forEach(tuple -> {
+ goldenPidSourcePidTuples.forEach(tuple -> {
flattenedPids.add(tuple.getSourcePid());
flattenedPids.add(tuple.getGoldenPid());
});
Set resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids);
- ourLog.debug("Expanded pids are [{}]", String.join(",", resourceIds));
+ ourLog.debug("Pid {} has been expanded to [{}]", theSourceResourcePid, String.join(",", resourceIds));
return resourceIds;
}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java
index b5deb25f0c6..ae034af6fa7 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java
@@ -83,15 +83,6 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
assertThat(lists, hasSize(10));
- lists.stream()
- .forEach(tuple -> {
- assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong())));
- assertThat(tuple.getSourcePid(), is(in(expectedExpandedPids)));
- });
-
- lists = myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(mdmLinks.get(0).getGoldenResourcePid(), MdmMatchResultEnum.MATCH);
- assertThat(lists, hasSize(10));
-
lists.stream()
.forEach(tuple -> {
assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong())));
From 3556299332cce58ff58d07dd3b660fa7b24cd891 Mon Sep 17 00:00:00 2001
From: Tadgh
Date: Thu, 19 Aug 2021 17:25:15 -0400
Subject: [PATCH 009/143] Add another failing Test
---
.../FhirResourceDaoR4VersionedReferenceTest.java | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
index 7476befb401..f61bf54ae59 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
@@ -832,5 +832,19 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
assertThat(versionedPatientReference, is(equalTo("Patient/RED/_history/1")));
}
+ @Test
+ public void testNoNpeMinimal() {
+ myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
+ myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
+
+ Observation obs = new Observation();
+ obs.setId("Observation/DEF");
+ obs.setSubject(new Reference("Patient/RED"));
+ BundleBuilder builder = new BundleBuilder(myFhirCtx);
+ builder.addTransactionUpdateEntry(obs);
+
+ mySystemDao.transaction(new SystemRequestDetails(), (Bundle) builder.getBundle());
+ }
+
}
From c2e3401185a1795909f1d0b1ba37d849927bd932 Mon Sep 17 00:00:00 2001
From: Tadgh
Date: Thu, 19 Aug 2021 17:25:51 -0400
Subject: [PATCH 010/143] Move display name
---
.../jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
index f61bf54ae59..f2c661b4522 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
@@ -790,7 +790,6 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
}
@Test
- @DisplayName("GH-2901 Test no NPE is thrown on autoversioned references")
public void testNoNpeOnEoBBundle() {
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
List strings = Arrays.asList(
@@ -833,6 +832,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
}
@Test
+ @DisplayName("GH-2901 Test no NPE is thrown on autoversioned references")
public void testNoNpeMinimal() {
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
From 6ea58f74d1e3317683242a8fd0f94ac8bb2b881d Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Mon, 23 Aug 2021 10:55:09 -0400
Subject: [PATCH 011/143] Code review
---
.../jpa/mdm/broker/MdmMessageHandler.java | 22 +++-----
.../jpa/mdm/config/MdmConsumerConfig.java | 11 +++-
.../jpa/mdm/svc/IMdmModelConverterSvc.java | 39 ++++++++++++++
...cImpl.java => MdmLinkQuerySvcImplSvc.java} | 32 ++++--------
.../jpa/mdm/svc/MdmModelConverterSvcImpl.java | 52 +++++++++++++++++++
.../interceptor/MdmStorageInterceptorIT.java | 7 +--
...LinkChangeEvent.java => MdmLinkEvent.java} | 21 +++++++-
.../java/ca/uhn/fhir/mdm/api/MdmLinkJson.java | 19 ++++++-
.../fhir/mdm/model/MdmTransactionContext.java | 12 ++---
9 files changed, 160 insertions(+), 55 deletions(-)
create mode 100644 hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java
rename hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/{MdmLinkQuerySvcImpl.java => MdmLinkQuerySvcImplSvc.java} (73%)
create mode 100644 hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java
rename hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/{MdmLinkChangeEvent.java => MdmLinkEvent.java} (86%)
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
index 5f103311abb..701f00b034b 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java
@@ -26,15 +26,14 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
-import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl;
+import ca.uhn.fhir.jpa.mdm.svc.IMdmModelConverterSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
-import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
-import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
+import ca.uhn.fhir.mdm.api.MdmLinkEvent;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
@@ -42,7 +41,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
@@ -69,6 +67,8 @@ public class MdmMessageHandler implements MessageHandler {
private MdmResourceFilteringSvc myMdmResourceFilteringSvc;
@Autowired
private IMdmSettings myMdmSettings;
+ @Autowired
+ private IMdmModelConverterSvc myModelConverter;
@Override
public void handleMessage(Message> theMessage) throws MessagingException {
@@ -119,25 +119,17 @@ public class MdmMessageHandler implements MessageHandler {
ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType());
outgoingMsg.setTransactionId(theMsg.getTransactionId());
- MdmLinkChangeEvent linkChangeEvent = mdmContext.getMdmLinkChangeEvent();
+ MdmLinkEvent linkChangeEvent = mdmContext.getMdmLinkChangeEvent();
Optional mdmLinkBySource = myMdmLinkDaoSvc.findMdmLinkBySource(targetResource);
if (!mdmLinkBySource.isPresent()) {
ourLog.warn("Unable to find link by source for {}", targetResource.getIdElement());
}
- mdmLinkBySource.ifPresent(link -> {
- linkChangeEvent.setMdmMatchResult(link.getMatchResult());
- linkChangeEvent.setMdmLinkSource(link.getLinkSource());
- linkChangeEvent.setEidMatch(link.isEidMatchPresent());
- linkChangeEvent.setNewGoldenResource(link.getHadToCreateNewGoldenResource());
- linkChangeEvent.setScore(link.getScore());
- linkChangeEvent.setRuleCount(link.getRuleCount());
- });
-
+ mdmLinkBySource.ifPresent(link -> linkChangeEvent.setFromLink(myModelConverter.toJson(link)));
HookParams params = new HookParams()
.add(ResourceOperationMessage.class, outgoingMsg)
.add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages())
- .add(MdmLinkChangeEvent.class, mdmContext.getMdmLinkChangeEvent());
+ .add(MdmLinkEvent.class, mdmContext.getMdmLinkChangeEvent());
myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params);
}
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java
index 51981cdecaa..56f81f8b87a 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java
@@ -31,15 +31,17 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkFactory;
import ca.uhn.fhir.jpa.mdm.interceptor.IMdmStorageInterceptor;
import ca.uhn.fhir.jpa.mdm.interceptor.MdmStorageInterceptor;
import ca.uhn.fhir.jpa.mdm.svc.GoldenResourceMergerSvcImpl;
+import ca.uhn.fhir.jpa.mdm.svc.IMdmModelConverterSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmClearSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmControllerSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmEidUpdateService;
import ca.uhn.fhir.jpa.mdm.svc.MdmGoldenResourceDeletingSvc;
-import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImpl;
+import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImplSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchFinderSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
+import ca.uhn.fhir.jpa.mdm.svc.MdmModelConverterSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc;
@@ -179,7 +181,12 @@ public class MdmConsumerConfig {
@Bean
IMdmLinkQuerySvc mdmLinkQuerySvc() {
- return new MdmLinkQuerySvcImpl();
+ return new MdmLinkQuerySvcImplSvc();
+ }
+
+ @Bean
+ IMdmModelConverterSvc mdmModelConverterSvc() {
+ return new MdmModelConverterSvcImpl();
}
@Bean
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java
new file mode 100644
index 00000000000..67c534a9489
--- /dev/null
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java
@@ -0,0 +1,39 @@
+package ca.uhn.fhir.jpa.mdm.svc;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server - Master Data Management
+ * %%
+ * Copyright (C) 2014 - 2021 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import ca.uhn.fhir.jpa.entity.MdmLink;
+import ca.uhn.fhir.mdm.api.MdmLinkJson;
+
+/**
+ * Contract for decoupling API dependency from the base / JPA modules.
+ */
+public interface IMdmModelConverterSvc {
+
+ /**
+ * Creates JSON representation of the provided MDM link
+ *
+ * @param theLink Link to convert
+ * @return Returns the converted link
+ */
+ public MdmLinkJson toJson(MdmLink theLink);
+
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java
similarity index 73%
rename from hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImpl.java
rename to hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java
index ea00e81ec4d..996dbe8e5cc 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImpl.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java
@@ -36,20 +36,24 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
-public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
+public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc {
- private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImpl.class);
+ private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImplSvc.class);
+
+ @Autowired
+ MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
IdHelperService myIdHelperService;
+
@Autowired
- MdmLinkDaoSvc myMdmLinkDaoSvc;
+ IMdmModelConverterSvc myMdmModelConverterSvc;
@Override
public Page queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
Example exampleLink = exampleLinkFromParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource);
Page mdmLinkByExample = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest);
- Page map = mdmLinkByExample.map(this::toJson);
+ Page map = mdmLinkByExample.map(myMdmModelConverterSvc::toJson);
return map;
}
@@ -57,28 +61,10 @@ public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
public Page getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
Example exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null);
Page mdmLinkPage = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest);
- Page map = mdmLinkPage.map(this::toJson);
+ Page map = mdmLinkPage.map(myMdmModelConverterSvc::toJson);
return map;
}
- private MdmLinkJson toJson(MdmLink theLink) {
- MdmLinkJson retval = new MdmLinkJson();
- String sourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourcePid()).toVersionless().getValue();
- retval.setSourceId(sourceId);
- String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePid()).toVersionless().getValue();
- retval.setGoldenResourceId(goldenResourceId);
- retval.setCreated(theLink.getCreated());
- retval.setEidMatch(theLink.getEidMatch());
- retval.setLinkSource(theLink.getLinkSource());
- retval.setMatchResult(theLink.getMatchResult());
- retval.setLinkCreatedNewResource(theLink.getHadToCreateNewGoldenResource());
- retval.setScore(theLink.getScore());
- retval.setUpdated(theLink.getUpdated());
- retval.setVector(theLink.getVector());
- retval.setVersion(theLink.getVersion());
- return retval;
- }
-
private Example exampleLinkFromParameters(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) {
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
if (theGoldenResourceId != null) {
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java
new file mode 100644
index 00000000000..195ac388e68
--- /dev/null
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java
@@ -0,0 +1,52 @@
+package ca.uhn.fhir.jpa.mdm.svc;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server - Master Data Management
+ * %%
+ * Copyright (C) 2014 - 2021 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.entity.MdmLink;
+import ca.uhn.fhir.mdm.api.MdmLinkJson;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class MdmModelConverterSvcImpl implements IMdmModelConverterSvc {
+
+ @Autowired
+ IdHelperService myIdHelperService;
+
+ public MdmLinkJson toJson(MdmLink theLink) {
+ MdmLinkJson retval = new MdmLinkJson();
+ String sourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourcePid()).toVersionless().getValue();
+ retval.setSourceId(sourceId);
+ String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePid()).toVersionless().getValue();
+ retval.setGoldenResourceId(goldenResourceId);
+ retval.setCreated(theLink.getCreated());
+ retval.setEidMatch(theLink.getEidMatch());
+ retval.setLinkSource(theLink.getLinkSource());
+ retval.setMatchResult(theLink.getMatchResult());
+ retval.setLinkCreatedNewResource(theLink.getHadToCreateNewGoldenResource());
+ retval.setScore(theLink.getScore());
+ retval.setUpdated(theLink.getUpdated());
+ retval.setVector(theLink.getVector());
+ retval.setVersion(theLink.getVersion());
+ retval.setRuleCount(theLink.getRuleCount());
+ return retval;
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
index 4d729730f7c..cd1518cbc59 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java
@@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
-import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
+import ca.uhn.fhir.mdm.api.MdmLinkEvent;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
@@ -32,14 +32,11 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
-import org.springframework.data.domain.ExampleMatcher;
-import org.springframework.data.domain.Pageable;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import java.util.Date;
import java.util.List;
-import java.util.Optional;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED;
@@ -89,7 +86,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
MdmLink link = getLinkByTargetId(pr);
- MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class);
+ MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class);
assertNotNull(linkChangeEvent);
assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong());
assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong());
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java
similarity index 86%
rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java
index 8e306c16e6e..7eda6f328d8 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java
@@ -28,7 +28,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
import java.util.HashSet;
import java.util.Set;
-public class MdmLinkChangeEvent implements IModelJson {
+public class MdmLinkEvent implements IModelJson {
@JsonProperty(value = "matchResult")
private MdmMatchResultEnum myMdmMatchResult;
@@ -92,7 +92,7 @@ public class MdmLinkChangeEvent implements IModelJson {
myDuplicateGoldenResourceIds = theDuplicateGoldenResourceIds;
}
- public MdmLinkChangeEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) {
+ public MdmLinkEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) {
String id = getIdAsString(theDuplicateGoldenResourceId);
if (id != null) {
getDuplicateGoldenResourceIds().add(id);
@@ -121,6 +121,10 @@ public class MdmLinkChangeEvent implements IModelJson {
}
public void setEidMatch(Boolean theEidMatch) {
+ if (theEidMatch == null) {
+ myEidMatch = Boolean.FALSE;
+ return;
+ }
myEidMatch = theEidMatch;
}
@@ -129,6 +133,10 @@ public class MdmLinkChangeEvent implements IModelJson {
}
public void setNewGoldenResource(Boolean theNewGoldenResource) {
+ if (theNewGoldenResource == null) {
+ myNewGoldenResource = Boolean.FALSE;
+ return;
+ }
myNewGoldenResource = theNewGoldenResource;
}
@@ -148,6 +156,15 @@ public class MdmLinkChangeEvent implements IModelJson {
myRuleCount = theRuleCount;
}
+ public void setFromLink(MdmLinkJson theMdmLinkJson) {
+ setMdmMatchResult(theMdmLinkJson.getMatchResult());
+ setMdmLinkSource(theMdmLinkJson.getLinkSource());
+ setEidMatch(theMdmLinkJson.getEidMatch());
+ setNewGoldenResource(theMdmLinkJson.getLinkCreatedNewResource());
+ setScore(theMdmLinkJson.getScore());
+ setRuleCount(theMdmLinkJson.getRuleCount());
+ }
+
@Override
public String toString() {
return "MdmLinkChangeEvent{" +
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java
index 744aa384823..424298418b4 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java
@@ -48,11 +48,15 @@ public class MdmLinkJson implements IModelJson {
@JsonProperty("version")
private String myVersion;
- /** This link was created as a result of an eid match **/
+ /**
+ * This link was created as a result of an eid match
+ **/
@JsonProperty("eidMatch")
private Boolean myEidMatch;
- /** This link created a new golden resource **/
+ /**
+ * This link created a new golden resource
+ **/
@JsonProperty("linkCreatedNewGoldenResource")
private Boolean myLinkCreatedNewResource;
@@ -62,6 +66,9 @@ public class MdmLinkJson implements IModelJson {
@JsonProperty("score")
private Double myScore;
+ @JsonProperty("ruleCount")
+ private Long myRuleCount;
+
public String getGoldenResourceId() {
return myGoldenResourceId;
}
@@ -160,4 +167,12 @@ public class MdmLinkJson implements IModelJson {
myScore = theScore;
return this;
}
+
+ public Long getRuleCount() {
+ return myRuleCount;
+ }
+
+ public void setRuleCount(Long theRuleCount) {
+ myRuleCount = theRuleCount;
+ }
}
diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
index ce35ea3f150..9853918a9d7 100644
--- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
+++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java
@@ -20,7 +20,7 @@ package ca.uhn.fhir.mdm.model;
* #L%
*/
-import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
+import ca.uhn.fhir.mdm.api.MdmLinkEvent;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
public class MdmTransactionContext {
@@ -46,7 +46,7 @@ public class MdmTransactionContext {
private String myResourceType;
- private MdmLinkChangeEvent myMdmLinkChangeEvent = new MdmLinkChangeEvent();
+ private MdmLinkEvent myMdmLinkEvent = new MdmLinkEvent();
public TransactionLogMessages getTransactionLogMessages() {
return myTransactionLogMessages;
@@ -96,12 +96,12 @@ public class MdmTransactionContext {
this.myResourceType = myResourceType;
}
- public MdmLinkChangeEvent getMdmLinkChangeEvent() {
- return myMdmLinkChangeEvent;
+ public MdmLinkEvent getMdmLinkChangeEvent() {
+ return myMdmLinkEvent;
}
- public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) {
- myMdmLinkChangeEvent = theMdmLinkChangeEvent;
+ public void setMdmLinkChangeEvent(MdmLinkEvent theMdmLinkEvent) {
+ myMdmLinkEvent = theMdmLinkEvent;
}
}
From 3a5e391c77690c0cc57975cd6dd9cf478fe174a0 Mon Sep 17 00:00:00 2001
From: Nick Goupinets
Date: Mon, 23 Aug 2021 11:00:04 -0400
Subject: [PATCH 012/143] Fixed pointcut def
---
.../src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index d8f0f33ae9f..30f81b90889 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -1996,7 +1996,7 @@ public enum Pointcut implements IPointcut {
MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class,
"ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage",
"ca.uhn.fhir.rest.server.TransactionLogMessages",
- "ca.uhn.fhir.mdm.api.MdmLinkChangeEvent"),
+ "ca.uhn.fhir.mdm.api.MdmLinkEvent"),
/**
* Performance Tracing Hook:
From b51c7227559fd222040e3ea0dd0038ef53a20ffd Mon Sep 17 00:00:00 2001
From: leif stawnyczy
Date: Wed, 25 Aug 2021 10:41:58 -0400
Subject: [PATCH 013/143] stashing minor refactors
---
.../java/ca/uhn/fhir/util/FhirTerser.java | 1 +
.../jpa/dao/BaseTransactionProcessor.java | 368 +++++++++++-------
.../ca/uhn/fhir/jpa/dao/r4/AAAATests.java | 83 ++++
...irResourceDaoR4VersionedReferenceTest.java | 15 +-
.../uhn/fhir/rest/server/mail/MailConfig.java | 20 +
.../ca/uhn/fhir/rest/server/mail/MailSvc.java | 20 +
.../model/dstu2/composite/NarrativeDt.java | 16 -
7 files changed, 357 insertions(+), 166 deletions(-)
create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
index 8cc1c9be1de..b0dca947658 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
@@ -961,6 +961,7 @@ public class FhirTerser {
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
List> values = nextChild.getAccessor().getValues(theElement);
+
if (values != null) {
for (Object nextValueObject : values) {
IBase nextValue;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
index f599b5bc435..3e30960b590 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
@@ -240,8 +240,10 @@ public abstract class BaseTransactionProcessor {
myVersionAdapter.populateEntryWithOperationOutcome(caughtEx, nextEntry);
}
- private void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, IIdType nextResourceId, DaoMethodOutcome outcome,
- IBase newEntry, String theResourceType, IBaseResource theRes, RequestDetails theRequestDetails) {
+ private void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome,
+ IIdType nextResourceId, DaoMethodOutcome outcome,
+ IBase newEntry, String theResourceType,
+ IBaseResource theRes, RequestDetails theRequestDetails) {
IIdType newId = outcome.getId().toUnqualified();
IIdType resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
if (newId.equals(resourceId) == false) {
@@ -652,8 +654,129 @@ public abstract class BaseTransactionProcessor {
myModelConfig = theModelConfig;
}
- protected Map doTransactionWriteOperations(final RequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set theAllIds,
- Map theIdSubstitutions, Map theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap theOriginalRequestOrder, List theEntries, StopWatch theTransactionStopWatch) {
+ /**
+ * Searches for duplicate conditional creates and consolidates them.
+ *
+ * @param theEntries
+ */
+ private void consolidateDuplicateConditionalCreates(List theEntries) {
+ final HashMap keyToUuid = new HashMap<>();
+ for (int index = 0, originalIndex = 0; index < theEntries.size(); index++, originalIndex++) {
+ IBase nextReqEntry = theEntries.get(index);
+ IBaseResource resource = myVersionAdapter.getResource(nextReqEntry);
+ if (resource != null) {
+ String verb = myVersionAdapter.getEntryRequestVerb(myContext, nextReqEntry);
+ String entryUrl = myVersionAdapter.getFullUrl(nextReqEntry);
+ String requestUrl = myVersionAdapter.getEntryRequestUrl(nextReqEntry);
+ String ifNoneExist = myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry);
+ String key = verb + "|" + requestUrl + "|" + ifNoneExist;
+
+ // Conditional UPDATE
+ boolean consolidateEntry = false;
+ if ("PUT".equals(verb)) {
+ if (isNotBlank(entryUrl) && isNotBlank(requestUrl)) {
+ int questionMarkIndex = requestUrl.indexOf('?');
+ if (questionMarkIndex >= 0 && requestUrl.length() > (questionMarkIndex + 1)) {
+ consolidateEntry = true;
+ }
+ }
+ }
+
+ // Conditional CREATE
+ if ("POST".equals(verb)) {
+ if (isNotBlank(entryUrl) && isNotBlank(requestUrl) && isNotBlank(ifNoneExist)) {
+ if (!entryUrl.equals(requestUrl)) {
+ consolidateEntry = true;
+ }
+ }
+ }
+
+ if (consolidateEntry) {
+ if (!keyToUuid.containsKey(key)) {
+ keyToUuid.put(key, entryUrl);
+ } else {
+ ourLog.info("Discarding transaction bundle entry {} as it contained a duplicate conditional {}", originalIndex, verb);
+ theEntries.remove(index);
+ index--;
+ String existingUuid = keyToUuid.get(key);
+ for (IBase nextEntry : theEntries) {
+ IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
+ for (IBaseReference nextReference : myContext.newTerser().getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class)) {
+ // We're interested in any references directly to the placeholder ID, but also
+ // references that have a resource target that has the placeholder ID.
+ String nextReferenceId = nextReference.getReferenceElement().getValue();
+ if (isBlank(nextReferenceId) && nextReference.getResource() != null) {
+ nextReferenceId = nextReference.getResource().getIdElement().getValue();
+ }
+ if (entryUrl.equals(nextReferenceId)) {
+ nextReference.setReference(existingUuid);
+ nextReference.setResource(null);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves teh next resource id (IIdType) from the base resource and next request entry.
+ * @param theBaseResource - base resource
+ * @param theNextReqEntry - next request entry
+ * @param theAllIds - set of all IIdType values
+ * @return
+ */
+ private IIdType getNextResourceIdFromBaseResource(IBaseResource theBaseResource,
+ IBase theNextReqEntry,
+ Set theAllIds) {
+ IIdType nextResourceId = null;
+ if (theBaseResource != null) {
+ nextResourceId = theBaseResource.getIdElement();
+
+ String fullUrl = myVersionAdapter.getFullUrl(theNextReqEntry);
+ if (isNotBlank(fullUrl)) {
+ IIdType fullUrlIdType = newIdType(fullUrl);
+ if (isPlaceholder(fullUrlIdType)) {
+ nextResourceId = fullUrlIdType;
+ } else if (!nextResourceId.hasIdPart()) {
+ nextResourceId = fullUrlIdType;
+ }
+ }
+
+ if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+:.*") && !isPlaceholder(nextResourceId)) {
+ throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'");
+ }
+
+ if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) {
+ nextResourceId = newIdType(toResourceName(theBaseResource.getClass()), nextResourceId.getIdPart());
+ theBaseResource.setId(nextResourceId);
+ }
+
+ /*
+ * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
+ */
+ if (isPlaceholder(nextResourceId)) {
+ if (!theAllIds.add(nextResourceId)) {
+ throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId));
+ }
+ } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
+ IIdType nextId = nextResourceId.toUnqualifiedVersionless();
+ if (!theAllIds.add(nextId)) {
+ throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
+ }
+ }
+
+ }
+
+ return nextResourceId;
+ }
+
+ protected Map doTransactionWriteOperations(final RequestDetails theRequest, String theActionName,
+ TransactionDetails theTransactionDetails, Set theAllIds,
+ Map theIdSubstitutions, Map theIdToPersistedOutcome,
+ IBaseBundle theResponse, IdentityHashMap theOriginalRequestOrder,
+ List theEntries, StopWatch theTransactionStopWatch) {
theTransactionDetails.beginAcceptingDeferredInterceptorBroadcasts(
Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED,
@@ -661,7 +784,6 @@ public abstract class BaseTransactionProcessor {
Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED
);
try {
-
Set deletedResources = new HashSet<>();
DeleteConflictList deleteConflicts = new DeleteConflictList();
Map entriesToProcess = new IdentityHashMap<>();
@@ -673,117 +795,20 @@ public abstract class BaseTransactionProcessor {
/*
* Look for duplicate conditional creates and consolidate them
*/
- final HashMap keyToUuid = new HashMap<>();
- for (int index = 0, originalIndex = 0; index < theEntries.size(); index++, originalIndex++) {
- IBase nextReqEntry = theEntries.get(index);
- IBaseResource resource = myVersionAdapter.getResource(nextReqEntry);
- if (resource != null) {
- String verb = myVersionAdapter.getEntryRequestVerb(myContext, nextReqEntry);
- String entryUrl = myVersionAdapter.getFullUrl(nextReqEntry);
- String requestUrl = myVersionAdapter.getEntryRequestUrl(nextReqEntry);
- String ifNoneExist = myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry);
- String key = verb + "|" + requestUrl + "|" + ifNoneExist;
-
- // Conditional UPDATE
- boolean consolidateEntry = false;
- if ("PUT".equals(verb)) {
- if (isNotBlank(entryUrl) && isNotBlank(requestUrl)) {
- int questionMarkIndex = requestUrl.indexOf('?');
- if (questionMarkIndex >= 0 && requestUrl.length() > (questionMarkIndex + 1)) {
- consolidateEntry = true;
- }
- }
- }
-
- // Conditional CREATE
- if ("POST".equals(verb)) {
- if (isNotBlank(entryUrl) && isNotBlank(requestUrl) && isNotBlank(ifNoneExist)) {
- if (!entryUrl.equals(requestUrl)) {
- consolidateEntry = true;
- }
- }
- }
-
- if (consolidateEntry) {
- if (!keyToUuid.containsKey(key)) {
- keyToUuid.put(key, entryUrl);
- } else {
- ourLog.info("Discarding transaction bundle entry {} as it contained a duplicate conditional {}", originalIndex, verb);
- theEntries.remove(index);
- index--;
- String existingUuid = keyToUuid.get(key);
- for (IBase nextEntry : theEntries) {
- IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
- for (IBaseReference nextReference : myContext.newTerser().getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class)) {
- // We're interested in any references directly to the placeholder ID, but also
- // references that have a resource target that has the placeholder ID.
- String nextReferenceId = nextReference.getReferenceElement().getValue();
- if (isBlank(nextReferenceId) && nextReference.getResource() != null) {
- nextReferenceId = nextReference.getResource().getIdElement().getValue();
- }
- if (entryUrl.equals(nextReferenceId)) {
- nextReference.setReference(existingUuid);
- nextReference.setResource(null);
- }
- }
- }
- }
- }
- }
- }
-
+ consolidateDuplicateConditionalCreates(theEntries);
/*
* Loop through the request and process any entries of type
* PUT, POST or DELETE
*/
for (int i = 0; i < theEntries.size(); i++) {
-
if (i % 250 == 0) {
ourLog.debug("Processed {} non-GET entries out of {} in transaction", i, theEntries.size());
}
IBase nextReqEntry = theEntries.get(i);
IBaseResource res = myVersionAdapter.getResource(nextReqEntry);
- IIdType nextResourceId = null;
- if (res != null) {
-
- nextResourceId = res.getIdElement();
-
- String fullUrl = myVersionAdapter.getFullUrl(nextReqEntry);
- if (isNotBlank(fullUrl)) {
- IIdType fullUrlIdType = newIdType(fullUrl);
- if (isPlaceholder(fullUrlIdType)) {
- nextResourceId = fullUrlIdType;
- } else if (!nextResourceId.hasIdPart()) {
- nextResourceId = fullUrlIdType;
- }
- }
-
- if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+:.*") && !isPlaceholder(nextResourceId)) {
- throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'");
- }
-
- if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) {
- nextResourceId = newIdType(toResourceName(res.getClass()), nextResourceId.getIdPart());
- res.setId(nextResourceId);
- }
-
- /*
- * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
- */
- if (isPlaceholder(nextResourceId)) {
- if (!theAllIds.add(nextResourceId)) {
- throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId));
- }
- } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
- IIdType nextId = nextResourceId.toUnqualifiedVersionless();
- if (!theAllIds.add(nextId)) {
- throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
- }
- }
-
- }
+ IIdType nextResourceId = getNextResourceIdFromBaseResource(res, nextReqEntry, theAllIds);
String verb = myVersionAdapter.getEntryRequestVerb(myContext, nextReqEntry);
String resourceType = res != null ? myContext.getResourceType(res) : null;
@@ -891,7 +916,8 @@ public abstract class BaseTransactionProcessor {
}
}
- handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequest);
+ handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId,
+ outcome, nextRespEntry, resourceType, res, theRequest);
entriesToProcess.put(nextRespEntry, outcome.getId());
break;
}
@@ -958,52 +984,24 @@ public abstract class BaseTransactionProcessor {
* was also deleted as a part of this transaction, which is why we check this now at the
* end.
*/
- for (Iterator iter = deleteConflicts.iterator(); iter.hasNext(); ) {
- DeleteConflict nextDeleteConflict = iter.next();
+ checkForDeleteConflicts(deleteConflicts, deletedResources, updatedResources);
- /*
- * If we have a conflict, it means we can't delete Resource/A because
- * Resource/B has a reference to it. We'll ignore that conflict though
- * if it turns out we're also deleting Resource/B in this transaction.
- */
- if (deletedResources.contains(nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue())) {
- iter.remove();
- continue;
- }
-
- /*
- * And then, this is kind of a last ditch check. It's also ok to delete
- * Resource/A if Resource/B isn't being deleted, but it is being UPDATED
- * in this transaction, and the updated version of it has no references
- * to Resource/A any more.
- */
- String sourceId = nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue();
- String targetId = nextDeleteConflict.getTargetId().toUnqualifiedVersionless().getValue();
- Optional updatedSource = updatedResources
- .stream()
- .filter(t -> sourceId.equals(t.getIdElement().toUnqualifiedVersionless().getValue()))
- .findFirst();
- if (updatedSource.isPresent()) {
- List referencesInSource = myContext.newTerser().getAllResourceReferences(updatedSource.get());
- boolean sourceStillReferencesTarget = referencesInSource
- .stream()
- .anyMatch(t -> targetId.equals(t.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue()));
- if (!sourceStillReferencesTarget) {
- iter.remove();
- }
- }
- }
- DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(myContext, deleteConflicts);
-
- theIdToPersistedOutcome.entrySet().forEach(t -> theTransactionDetails.addResolvedResourceId(t.getKey(), t.getValue().getPersistentId()));
+ theIdToPersistedOutcome.entrySet().forEach(idAndOutcome -> {
+ theTransactionDetails.addResolvedResourceId(idAndOutcome.getKey(), idAndOutcome.getValue().getPersistentId());
+ });
/*
* Perform ID substitutions and then index each resource we have saved
*/
- resolveReferencesThenSaveAndIndexResources(theRequest, theTransactionDetails, theIdSubstitutions, theIdToPersistedOutcome, theTransactionStopWatch, entriesToProcess, nonUpdatedEntities, updatedEntities);
+ resolveReferencesThenSaveAndIndexResources(theRequest, theTransactionDetails,
+ theIdSubstitutions, theIdToPersistedOutcome,
+ theTransactionStopWatch, entriesToProcess,
+ nonUpdatedEntities, updatedEntities);
theTransactionStopWatch.endCurrentTask();
+
+ // flush writes to db
theTransactionStopWatch.startTask("Flush writes to database");
flushSession(theIdToPersistedOutcome);
@@ -1061,6 +1059,53 @@ public abstract class BaseTransactionProcessor {
}
}
+ /**
+ * Checks for any delete conflicts.
+ * @param theDeleteConflicts - set of delete conflicts
+ * @param theDeletedResources - set of deleted resources
+ * @param theUpdatedResources - list of updated resources
+ */
+ private void checkForDeleteConflicts(DeleteConflictList theDeleteConflicts,
+ Set theDeletedResources,
+ List theUpdatedResources) {
+ for (Iterator iter = theDeleteConflicts.iterator(); iter.hasNext(); ) {
+ DeleteConflict nextDeleteConflict = iter.next();
+
+ /*
+ * If we have a conflict, it means we can't delete Resource/A because
+ * Resource/B has a reference to it. We'll ignore that conflict though
+ * if it turns out we're also deleting Resource/B in this transaction.
+ */
+ if (theDeletedResources.contains(nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue())) {
+ iter.remove();
+ continue;
+ }
+
+ /*
+ * And then, this is kind of a last ditch check. It's also ok to delete
+ * Resource/A if Resource/B isn't being deleted, but it is being UPDATED
+ * in this transaction, and the updated version of it has no references
+ * to Resource/A any more.
+ */
+ String sourceId = nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue();
+ String targetId = nextDeleteConflict.getTargetId().toUnqualifiedVersionless().getValue();
+ Optional updatedSource = theUpdatedResources
+ .stream()
+ .filter(t -> sourceId.equals(t.getIdElement().toUnqualifiedVersionless().getValue()))
+ .findFirst();
+ if (updatedSource.isPresent()) {
+ List referencesInSource = myContext.newTerser().getAllResourceReferences(updatedSource.get());
+ boolean sourceStillReferencesTarget = referencesInSource
+ .stream()
+ .anyMatch(t -> targetId.equals(t.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue()));
+ if (!sourceStillReferencesTarget) {
+ iter.remove();
+ }
+ }
+ }
+ DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(myContext, theDeleteConflicts);
+ }
+
/**
* This method replaces any placeholder references in the
* source transaction Bundle with their actual targets, then stores the resource contents and indexes
@@ -1083,7 +1128,10 @@ public abstract class BaseTransactionProcessor {
* pass because it's too complex to try and insert the auto-versioned references and still
* account for NOPs, so we block NOPs in that pass.
*/
- private void resolveReferencesThenSaveAndIndexResources(RequestDetails theRequest, TransactionDetails theTransactionDetails, Map theIdSubstitutions, Map theIdToPersistedOutcome, StopWatch theTransactionStopWatch, Map entriesToProcess, Set nonUpdatedEntities, Set updatedEntities) {
+ private void resolveReferencesThenSaveAndIndexResources(RequestDetails theRequest, TransactionDetails theTransactionDetails,
+ Map theIdSubstitutions, Map theIdToPersistedOutcome,
+ StopWatch theTransactionStopWatch, Map entriesToProcess,
+ Set nonUpdatedEntities, Set updatedEntities) {
FhirTerser terser = myContext.newTerser();
theTransactionStopWatch.startTask("Index " + theIdToPersistedOutcome.size() + " resources");
IdentityHashMap> deferredIndexesForAutoVersioning = null;
@@ -1105,14 +1153,28 @@ public abstract class BaseTransactionProcessor {
Set referencesToAutoVersion = BaseStorageDao.extractReferencesToAutoVersion(myContext, myModelConfig, nextResource);
if (referencesToAutoVersion.isEmpty()) {
- resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails, theIdSubstitutions, theIdToPersistedOutcome, entriesToProcess, nonUpdatedEntities, updatedEntities, terser, nextOutcome, nextResource, referencesToAutoVersion);
+ // no references to autoversion - we can do the resolve and save now
+ resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails,
+ theIdSubstitutions, theIdToPersistedOutcome,
+ entriesToProcess, nonUpdatedEntities,
+ updatedEntities, terser,
+ nextOutcome, nextResource,
+ referencesToAutoVersion); // this is empty
} else {
if (deferredIndexesForAutoVersioning == null) {
deferredIndexesForAutoVersioning = new IdentityHashMap<>();
}
deferredIndexesForAutoVersioning.put(nextOutcome, referencesToAutoVersion);
- }
+ // TODO - add the references to the
+ // idsToPersistedOutcomes
+// for (IBaseReference autoVersion: referencesToAutoVersion) {
+// IBaseResource resource = myVersionAdapter.getResource(autoVersion);
+// IFhirResourceDao dao = getDaoOrThrowException(resource.getClass());
+//
+// }
+// theIdToPersistedOutcome.put()
+ }
}
// If we have any resources we'll be auto-versioning, index these next
@@ -1121,12 +1183,22 @@ public abstract class BaseTransactionProcessor {
DaoMethodOutcome nextOutcome = nextEntry.getKey();
Set referencesToAutoVersion = nextEntry.getValue();
IBaseResource nextResource = nextOutcome.getResource();
- resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails, theIdSubstitutions, theIdToPersistedOutcome, entriesToProcess, nonUpdatedEntities, updatedEntities, terser, nextOutcome, nextResource, referencesToAutoVersion);
+ resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails,
+ theIdSubstitutions, theIdToPersistedOutcome,
+ entriesToProcess, nonUpdatedEntities,
+ updatedEntities, terser,
+ nextOutcome, nextResource,
+ referencesToAutoVersion);
}
}
}
- private void resolveReferencesThenSaveAndIndexResource(RequestDetails theRequest, TransactionDetails theTransactionDetails, Map theIdSubstitutions, Map theIdToPersistedOutcome, Map entriesToProcess, Set nonUpdatedEntities, Set updatedEntities, FhirTerser terser, DaoMethodOutcome nextOutcome, IBaseResource nextResource, Set theReferencesToAutoVersion) {
+ private void resolveReferencesThenSaveAndIndexResource(RequestDetails theRequest, TransactionDetails theTransactionDetails,
+ Map theIdSubstitutions, Map theIdToPersistedOutcome,
+ Map entriesToProcess, Set nonUpdatedEntities,
+ Set updatedEntities, FhirTerser terser,
+ DaoMethodOutcome nextOutcome, IBaseResource nextResource,
+ Set theReferencesToAutoVersion) {
// References
List allRefs = terser.getAllResourceReferences(nextResource);
for (ResourceReferenceInfo nextRef : allRefs) {
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java
new file mode 100644
index 00000000000..8f92f333392
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java
@@ -0,0 +1,83 @@
+package ca.uhn.fhir.jpa.dao.r4;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.ParserOptions;
+import ca.uhn.fhir.jpa.api.config.DaoConfig;
+import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
+import ca.uhn.fhir.jpa.model.entity.ModelConfig;
+import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
+import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.param.TokenParam;
+import ca.uhn.fhir.util.BundleBuilder;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Condition;
+import org.hl7.fhir.r4.model.Encounter;
+import org.hl7.fhir.r4.model.ExplanationOfBenefit;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Organization;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.Task;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.matchesPattern;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AAAATests extends BaseJpaR4Test {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4VersionedReferenceTest.class);
+
+ @AfterEach
+ public void afterEach() {
+ myFhirCtx.getParserOptions().setStripVersionsFromReferences(true);
+ myFhirCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths().clear();
+ myDaoConfig.setDeleteEnabled(new DaoConfig().isDeleteEnabled());
+ myModelConfig.setRespectVersionsForSearchIncludes(new ModelConfig().isRespectVersionsForSearchIncludes());
+ myModelConfig.setAutoVersionReferenceAtPaths(new ModelConfig().getAutoVersionReferenceAtPaths());
+ }
+
+
+ @Test
+ @DisplayName("GH-2901 Test no NPE is thrown on autoversioned references")
+ public void testNoNpeMinimal() {
+ myDaoConfig.setAutoCreatePlaceholderReferenceTargets(false);
+ myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
+
+// ParserOptions options = new ParserOptions();
+// options.setDontStripVersionsFromReferencesAtPaths("Observation.subject");
+// myFhirCtx.setParserOptions(options);
+
+ Patient patient = new Patient();
+ patient.setId("Patient/RED");
+ myPatientDao.update(patient);
+
+ Observation obs = new Observation();
+ obs.setId("Observation/DEF");
+ obs.setSubject(new Reference("Patient/RED"));
+ BundleBuilder builder = new BundleBuilder(myFhirCtx);
+ builder.addTransactionUpdateEntry(obs);
+
+ mySystemDao.transaction(new SystemRequestDetails(), (Bundle) builder.getBundle());
+ }
+
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
index f2c661b4522..73063fe38cb 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
@@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
@@ -802,7 +803,9 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
);
myModelConfig.setAutoVersionReferenceAtPaths(new HashSet(strings));
- Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, new InputStreamReader(FhirResourceDaoR4VersionedReferenceTest.class.getResourceAsStream("/npe-causing-bundle.json")));
+ Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class,
+ new InputStreamReader(
+ FhirResourceDaoR4VersionedReferenceTest.class.getResourceAsStream("/npe-causing-bundle.json")));
Bundle transaction = mySystemDao.transaction(new SystemRequestDetails(), bundle);
}
@@ -834,9 +837,17 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
@Test
@DisplayName("GH-2901 Test no NPE is thrown on autoversioned references")
public void testNoNpeMinimal() {
- myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
+ myDaoConfig.setAutoCreatePlaceholderReferenceTargets(false);
myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
+// ParserOptions options = new ParserOptions();
+// options.setDontStripVersionsFromReferencesAtPaths("Observation.subject");
+// myFhirCtx.setParserOptions(options);
+
+ Patient patient = new Patient();
+ patient.setId("Patient/RED");
+ myPatientDao.update(patient);
+
Observation obs = new Observation();
obs.setId("Observation/DEF");
obs.setSubject(new Reference("Patient/RED"));
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java
index 02bb16f9fdc..2202f27b77e 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.mail;
+/*-
+ * #%L
+ * HAPI FHIR - Server Framework
+ * %%
+ * Copyright (C) 2014 - 2021 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java
index 5c87c4256dd..df1cd4e9dca 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.mail;
+/*-
+ * #%L
+ * HAPI FHIR - Server Framework
+ * %%
+ * Copyright (C) 2014 - 2021 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import org.apache.commons.lang3.Validate;
import org.simplejavamail.MailException;
import org.simplejavamail.api.email.Email;
diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java
index ef74811416d..085008a62b1 100644
--- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java
+++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java
@@ -1,19 +1,3 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
package ca.uhn.fhir.model.dstu2.composite;
/*
From 646592b1b7cd685b22f3308cae752f7d9834a8a7 Mon Sep 17 00:00:00 2001
From: leif stawnyczy
Date: Wed, 25 Aug 2021 15:56:55 -0400
Subject: [PATCH 014/143] issue-2901 some refactoring
---
.../jpa/dao/BaseTransactionProcessor.java | 188 ++++++++++--------
.../ca/uhn/fhir/jpa/dao/r4/AAAATests.java | 83 --------
...irResourceDaoR4VersionedReferenceTest.java | 7 -
3 files changed, 105 insertions(+), 173 deletions(-)
delete mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
index 3e30960b590..866b3e780d9 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
@@ -384,7 +384,8 @@ public abstract class BaseTransactionProcessor {
myHapiTransactionService = theHapiTransactionService;
}
- private IBaseBundle processTransaction(final RequestDetails theRequestDetails, final IBaseBundle theRequest, final String theActionName, boolean theNestedMode) {
+ private IBaseBundle processTransaction(final RequestDetails theRequestDetails, final IBaseBundle theRequest,
+ final String theActionName, boolean theNestedMode) {
validateDependencies();
String transactionType = myVersionAdapter.getBundleType(theRequest);
@@ -440,10 +441,11 @@ public abstract class BaseTransactionProcessor {
List getEntries = new ArrayList<>();
final IdentityHashMap originalRequestOrder = new IdentityHashMap<>();
for (int i = 0; i < requestEntries.size(); i++) {
- originalRequestOrder.put(requestEntries.get(i), i);
+ IBase requestEntry = requestEntries.get(i);
+ originalRequestOrder.put(requestEntry, i);
myVersionAdapter.addEntry(response);
- if (myVersionAdapter.getEntryRequestVerb(myContext, requestEntries.get(i)).equals("GET")) {
- getEntries.add(requestEntries.get(i));
+ if (myVersionAdapter.getEntryRequestVerb(myContext, requestEntry).equals("GET")) {
+ getEntries.add(requestEntry);
}
}
@@ -462,73 +464,17 @@ public abstract class BaseTransactionProcessor {
}
entries.sort(new TransactionSorter(placeholderIds));
- doTransactionWriteOperations(theRequestDetails, theActionName, transactionDetails, transactionStopWatch, response, originalRequestOrder, entries);
+ // perform all writes
+ doTransactionWriteOperations(theRequestDetails, theActionName,
+ transactionDetails, transactionStopWatch,
+ response, originalRequestOrder, entries);
- /*
- * Loop through the request and process any entries of type GET
- */
- if (getEntries.size() > 0) {
- transactionStopWatch.startTask("Process " + getEntries.size() + " GET entries");
- }
- for (IBase nextReqEntry : getEntries) {
-
- if (theNestedMode) {
- throw new InvalidRequestException("Can not invoke read operation on nested transaction");
- }
-
- if (!(theRequestDetails instanceof ServletRequestDetails)) {
- throw new MethodNotAllowedException("Can not call transaction GET methods from this context");
- }
-
- ServletRequestDetails srd = (ServletRequestDetails) theRequestDetails;
- Integer originalOrder = originalRequestOrder.get(nextReqEntry);
- IBase nextRespEntry = (IBase) myVersionAdapter.getEntries(response).get(originalOrder);
-
- ArrayListMultimap paramValues = ArrayListMultimap.create();
-
- String transactionUrl = extractTransactionUrlOrThrowException(nextReqEntry, "GET");
-
- ServletSubRequestDetails requestDetails = ServletRequestUtil.getServletSubRequestDetails(srd, transactionUrl, paramValues);
-
- String url = requestDetails.getRequestPath();
-
- BaseMethodBinding> method = srd.getServer().determineResourceMethod(requestDetails, url);
- if (method == null) {
- throw new IllegalArgumentException("Unable to handle GET " + url);
- }
-
- if (isNotBlank(myVersionAdapter.getEntryRequestIfMatch(nextReqEntry))) {
- requestDetails.addHeader(Constants.HEADER_IF_MATCH, myVersionAdapter.getEntryRequestIfMatch(nextReqEntry));
- }
- if (isNotBlank(myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry))) {
- requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry));
- }
- if (isNotBlank(myVersionAdapter.getEntryRequestIfNoneMatch(nextReqEntry))) {
- requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, myVersionAdapter.getEntryRequestIfNoneMatch(nextReqEntry));
- }
-
- Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {}", url);
- try {
-
- BaseResourceReturningMethodBinding methodBinding = (BaseResourceReturningMethodBinding) method;
- requestDetails.setRestOperationType(methodBinding.getRestOperationType());
-
- IBaseResource resource = methodBinding.doInvokeServer(srd.getServer(), requestDetails);
- if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
- resource = filterNestedBundle(requestDetails, resource);
- }
- myVersionAdapter.setResource(nextRespEntry, resource);
- myVersionAdapter.setResponseStatus(nextRespEntry, toStatusString(Constants.STATUS_HTTP_200_OK));
- } catch (NotModifiedException e) {
- myVersionAdapter.setResponseStatus(nextRespEntry, toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
- } catch (BaseServerResponseException e) {
- ourLog.info("Failure processing transaction GET {}: {}", url, e.toString());
- myVersionAdapter.setResponseStatus(nextRespEntry, toStatusString(e.getStatusCode()));
- populateEntryWithOperationOutcome(e, nextRespEntry);
- }
-
- }
- transactionStopWatch.endCurrentTask();
+ // perform all gets
+ // (we do these last so that the gets happen on the final state of the DB;
+ // see above note)
+ doTransactionReadOperations(theRequestDetails, response,
+ getEntries, originalRequestOrder,
+ transactionStopWatch, theNestedMode);
// Interceptor broadcast: JPA_PERFTRACE_INFO
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequestDetails)) {
@@ -545,6 +491,74 @@ public abstract class BaseTransactionProcessor {
return response;
}
+ private void doTransactionReadOperations(final RequestDetails theRequestDetails, IBaseBundle theResponse,
+ List theGetEntries, IdentityHashMap theOriginalRequestOrder,
+ StopWatch theTransactionStopWatch, boolean theNestedMode) {
+ if (theGetEntries.size() > 0) {
+ theTransactionStopWatch.startTask("Process " + theGetEntries.size() + " GET entries");
+
+ /*
+ * Loop through the request and process any entries of type GET
+ */
+ for (IBase nextReqEntry : theGetEntries) {
+ if (theNestedMode) {
+ throw new InvalidRequestException("Can not invoke read operation on nested transaction");
+ }
+
+ if (!(theRequestDetails instanceof ServletRequestDetails)) {
+ throw new MethodNotAllowedException("Can not call transaction GET methods from this context");
+ }
+
+ ServletRequestDetails srd = (ServletRequestDetails) theRequestDetails;
+ Integer originalOrder = theOriginalRequestOrder.get(nextReqEntry);
+ IBase nextRespEntry = (IBase) myVersionAdapter.getEntries(theResponse).get(originalOrder);
+
+ ArrayListMultimap paramValues = ArrayListMultimap.create();
+
+ String transactionUrl = extractTransactionUrlOrThrowException(nextReqEntry, "GET");
+
+ ServletSubRequestDetails requestDetails = ServletRequestUtil.getServletSubRequestDetails(srd, transactionUrl, paramValues);
+
+ String url = requestDetails.getRequestPath();
+
+ BaseMethodBinding> method = srd.getServer().determineResourceMethod(requestDetails, url);
+ if (method == null) {
+ throw new IllegalArgumentException("Unable to handle GET " + url);
+ }
+
+ if (isNotBlank(myVersionAdapter.getEntryRequestIfMatch(nextReqEntry))) {
+ requestDetails.addHeader(Constants.HEADER_IF_MATCH, myVersionAdapter.getEntryRequestIfMatch(nextReqEntry));
+ }
+ if (isNotBlank(myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry))) {
+ requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry));
+ }
+ if (isNotBlank(myVersionAdapter.getEntryRequestIfNoneMatch(nextReqEntry))) {
+ requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, myVersionAdapter.getEntryRequestIfNoneMatch(nextReqEntry));
+ }
+
+ Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {}", url);
+ try {
+ BaseResourceReturningMethodBinding methodBinding = (BaseResourceReturningMethodBinding) method;
+ requestDetails.setRestOperationType(methodBinding.getRestOperationType());
+
+ IBaseResource resource = methodBinding.doInvokeServer(srd.getServer(), requestDetails);
+ if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
+ resource = filterNestedBundle(requestDetails, resource);
+ }
+ myVersionAdapter.setResource(nextRespEntry, resource);
+ myVersionAdapter.setResponseStatus(nextRespEntry, toStatusString(Constants.STATUS_HTTP_200_OK));
+ } catch (NotModifiedException e) {
+ myVersionAdapter.setResponseStatus(nextRespEntry, toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
+ } catch (BaseServerResponseException e) {
+ ourLog.info("Failure processing transaction GET {}: {}", url, e.toString());
+ myVersionAdapter.setResponseStatus(nextRespEntry, toStatusString(e.getStatusCode()));
+ populateEntryWithOperationOutcome(e, nextRespEntry);
+ }
+ }
+ theTransactionStopWatch.endCurrentTask();
+ }
+ }
+
/**
* All of the write operations in the transaction (PUT, POST, etc.. basically anything
* except GET) are performed in their own database transaction before we do the reads.
@@ -554,7 +568,10 @@ public abstract class BaseTransactionProcessor {
* heavy load with lots of concurrent transactions using all available
* database connections.
*/
- private void doTransactionWriteOperations(RequestDetails theRequestDetails, String theActionName, TransactionDetails theTransactionDetails, StopWatch theTransactionStopWatch, IBaseBundle theResponse, IdentityHashMap theOriginalRequestOrder, List theEntries) {
+ private void doTransactionWriteOperations(RequestDetails theRequestDetails, String theActionName,
+ TransactionDetails theTransactionDetails, StopWatch theTransactionStopWatch,
+ IBaseBundle theResponse, IdentityHashMap theOriginalRequestOrder,
+ List theEntries) {
TransactionWriteOperationsDetails writeOperationsDetails = null;
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_PRE, myInterceptorBroadcaster, theRequestDetails) ||
CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_POST, myInterceptorBroadcaster, theRequestDetails)) {
@@ -583,14 +600,18 @@ public abstract class BaseTransactionProcessor {
.add(TransactionDetails.class, theTransactionDetails)
.add(TransactionWriteOperationsDetails.class, writeOperationsDetails);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_PRE, params);
-
}
TransactionCallback