Changes to enable request delete of specific Code System version.

This commit is contained in:
ianmarshall 2020-08-25 12:06:07 -04:00
parent 29b293f179
commit 749c2bc4be
11 changed files with 242 additions and 69 deletions

View File

@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.HapiFhirResourceDaoCodeSystemUtil;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
@ -67,6 +69,8 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
@Autowired @Autowired
private ITermCodeSystemDao myCsDao; private ITermCodeSystemDao myCsDao;
@Autowired @Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
@Autowired @Autowired
private FhirContext myFhirContext; private FhirContext myFhirContext;
@ -148,13 +152,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) { protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
super.preDelete(theResourceToDelete, theEntityToDelete); super.preDelete(theResourceToDelete, theEntityToDelete);
String codeSystemUrl = theResourceToDelete.getUrl(); HapiFhirResourceDaoCodeSystemUtil.deleteCodeSystemEntities(myCsDao, myCsvDao, myTermDeferredStorageSvc, theResourceToDelete.getUrl(),
if (isNotBlank(codeSystemUrl)) { theResourceToDelete.getVersion());
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
if (persCs != null) {
myTermDeferredStorageSvc.deleteCodeSystem(persCs);
}
}
} }
@Override @Override

View File

@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.HapiFhirResourceDaoCodeSystemUtil;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
@ -61,6 +63,8 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
@Autowired @Autowired
private ITermCodeSystemDao myCsDao; private ITermCodeSystemDao myCsDao;
@Autowired @Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
@Autowired @Autowired
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc; protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
@ -146,13 +150,9 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) { protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
super.preDelete(theResourceToDelete, theEntityToDelete); super.preDelete(theResourceToDelete, theEntityToDelete);
String codeSystemUrl = theResourceToDelete.getUrl(); HapiFhirResourceDaoCodeSystemUtil.deleteCodeSystemEntities(myCsDao, myCsvDao, myTermDeferredStorageSvc, theResourceToDelete.getUrl(),
if (isNotBlank(codeSystemUrl)) { theResourceToDelete.getVersion());
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
if (persCs != null) {
myTermDeferredStorageSvc.deleteCodeSystem(persCs);
}
}
} }
@Override @Override

View File

@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.HapiFhirResourceDaoCodeSystemUtil;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
@ -65,6 +67,8 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
@Autowired @Autowired
private ITermCodeSystemDao myCsDao; private ITermCodeSystemDao myCsDao;
@Autowired @Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
@Autowired @Autowired
private FhirContext myFhirContext; private FhirContext myFhirContext;
@ -117,11 +121,11 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
codeSystemVersion = theVersion.getValue(); codeSystemVersion = theVersion.getValue();
} }
ourLog.info("Looking up {} / {}, version {}", system, code, codeSystemVersion); ourLog.debug("Looking up {} / {}, version {}", system, code, codeSystemVersion);
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) { if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
ourLog.info("Code system {} is supported", system); ourLog.debug("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, codeSystemVersion); IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, codeSystemVersion);
if (retVal != null) { if (retVal != null) {
return retVal; return retVal;
@ -148,13 +152,9 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) { protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
super.preDelete(theResourceToDelete, theEntityToDelete); super.preDelete(theResourceToDelete, theEntityToDelete);
String codeSystemUrl = theResourceToDelete.getUrl(); HapiFhirResourceDaoCodeSystemUtil.deleteCodeSystemEntities(myCsDao, myCsvDao, myTermDeferredStorageSvc, theResourceToDelete.getUrl(),
if (isNotBlank(codeSystemUrl)) { theResourceToDelete.getVersion());
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
if (persCs != null) {
myTermDeferredStorageSvc.deleteCodeSystem(persCs);
}
}
} }
@Override @Override

View File

@ -58,7 +58,7 @@ public class TermCodeSystem implements Serializable {
@Column(name = "PID") @Column(name = "PID")
private Long myPid; private Long myPid;
@OneToOne(fetch = FetchType.LAZY) @OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_RES")) @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = true, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_RES"))
private ResourceTable myResource; private ResourceTable myResource;
@Column(name = "RES_ID", insertable = false, updatable = false) @Column(name = "RES_ID", insertable = false, updatable = false)
private Long myResourcePid; private Long myResourcePid;

View File

@ -231,6 +231,14 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
}); });
} }
@Override
@Transactional(propagation = Propagation.NEVER)
public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) {
ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getPid());
deleteCodeSystemVersion(theCodeSystemVersion.getPid());
}
/** /**
* Returns the number of saved concepts * Returns the number of saved concepts
*/ */
@ -680,9 +688,9 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
if (codeSystem == null) { if (codeSystem == null) {
codeSystem = new TermCodeSystem(); codeSystem = new TermCodeSystem();
} }
codeSystem.setResource(theCodeSystemResourceTable);
} }
codeSystem.setResource(theCodeSystemResourceTable);
codeSystem.setCodeSystemUri(theSystemUri); codeSystem.setCodeSystemUri(theSystemUri);
codeSystem.setName(theSystemName); codeSystem.setName(theSystemName);
codeSystem = myCodeSystemDao.save(codeSystem); codeSystem = myCodeSystemDao.save(codeSystem);
@ -692,15 +700,19 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
private void validateCodeSystemVersionUpdate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) { private void validateCodeSystemVersionUpdate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) {
// Check if CodeSystemVersion entity already exists. // Check if CodeSystemVersion entity already exists.
TermCodeSystemVersion codeSystemVersionEntity; TermCodeSystemVersion codeSystemVersionEntity;
String msg; String msg = null;
if (theSystemVersionId == null) { if (theSystemVersionId == null) {
codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid()); codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid());
if (codeSystemVersionEntity != null) {
msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri,
theCodeSystem.getResource().getIdDt().toUnqualifiedVersionless().getValue()); codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue());
}
} else { } else {
codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId); codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId);
if (codeSystemVersionEntity != null) {
msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri,
theSystemVersionId, theCodeSystem.getResource().getIdDt().toUnqualifiedVersionless().getValue()); theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue());
}
} }
// Throw exception if the CodeSystemVersion is being duplicated. // Throw exception if the CodeSystemVersion is being duplicated.
if (codeSystemVersionEntity != null) { if (codeSystemVersionEntity != null) {

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.HapiJob;
@ -68,6 +69,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
protected PlatformTransactionManager myTransactionMgr; protected PlatformTransactionManager myTransactionMgr;
private boolean myProcessDeferred = true; private boolean myProcessDeferred = true;
private List<TermCodeSystem> myDefferedCodeSystemsDeletions = Collections.synchronizedList(new ArrayList<>()); private List<TermCodeSystem> myDefferedCodeSystemsDeletions = Collections.synchronizedList(new ArrayList<>());
private List<TermCodeSystemVersion> myDefferedCodeSystemVersionsDeletions = Collections.synchronizedList(new ArrayList<>());
private List<TermConcept> myDeferredConcepts = Collections.synchronizedList(new ArrayList<>()); private List<TermConcept> myDeferredConcepts = Collections.synchronizedList(new ArrayList<>());
private List<ValueSet> myDeferredValueSets = Collections.synchronizedList(new ArrayList<>()); private List<ValueSet> myDeferredValueSets = Collections.synchronizedList(new ArrayList<>());
private List<ConceptMap> myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>()); private List<ConceptMap> myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>());
@ -115,6 +117,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
myDefferedCodeSystemsDeletions.add(theCodeSystem); myDefferedCodeSystemsDeletions.add(theCodeSystem);
} }
@Override
@Transactional
public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) {
myDefferedCodeSystemVersionsDeletions.add(theCodeSystemVersion);
}
@Override @Override
public void saveAllDeferred() { public void saveAllDeferred() {
while (!isStorageQueueEmpty()) { while (!isStorageQueueEmpty()) {
@ -266,6 +274,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
} }
private void processDeferredCodeSystemDeletions() { private void processDeferredCodeSystemDeletions() {
for (TermCodeSystemVersion next : myDefferedCodeSystemVersionsDeletions) {
myCodeSystemStorageSvc.deleteCodeSystemVersion(next);
}
myDefferedCodeSystemVersionsDeletions.clear();
for (TermCodeSystem next : myDefferedCodeSystemsDeletions) { for (TermCodeSystem next : myDefferedCodeSystemsDeletions) {
myCodeSystemStorageSvc.deleteCodeSystem(next); myCodeSystemStorageSvc.deleteCodeSystem(next);
} }
@ -300,7 +314,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
} }
private boolean isDeferredCodeSystemDeletions() { private boolean isDeferredCodeSystemDeletions() {
return !myDefferedCodeSystemsDeletions.isEmpty(); return !myDefferedCodeSystemsDeletions.isEmpty() || !myDefferedCodeSystemVersionsDeletions.isEmpty();
} }
private boolean isDeferredConcepts() { private boolean isDeferredConcepts() {

View File

@ -41,6 +41,8 @@ public interface ITermCodeSystemStorageSvc {
void deleteCodeSystem(TermCodeSystem theCodeSystem); void deleteCodeSystem(TermCodeSystem theCodeSystem);
void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion);
void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable); void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable);
/** /**

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term.api;
*/ */
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap;
@ -54,6 +55,8 @@ public interface ITermDeferredStorageSvc {
void deleteCodeSystem(TermCodeSystem theCodeSystem); void deleteCodeSystem(TermCodeSystem theCodeSystem);
void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion);
/** /**
* This is mostly here for unit tests - Saves any and all deferred concepts and links * This is mostly here for unit tests - Saves any and all deferred concepts and links
*/ */

View File

@ -1,11 +1,19 @@
package ca.uhn.fhir.jpa.term; package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test {
@ -14,47 +22,123 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test {
@Test @Test
public void testStoreNewCodeSystemVersionForExistingCodeSystemNoVersionId() { public void testStoreNewCodeSystemVersionForExistingCodeSystemNoVersionId() {
CodeSystem upload = createCodeSystemWithMoreThan100Concepts(); CodeSystem firstUpload = createCodeSystemWithMoreThan100Concepts();
CodeSystem duplicateUpload = createCodeSystemWithMoreThan100Concepts();
// Create CodeSystem resource testCreatingAndUpdatingCodeSystemEntity(firstUpload, duplicateUpload, 125, "Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/");
ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(upload, mySrd).getEntity();
// Update the CodeSystem resource runInTransaction(() -> {
runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity)); assertEquals(1, myTermCodeSystemDao.count());
assertEquals(1, myTermCodeSystemVersionDao.count());
TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM);
/* TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidVersionIsNull( myTermCodeSystem.getPid());
Because there are more than 100 concepts in the code system, the first 100 will be persisted immediately and assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid());
the remaining 25 concepts will be queued up for "deferred save". assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId());
});
As the CodeSystem was persisted twice, the extra 25 term concepts will be queued twice, each with a different
CodeSystem version PID. Only one set of the term concepts should be persisted (i.e. 125 term concepts in total).
*/
myTerminologyDeferredStorageSvc.setProcessDeferred(true);
myTerminologyDeferredStorageSvc.saveDeferred();
assertEquals(125, myTermConceptDao.count());
} }
@Test @Test
public void testStoreNewCodeSystemVersionForExistingCodeSystemVersionId() { public void testStoreNewCodeSystemVersionForExistingCodeSystemVersionId() {
CodeSystem upload = createCodeSystemWithMoreThan100Concepts(); CodeSystem firstUpload = createCodeSystemWithMoreThan100Concepts();
upload.setVersion("1"); firstUpload.setVersion("1");
// Create CodeSystem resource CodeSystem duplicateUpload = createCodeSystemWithMoreThan100Concepts();
ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(upload, mySrd).getEntity(); duplicateUpload.setVersion("1");
// Update the CodeSystem resource testCreatingAndUpdatingCodeSystemEntity(firstUpload, duplicateUpload, 125,"Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\" and CodeSystem.version \"1\", already have one with resource ID: CodeSystem/");
runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity));
/* runInTransaction(() -> {
Because there are more than 100 concepts in the code system, the first 100 will be persisted immediately and assertEquals(1, myTermCodeSystemDao.count());
the remaining 25 concepts will be queued up for "deferred save". assertEquals(1, myTermCodeSystemVersionDao.count());
TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM);
TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion( myTermCodeSystem.getPid(), "1");
assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid());
assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId());
});
// Now add a second version
firstUpload = createCodeSystemWithMoreThan100Concepts();
firstUpload.setVersion("2");
duplicateUpload = createCodeSystemWithMoreThan100Concepts();
duplicateUpload.setVersion("2");
testCreatingAndUpdatingCodeSystemEntity(firstUpload, duplicateUpload, 251,"Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\" and CodeSystem.version \"2\", already have one with resource ID: CodeSystem/");
runInTransaction(() -> {
assertEquals(1, myTermCodeSystemDao.count());
assertEquals(2, myTermCodeSystemVersionDao.count());
TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM);
TermCodeSystemVersion mySecondTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2");
assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), mySecondTermCodeSystemVersion.getPid());
assertEquals(myTermCodeSystem.getResource().getId(), mySecondTermCodeSystemVersion.getResource().getId());
});
}
@Test
public void testDeleteCodeSystem() {
CodeSystem codeSystemToDelete = createSmallCodeSystem();
// Create CodeSystem resource and entity
ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(codeSystemToDelete, mySrd).getEntity();
validateCodeSystemUpdates(10);
runInTransaction(() -> {
assertEquals(1, myTermCodeSystemDao.count());
assertEquals(1, myTermCodeSystemVersionDao.count());
});
// Attempt to delete
myCodeSystemDao.delete(codeSystemResourceEntity.getIdDt(), mySrd);
validateCodeSystemUpdates(0);
runInTransaction(() -> {
assertEquals(0, myTermCodeSystemDao.count());
assertEquals(0, myTermCodeSystemVersionDao.count());
});
}
@Test
public void testDeleteCodeSystemVersion() {
CodeSystem firstCodeSystemVersion = createSmallCodeSystem();
firstCodeSystemVersion.setVersion("1");
// Create first CodeSystem resource and entity
ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(firstCodeSystemVersion, mySrd).getEntity();
validateCodeSystemUpdates(10);
runInTransaction(() -> {
assertEquals(1, myTermCodeSystemDao.count());
assertEquals(1, myTermCodeSystemVersionDao.count());
});
CodeSystem secondCodeSystemVersion = createSmallCodeSystem();
secondCodeSystemVersion.setVersion("2");
// Create CodeSystem resource and entity
myCodeSystemDao.create(secondCodeSystemVersion, mySrd);
validateCodeSystemUpdates(20);
runInTransaction(() -> {
assertEquals(1, myTermCodeSystemDao.count());
assertEquals(2, myTermCodeSystemVersionDao.count());
});
// Attempt to delete first version
myCodeSystemDao.delete(codeSystemResourceEntity.getIdDt(), mySrd);
validateCodeSystemUpdates(10);
runInTransaction(() -> {
assertEquals(1, myTermCodeSystemDao.count());
assertEquals(1, myTermCodeSystemVersionDao.count());
});
As the CodeSystem was persisted twice, the extra 25 term concepts will be queued twice, each with a different
CodeSystem version PID. Only one set of the term concepts should be persisted (i.e. 125 term concepts in total).
*/
myTerminologyDeferredStorageSvc.setProcessDeferred(true);
myTerminologyDeferredStorageSvc.saveDeferred();
assertEquals(125, myTermConceptDao.count());
} }
private CodeSystem createCodeSystemWithMoreThan100Concepts() { private CodeSystem createCodeSystemWithMoreThan100Concepts() {
@ -70,5 +154,56 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test {
} }
private CodeSystem createSmallCodeSystem() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
for (int i = 0; i < 10; i++) {
codeSystem.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeA " + i)));
}
codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
return codeSystem;
}
private void testCreatingAndUpdatingCodeSystemEntity(CodeSystem theUpload, CodeSystem theDuplicate, int expectedCnt, String theDuplicateErrorBaseMsg) {
// Create CodeSystem resource
ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(theUpload, mySrd).getEntity();
// Create the CodeSystem and CodeSystemVersion entities
validateCodeSystemUpdates(expectedCnt);
// Update the CodeSystem
theUpload.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeB")));
// Update the CodeSystem and CodeSystemVersion entities
runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(theUpload, codeSystemResourceEntity));
validateCodeSystemUpdates(expectedCnt+1);
// Try duplicating the CodeSystem
Long originalResId = codeSystemResourceEntity.getId();
try {
myCodeSystemDao.create(theDuplicate, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals(theDuplicateErrorBaseMsg + originalResId, e.getMessage());
}
// Try updating code system when content mode is NOT PRESENT
theUpload.setConcept(new ArrayList<>());
theUpload.setContent((CodeSystem.CodeSystemContentMode.NOTPRESENT));
runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(theUpload, codeSystemResourceEntity));
validateCodeSystemUpdates(expectedCnt+1);
}
private void validateCodeSystemUpdates(int theExpectedConceptCount) {
myTerminologyDeferredStorageSvc.setProcessDeferred(true);
myTerminologyDeferredStorageSvc.saveDeferred();
myTerminologyDeferredStorageSvc.setProcessDeferred(false);
assertEquals(theExpectedConceptCount, myTermConceptDao.count());
}
} }

View File

@ -351,12 +351,21 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
CodeSystem loincCS = mySystemCaptor.getValue(); CodeSystem loincCS = mySystemCaptor.getValue();
assertEquals("2.67", loincCS.getVersion()); assertEquals("2.67", loincCS.getVersion());
// Update LOINC marked as version 2.67
myFiles = new ZipCollectionBuilder();
addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v267_loincupload.properties");
mySvc.loadLoinc(myFiles.getFiles(), mySrd);
verify(myTermCodeSystemStorageSvc, times(2)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture());
loincCS = mySystemCaptor.getValue();
assertEquals("2.67", loincCS.getVersion());
// Load LOINC marked as version 2.68 // Load LOINC marked as version 2.68
myFiles = new ZipCollectionBuilder(); myFiles = new ZipCollectionBuilder();
addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v268_loincupload.properties"); addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v268_loincupload.properties");
mySvc.loadLoinc(myFiles.getFiles(), mySrd); mySvc.loadLoinc(myFiles.getFiles(), mySrd);
verify(myTermCodeSystemStorageSvc, times(2)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); verify(myTermCodeSystemStorageSvc, times(3)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture());
loincCS = mySystemCaptor.getValue(); loincCS = mySystemCaptor.getValue();
assertEquals("2.68", loincCS.getVersion()); assertEquals("2.68", loincCS.getVersion());

View File

@ -12,7 +12,6 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.model.dstu2.valueset.ContentTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.CanonicalType;
@ -25,7 +24,6 @@ import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.hl7.fhir.r4.model.codesystems.HttpVerb;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -1787,10 +1785,10 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
createCodeSystem(); createCodeSystem();
IValidationSupport.CodeValidationResult validation = myTermSvc.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), CS_URL, "ParentWithNoChildrenA", null, null); IValidationSupport.CodeValidationResult validation = myTermSvc.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), CS_URL, "ParentWithNoChildrenA", null, null);
assertEquals(true, validation.isOk()); assertTrue(validation.isOk());
validation = myTermSvc.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), CS_URL, "ZZZZZZZ", null, null); validation = myTermSvc.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), CS_URL, "ZZZZZZZ", null, null);
assertEquals(false, validation.isOk()); assertFalse(validation.isOk());
} }
@Test @Test
@ -1927,8 +1925,8 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
codeSystem codeSystem
.addConcept().setCode("C").setDisplay("Code C"); .addConcept().setCode("C").setDisplay("Code C");
myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); IIdType id_v2 = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "C"); codes = myTermSvc.findCodesBelow(id_v2.getIdPartAsLong(), id_v2.getVersionIdPartAsLong(), "C");
assertThat(toCodes(codes), containsInAnyOrder("C")); assertThat(toCodes(codes), containsInAnyOrder("C"));
runInTransaction(() -> { runInTransaction(() -> {
@ -1939,7 +1937,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
Set<TermConcept> termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts()); Set<TermConcept> termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts());
assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B", "C")); assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B", "C"));
TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id_v2.getIdPartAsLong());
assertEquals("2", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); assertEquals("2", termCodeSystem.getCurrentVersion().getCodeSystemVersionId());
}); });
} }