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.DeleteConflictList;
|
||||
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.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -99,7 +100,7 @@ public class OverridePathBasedReferentialIntegrityForDeletesInterceptor {
|
|||
* Interceptor hook method. Do not invoke directly.
|
||||
*/
|
||||
@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) {
|
||||
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();
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue