Add circular reference handling, and DeleteConflict handling

This commit is contained in:
Tadgh 2020-07-27 14:18:40 -07:00
parent 7e694ded3f
commit fac14240c8
4 changed files with 100 additions and 6 deletions

View File

@ -231,6 +231,7 @@ public class EmpiLinkDaoSvc {
*
* @return A list of Long representing the related Person Pids.
*/
@Transactional
public List<Long> deleteAllEmpiLinksAndReturnPersonPids() {
List<EmpiLink> all = myEmpiLinkDao.findAll();
return deleteEmpiLinksAndReturnPersonPids(all);

View File

@ -22,10 +22,19 @@ package ca.uhn.fhir.jpa.empi.svc;
import ca.uhn.fhir.empi.api.IEmpiResetSvc;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.expunge.IResourceExpungeService;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -47,9 +56,18 @@ public class EmpiResetSvcImpl implements IEmpiResetSvc {
@Autowired
private IResourceExpungeService myResourceExpungeService;
@Autowired
private IResourceTableDao myResourceTable;
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private HapiTransactionService myTransactionService;
@Override
public long expungeAllEmpiLinksOfTargetType(String theResourceType) {
throwExceptionIfInvalidTargetType(theResourceType);
List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksOfTypeAndReturnPersonPids(theResourceType);
myResourceExpungeService.expungeCurrentVersionOfResources(null, longs, new AtomicInteger(longs.size()));
return longs.size();
@ -69,9 +87,43 @@ public class EmpiResetSvcImpl implements IEmpiResetSvc {
List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksAndReturnPersonPids();
longs = longs.stream()
.distinct().collect(Collectors.toList());
DeleteConflictList dlc = new DeleteConflictList();
List<Long> finalLongs = longs;
myTransactionService.execute(null, tx -> {
finalLongs.stream().forEach(pid -> {
deleteCascade(pid, dlc);
});
return null;
});
IFhirResourceDao personDao = myDaoRegistry.getResourceDao("Person");
while (!dlc.isEmpty()) {
myTransactionService.execute(null, tx -> {
deleteConflictBatch(dlc, personDao);
return null;
});
}
myResourceExpungeService.expungeHistoricalVersionsOfIds(null, longs, new AtomicInteger(longs.size()));
myResourceExpungeService.expungeCurrentVersionOfResources(null, longs, new AtomicInteger(longs.size()));
//myResourceExpungeService.expungeCurrentVersionOfResources(null, longs, new AtomicInteger(longs.size()));
return longs.size();
}
private void deleteConflictBatch(DeleteConflictList theDcl, IFhirResourceDao<IBaseResource> theDao) {
DeleteConflictList newBatch = new DeleteConflictList();
for (DeleteConflict next: theDcl) {
IdDt nextSource = next.getSourceId();
ourLog.info("Have delete conflict {} - Cascading delete", next);
theDao.delete(nextSource.toVersionless(), newBatch, null, null);
}
theDcl.removeIf(x -> true);
theDcl.addAll(newBatch);
}
private void deleteCascade(Long pid, DeleteConflictList theDcl) {
ourLog.debug("About to cascade delete: " + pid);
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao("Person");
resourceDao.delete(new IdType("Person/" + pid), theDcl, null, null);
}
}

View File

@ -73,6 +73,8 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
private static final ContactPoint TEST_TELECOM = new ContactPoint()
.setSystem(ContactPoint.ContactPointSystem.PHONE)
.setValue("555-555-5555");
private static final String NAME_GIVEN_FRANK = "Frank";
protected static final String FRANK_ID = "ID.FRANK.789";
@Autowired
protected FhirContext myFhirContext;
@ -244,6 +246,11 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
return buildPatientWithNameAndId(NAME_GIVEN_PAUL, PAUL_ID);
}
@Nonnull
protected Patient buildFrankPatient() {
return buildPatientWithNameAndId(NAME_GIVEN_FRANK, FRANK_ID);
}
@Nonnull
protected Patient buildJaneWithBirthday(Date theToday) {
return buildPatientWithNameIdAndBirthday(NAME_GIVEN_JANE, JANE_ID, theToday);

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.jpa.empi.provider;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.r4.model.Patient;
@ -92,14 +94,46 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
Person personFromTarget = getPersonFromTarget(patientAndUpdateLinks);
Person personFromTarget2 = getPersonFromTarget(patientAndUpdateLinks1);
Person.PersonLinkComponent plc = new Person.PersonLinkComponent();
plc.setAssurance(Person.IdentityAssuranceLevel.LEVEL2);
plc.setTarget(new Reference(personFromTarget2.getIdElement().toUnqualifiedVersionless()));
personFromTarget.getLink().add(plc);
myPersonDao.update(personFromTarget);
linkPersons(personFromTarget, personFromTarget2);
//SUT
myEmpiProviderR4.clearEmpiLinks(null);
assertNoPatientLinksExist();
IBundleProvider search = myPersonDao.search(new SearchParameterMap().setLoadSynchronous(true));
assertThat(search.size(), is(equalTo(0)));
}
@Test
public void testPersonsWithCircularReferenceCanBeCleared() {
Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildPaulPatient());
Patient patientAndUpdateLinks1 = createPatientAndUpdateLinks(buildJanePatient());
Patient patientAndUpdateLinks2 = createPatientAndUpdateLinks(buildFrankPatient());
Person personFromTarget = getPersonFromTarget(patientAndUpdateLinks);
Person personFromTarget1 = getPersonFromTarget(patientAndUpdateLinks1);
Person personFromTarget2 = getPersonFromTarget(patientAndUpdateLinks2);
// A -> B -> C -> A linkages.
linkPersons(personFromTarget, personFromTarget1);
linkPersons(personFromTarget1, personFromTarget2);
linkPersons(personFromTarget2, personFromTarget);
//SUT
myEmpiProviderR4.clearEmpiLinks(null);
assertNoPatientLinksExist();
IBundleProvider search = myPersonDao.search(new SearchParameterMap().setLoadSynchronous(true));
assertThat(search.size(), is(equalTo(0)));
}
private void linkPersons(Person theSourcePerson, Person theTargetPerson) {
Person.PersonLinkComponent plc1 = new Person.PersonLinkComponent();
plc1.setAssurance(Person.IdentityAssuranceLevel.LEVEL2);
plc1.setTarget(new Reference(theTargetPerson.getIdElement().toUnqualifiedVersionless()));
theSourcePerson.getLink().add(plc1);
myPersonDao.update(theSourcePerson);
}
@Test