The `OverridePathBasedReferentialIntegrityForDeletesInterceptor` now … (#4483)
* The `OverridePathBasedReferentialIntegrityForDeletesInterceptor` now works for a multitenant enabled server. The `RequestDetails` weren't set while requesting the conflicting resource via the `DaoRegistry`. * Add credit --------- Co-authored-by: Joris Scharp <joris@headease.nl> Co-authored-by: James Agnew <jamesagnew@gmail.com>
This commit is contained in:
parent
eaa1e62655
commit
9665476c00
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 4483
|
||||||
|
title: "The OverridePathBasedReferentialIntegrityForDeletesInterceptor now works on partitioned
|
||||||
|
servers. Thanks to GitHub user @JorisHeadease for the pull request!"
|
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
|
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
|
||||||
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
|
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -99,7 +100,7 @@ public class OverridePathBasedReferentialIntegrityForDeletesInterceptor {
|
||||||
* Interceptor hook method. Do not invoke directly.
|
* Interceptor hook method. Do not invoke directly.
|
||||||
*/
|
*/
|
||||||
@Hook(value = Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, order = CascadingDeleteInterceptor.OVERRIDE_PATH_BASED_REF_INTEGRITY_INTERCEPTOR_ORDER)
|
@Hook(value = Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, order = CascadingDeleteInterceptor.OVERRIDE_PATH_BASED_REF_INTEGRITY_INTERCEPTOR_ORDER)
|
||||||
public void handleDeleteConflicts(DeleteConflictList theDeleteConflictList) {
|
public void handleDeleteConflicts(DeleteConflictList theDeleteConflictList, RequestDetails requestDetails) {
|
||||||
for (DeleteConflict nextConflict : theDeleteConflictList) {
|
for (DeleteConflict nextConflict : theDeleteConflictList) {
|
||||||
ourLog.info("Ignoring referential integrity deleting {} - Referred to from {} at path {}", nextConflict.getTargetId(), nextConflict.getSourceId(), nextConflict.getSourcePath());
|
ourLog.info("Ignoring referential integrity deleting {} - Referred to from {} at path {}", nextConflict.getTargetId(), nextConflict.getSourceId(), nextConflict.getSourcePath());
|
||||||
|
|
||||||
|
@ -107,7 +108,7 @@ public class OverridePathBasedReferentialIntegrityForDeletesInterceptor {
|
||||||
IdDt targetId = nextConflict.getTargetId();
|
IdDt targetId = nextConflict.getTargetId();
|
||||||
String targetIdValue = targetId.toVersionless().getValue();
|
String targetIdValue = targetId.toVersionless().getValue();
|
||||||
|
|
||||||
IBaseResource sourceResource = myDaoRegistry.getResourceDao(sourceId.getResourceType()).read(sourceId);
|
IBaseResource sourceResource = myDaoRegistry.getResourceDao(sourceId.getResourceType()).read(sourceId, requestDetails);
|
||||||
|
|
||||||
IFhirPath fhirPath = myFhirContext.newFhirPath();
|
IFhirPath fhirPath = myFhirContext.newFhirPath();
|
||||||
for (String nextPath : myPaths) {
|
for (String nextPath : myPaths) {
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
package ca.uhn.fhir.jpa.interceptor;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
|
import ca.uhn.fhir.jpa.provider.r4.BaseMultitenantResourceProviderR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
import org.hl7.fhir.r4.model.AuditEvent;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Multitenant test version of {@link OverridePathBasedReferentialIntegrityForDeletesInterceptorTest}.</p>
|
||||||
|
*
|
||||||
|
* <p>Ensures the tenant is properly propagated down to the {@link ca.uhn.fhir.jpa.api.dao.DaoRegistry} when requesting
|
||||||
|
* the conflicting resource.</p>
|
||||||
|
*
|
||||||
|
* <p>This test runs a subset of tests from {@link OverridePathBasedReferentialIntegrityForDeletesInterceptorTest}
|
||||||
|
* against the {@link BaseMultitenantResourceProviderR4Test}</p>
|
||||||
|
*/
|
||||||
|
public class MultitenantOverridePathBasedReferentialIntegrityForDeletesInterceptorTest extends BaseMultitenantResourceProviderR4Test {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OverridePathBasedReferentialIntegrityForDeletesInterceptor mySvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CascadingDeleteInterceptor myCascadingDeleteInterceptor;
|
||||||
|
|
||||||
|
RequestDetails requestDetails = new SystemRequestDetails();
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void beforeEach() {
|
||||||
|
requestDetails.setTenantId(TENANT_A);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() throws Exception {
|
||||||
|
myPartitionSettings.setAllowReferencesAcrossPartitions(PartitionSettings.CrossPartitionReferenceMode.NOT_ALLOWED);
|
||||||
|
assertFalse(myPartitionSettings.isAllowUnqualifiedCrossPartitionReference());
|
||||||
|
|
||||||
|
myInterceptorRegistry.unregisterInterceptor(mySvc);
|
||||||
|
mySvc.clearPaths();
|
||||||
|
|
||||||
|
super.after();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllowDelete() {
|
||||||
|
|
||||||
|
mySvc.addPath("AuditEvent.agent.who");
|
||||||
|
myInterceptorRegistry.registerInterceptor(mySvc);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("P");
|
||||||
|
patient.setActive(true);
|
||||||
|
myPatientDao.update(patient, requestDetails);
|
||||||
|
|
||||||
|
AuditEvent audit = new AuditEvent();
|
||||||
|
audit.setId("A");
|
||||||
|
audit.addAgent().getWho().setReference("Patient/P");
|
||||||
|
myAuditEventDao.update(audit, requestDetails);
|
||||||
|
|
||||||
|
// Delete should proceed
|
||||||
|
myPatientDao.delete(new IdType("Patient/P"), requestDetails);
|
||||||
|
|
||||||
|
// Make sure we're deleted
|
||||||
|
try {
|
||||||
|
myPatientDao.read(new IdType("Patient/P"), requestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (ResourceGoneException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search should still work
|
||||||
|
IBundleProvider searchOutcome = myAuditEventDao.search(SearchParameterMap.newSynchronous(AuditEvent.SP_AGENT, new ReferenceParam("Patient/P")), requestDetails);
|
||||||
|
assertEquals(1, searchOutcome.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWrongPath() {
|
||||||
|
mySvc.addPath("AuditEvent.identifier");
|
||||||
|
mySvc.addPath("Patient.agent.who");
|
||||||
|
myInterceptorRegistry.registerInterceptor(mySvc);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("P");
|
||||||
|
patient.setActive(true);
|
||||||
|
myPatientDao.update(patient, requestDetails);
|
||||||
|
|
||||||
|
AuditEvent audit = new AuditEvent();
|
||||||
|
audit.setId("A");
|
||||||
|
audit.addAgent().getWho().setReference("Patient/P");
|
||||||
|
myAuditEventDao.update(audit, requestDetails);
|
||||||
|
|
||||||
|
// Delete should proceed
|
||||||
|
try {
|
||||||
|
myPatientDao.delete(new IdType("Patient/P"), requestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (ResourceVersionConflictException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCombineWithCascadeDeleteInterceptor() {
|
||||||
|
try {
|
||||||
|
myInterceptorRegistry.registerInterceptor(myCascadingDeleteInterceptor);
|
||||||
|
|
||||||
|
mySvc.addPath("AuditEvent.agent.who");
|
||||||
|
myInterceptorRegistry.registerInterceptor(mySvc);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("P");
|
||||||
|
patient.setActive(true);
|
||||||
|
myPatientDao.update(patient, requestDetails);
|
||||||
|
|
||||||
|
AuditEvent audit = new AuditEvent();
|
||||||
|
audit.setId("A");
|
||||||
|
audit.addAgent().getWho().setReference("Patient/P");
|
||||||
|
myAuditEventDao.update(audit, requestDetails);
|
||||||
|
|
||||||
|
// Delete should proceed
|
||||||
|
myPatientDao.delete(new IdType("Patient/P"), requestDetails);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
myInterceptorRegistry.unregisterInterceptor(myCascadingDeleteInterceptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
pom.xml
3
pom.xml
|
@ -852,6 +852,9 @@
|
||||||
<name>John D'Amore</name>
|
<name>John D'Amore</name>
|
||||||
<organization>More Informatics</organization>
|
<organization>More Informatics</organization>
|
||||||
</developer>
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>JorisHeadease</id>
|
||||||
|
</developer>
|
||||||
</developers>
|
</developers>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
|
|
Loading…
Reference in New Issue