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:
JorisHeadease 2023-01-31 14:41:41 +01:00 committed by GitHub
parent eaa1e62655
commit 9665476c00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 156 additions and 2 deletions

View File

@ -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!"

View File

@ -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) {

View File

@ -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);
}
}
}

View File

@ -852,6 +852,9 @@
<name>John D'Amore</name>
<organization>More Informatics</organization>
</developer>
<developer>
<id>JorisHeadease</id>
</developer>
</developers>
<licenses>