Merge remote-tracking branch 'remotes/origin/feature_20200830_term_multi_version_support' into feature_2020_09_02_term_multi_version_support
This commit is contained in:
commit
7b15f85fa1
|
@ -183,6 +183,24 @@ public interface IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||
*
|
||||
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theOptions Provides options controlling the validation
|
||||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @param theSystemVersion The code system version.
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
default CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, String theSystemVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
|
@ -212,6 +230,19 @@ public interface IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a code using the system, system version and code value
|
||||
*
|
||||
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theSystem The CodeSystem URL
|
||||
* @param theCode The code
|
||||
* @param theSystemVersion The CodeSystem version
|
||||
*/
|
||||
default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theSystemVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the given valueset can be validated by the given
|
||||
* validation support module
|
||||
|
|
|
@ -143,6 +143,7 @@ ca.uhn.fhir.jpa.binstore.BinaryAccessProvider.unknownType=Content in resource of
|
|||
|
||||
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrl=Can not create multiple CodeSystem resources with CodeSystem.url "{0}", already have one with resource ID: {1}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrlAndVersion=Can not create multiple CodeSystem resources with CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrlAndVersion=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", ConceptMap.version "{1}", already have one with resource ID: {2}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can not create multiple ValueSet resources with ValueSet.url "{0}", already have one with resource ID: {1}
|
||||
|
|
|
@ -42,6 +42,11 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext
|
|||
|
||||
SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CD theCodingA, CD theCodingB, RequestDetails theRequestDetails);
|
||||
|
||||
@Nonnull
|
||||
IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CD theCoding, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails);
|
||||
|
||||
SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CD theCodingA, CD theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails);
|
||||
|
||||
class SubsumesResult {
|
||||
|
||||
private final ConceptSubsumptionOutcome myOutcome;
|
||||
|
|
|
@ -43,7 +43,6 @@ import ca.uhn.fhir.rest.param.TokenParam;
|
|||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import org.apache.commons.codec.binary.StringUtils;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -235,6 +234,11 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao<ValueS
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CodingDt theCoding, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CodingDt theCoding, RequestDetails theRequest) {
|
||||
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
|
||||
|
@ -280,6 +284,11 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao<ValueS
|
|||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CodingDt theCodingA, CodingDt theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
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.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class HapiFhirResourceDaoCodeSystemUtil {
|
||||
|
||||
static public void deleteCodeSystemEntities(ITermCodeSystemDao theCsDao, ITermCodeSystemVersionDao theCsvDao,
|
||||
ITermDeferredStorageSvc theTermDeferredStorageSvc, String theCodeSystemUrl,
|
||||
String theCodeSystemVersion) {
|
||||
if (isNotBlank(theCodeSystemUrl)) {
|
||||
TermCodeSystem persCs = theCsDao.findByCodeSystemUri(theCodeSystemUrl);
|
||||
if (persCs != null) {
|
||||
if (theCodeSystemVersion != null) {
|
||||
TermCodeSystemVersion persCsVersion = theCsvDao.findByCodeSystemPidAndVersion(persCs.getPid(), theCodeSystemVersion);
|
||||
theTermDeferredStorageSvc.deleteCodeSystemVersion(persCsVersion);
|
||||
} else {
|
||||
theTermDeferredStorageSvc.deleteCodeSystem(persCs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,9 +35,19 @@ public interface ITermCodeSystemVersionDao extends JpaRepository<TermCodeSystemV
|
|||
@Query("DELETE FROM TermCodeSystemVersion csv WHERE csv.myCodeSystem = :cs")
|
||||
void deleteForCodeSystem(@Param("cs") TermCodeSystem theCodeSystem);
|
||||
|
||||
@Modifying
|
||||
@Query("DELETE FROM TermCodeSystemVersion csv WHERE csv.myId = :pid")
|
||||
void delete(@Param("pid") Long codesystemversion_pid);
|
||||
|
||||
@Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemPid = :codesystem_pid")
|
||||
List<TermCodeSystemVersion> findByCodeSystemPid(@Param("codesystem_pid") Long theCodeSystemPid);
|
||||
|
||||
@Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemPid = :codesystem_pid AND cs.myCodeSystemVersionId = :codesystem_version_id")
|
||||
TermCodeSystemVersion findByCodeSystemPidAndVersion(@Param("codesystem_pid") Long theCodeSystemPid, @Param("codesystem_version_id") String theCodeSystemVersionId);
|
||||
|
||||
@Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemPid = :codesystem_pid AND cs.myCodeSystemVersionId IS NULL")
|
||||
TermCodeSystemVersion findByCodeSystemPidVersionIsNull(@Param("codesystem_pid") Long theCodeSystemPid);
|
||||
|
||||
@Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myResourcePid = :resource_id")
|
||||
List<TermCodeSystemVersion> findByCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid);
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||
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.ITermCodeSystemVersionDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||
|
@ -67,6 +69,8 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
|
|||
@Autowired
|
||||
private ITermCodeSystemDao myCsDao;
|
||||
@Autowired
|
||||
private ITermCodeSystemVersionDao myCsvDao;
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
@ -89,6 +93,12 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
|
|||
@Nonnull
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
|
||||
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
|
||||
boolean haveCode = theCode != null && theCode.isEmpty() == false;
|
||||
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
|
||||
|
@ -102,19 +112,24 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
|
|||
|
||||
String code;
|
||||
String system;
|
||||
String codeSystemVersion = null;
|
||||
if (haveCoding) {
|
||||
code = theCoding.getCode();
|
||||
system = theCoding.getSystem();
|
||||
codeSystemVersion = theCoding.getVersion();
|
||||
} else {
|
||||
code = theCode.getValue();
|
||||
system = theSystem.getValue();
|
||||
if (theVersion != null) {
|
||||
codeSystemVersion = theVersion.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.debug("Looking up {} / {}", system, code);
|
||||
ourLog.debug("Looking up {} / {}, version {}", system, code, codeSystemVersion);
|
||||
|
||||
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
|
||||
ourLog.debug("Code system {} is supported", system);
|
||||
IValidationSupport.LookupCodeResult result = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code);
|
||||
IValidationSupport.LookupCodeResult result = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, codeSystemVersion);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
@ -124,6 +139,13 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
|
|||
return IValidationSupport.LookupCodeResult.notFound(system, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null;
|
||||
String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null;
|
||||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
|
||||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
|
||||
|
@ -133,13 +155,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
|
|||
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
|
||||
super.preDelete(theResourceToDelete, theEntityToDelete);
|
||||
|
||||
String codeSystemUrl = theResourceToDelete.getUrl();
|
||||
if (isNotBlank(codeSystemUrl)) {
|
||||
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
|
||||
if (persCs != null) {
|
||||
myTermDeferredStorageSvc.deleteCodeSystem(persCs);
|
||||
}
|
||||
}
|
||||
HapiFhirResourceDaoCodeSystemUtil.deleteCodeSystemEntities(myCsDao, myCsvDao, myTermDeferredStorageSvc, theResourceToDelete.getUrl(),
|
||||
theResourceToDelete.getVersion());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||
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.ITermCodeSystemVersionDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||
|
@ -61,6 +63,8 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
|
|||
@Autowired
|
||||
private ITermCodeSystemDao myCsDao;
|
||||
@Autowired
|
||||
private ITermCodeSystemVersionDao myCsvDao;
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
|
||||
|
@ -84,6 +88,12 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
|
|||
@Nonnull
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
|
||||
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
|
||||
boolean haveCode = theCode != null && theCode.isEmpty() == false;
|
||||
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
|
||||
|
@ -97,20 +107,25 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
|
|||
|
||||
String code;
|
||||
String system;
|
||||
String codeSystemVersion = null;
|
||||
if (haveCoding) {
|
||||
code = theCoding.getCode();
|
||||
system = theCoding.getSystem();
|
||||
codeSystemVersion = theCoding.getVersion();
|
||||
} else {
|
||||
code = theCode.getValue();
|
||||
system = theSystem.getValue();
|
||||
if (theVersion != null) {
|
||||
codeSystemVersion = theVersion.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.debug("Looking up {} / {}", system, code);
|
||||
ourLog.debug("Looking up {} / {}, version {}", system, code, codeSystemVersion);
|
||||
|
||||
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
|
||||
|
||||
ourLog.debug("Code system {} is supported", system);
|
||||
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code);
|
||||
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, codeSystemVersion);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
|
@ -122,6 +137,13 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null;
|
||||
String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null;
|
||||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
|
||||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
|
||||
|
@ -131,13 +153,9 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
|
|||
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
|
||||
super.preDelete(theResourceToDelete, theEntityToDelete);
|
||||
|
||||
String codeSystemUrl = theResourceToDelete.getUrl();
|
||||
if (isNotBlank(codeSystemUrl)) {
|
||||
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
|
||||
if (persCs != null) {
|
||||
myTermDeferredStorageSvc.deleteCodeSystem(persCs);
|
||||
}
|
||||
}
|
||||
HapiFhirResourceDaoCodeSystemUtil.deleteCodeSystemEntities(myCsDao, myCsvDao, myTermDeferredStorageSvc, theResourceToDelete.getUrl(),
|
||||
theResourceToDelete.getVersion());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -113,14 +113,17 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet>
|
|||
ValueSet source = new ValueSet();
|
||||
source.setUrl(theUri);
|
||||
|
||||
source.getCompose().addInclude().addValueSet(theUri);
|
||||
// source.getCompose().addInclude().addValueSet(theUri);
|
||||
|
||||
if (isNotBlank(theFilter)) {
|
||||
ConceptSetComponent include = source.getCompose().addInclude();
|
||||
ConceptSetFilterComponent filter = include.addFilter();
|
||||
// ConceptSetComponent include = source.getCompose().addInclude();
|
||||
// ConceptSetFilterComponent filter = include.addFilter();
|
||||
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
|
||||
filter.setProperty("display");
|
||||
filter.setOp(FilterOperator.EQUAL);
|
||||
filter.setValue(theFilter);
|
||||
} else {
|
||||
source.getCompose().addInclude().addValueSet(theUri);
|
||||
}
|
||||
|
||||
ValueSet retVal = doExpand(source);
|
||||
|
@ -148,14 +151,17 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet>
|
|||
ValueSet source = new ValueSet();
|
||||
source.setUrl(theUri);
|
||||
|
||||
source.getCompose().addInclude().addValueSet(theUri);
|
||||
// source.getCompose().addInclude().addValueSet(theUri);
|
||||
|
||||
if (isNotBlank(theFilter)) {
|
||||
ConceptSetComponent include = source.getCompose().addInclude();
|
||||
ConceptSetFilterComponent filter = include.addFilter();
|
||||
// ConceptSetComponent include = source.getCompose().addInclude();
|
||||
// ConceptSetFilterComponent filter = include.addFilter();
|
||||
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
|
||||
filter.setProperty("display");
|
||||
filter.setOp(FilterOperator.EQUAL);
|
||||
filter.setValue(theFilter);
|
||||
} else {
|
||||
source.getCompose().addInclude().addValueSet(theUri);
|
||||
}
|
||||
|
||||
ValueSet retVal = doExpand(source, theOffset, theCount);
|
||||
|
|
|
@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||
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.ITermCodeSystemVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
|
@ -65,6 +67,8 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
|
|||
@Autowired
|
||||
private ITermCodeSystemDao myCsDao;
|
||||
@Autowired
|
||||
private ITermCodeSystemVersionDao myCsvDao;
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
@ -86,6 +90,12 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
|
|||
@Nonnull
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
|
||||
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
|
||||
boolean haveCode = theCode != null && theCode.isEmpty() == false;
|
||||
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
|
||||
|
@ -99,20 +109,25 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
|
|||
|
||||
String code;
|
||||
String system;
|
||||
String codeSystemVersion = null;
|
||||
if (haveCoding) {
|
||||
code = theCoding.getCode();
|
||||
system = theCoding.getSystem();
|
||||
codeSystemVersion = theCoding.getVersion();
|
||||
} else {
|
||||
code = theCode.getValue();
|
||||
system = theSystem.getValue();
|
||||
if (theVersion != null) {
|
||||
codeSystemVersion = theVersion.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Looking up {} / {}", system, code);
|
||||
ourLog.debug("Looking up {} / {}, version {}", system, code, codeSystemVersion);
|
||||
|
||||
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
|
||||
|
||||
ourLog.info("Code system {} is supported", system);
|
||||
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code);
|
||||
ourLog.debug("Code system {} is supported", system);
|
||||
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, codeSystemVersion);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
|
@ -124,6 +139,13 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails) {
|
||||
String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null;
|
||||
String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null;
|
||||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
|
||||
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
|
||||
|
@ -133,13 +155,9 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
|
|||
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
|
||||
super.preDelete(theResourceToDelete, theEntityToDelete);
|
||||
|
||||
String codeSystemUrl = theResourceToDelete.getUrl();
|
||||
if (isNotBlank(codeSystemUrl)) {
|
||||
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
|
||||
if (persCs != null) {
|
||||
myTermDeferredStorageSvc.deleteCodeSystem(persCs);
|
||||
}
|
||||
}
|
||||
HapiFhirResourceDaoCodeSystemUtil.deleteCodeSystemEntities(myCsDao, myCsvDao, myTermDeferredStorageSvc, theResourceToDelete.getUrl(),
|
||||
theResourceToDelete.getVersion());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -58,7 +58,7 @@ public class TermCodeSystem implements Serializable {
|
|||
@Column(name = "PID")
|
||||
private Long myPid;
|
||||
@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;
|
||||
@Column(name = "RES_ID", insertable = false, updatable = false)
|
||||
private Long myResourcePid;
|
||||
|
|
|
@ -40,17 +40,21 @@ import javax.persistence.OneToMany;
|
|||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.length;
|
||||
|
||||
@Table(name = "TRM_CODESYSTEM_VER"
|
||||
@Table(name = "TRM_CODESYSTEM_VER",
|
||||
// Note, we used to have a constraint named IDX_CSV_RESOURCEPID_AND_VER (don't reuse this)
|
||||
)
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(name = TermCodeSystemVersion.IDX_CODESYSTEM_AND_VER, columnNames = {"CODESYSTEM_PID", "CS_VERSION_ID"})
|
||||
})
|
||||
@Entity()
|
||||
public class TermCodeSystemVersion implements Serializable {
|
||||
public static final String IDX_CODESYSTEM_AND_VER = "IDX_CODESYSTEM_AND_VER";
|
||||
public static final int MAX_VERSION_LENGTH = 200;
|
||||
private static final long serialVersionUID = 1L;
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "myCodeSystem")
|
||||
|
|
|
@ -53,6 +53,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD
|
|||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
|
||||
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
@ -60,7 +61,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD
|
|||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
|
||||
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails);
|
||||
result.throwNotFoundIfAppropriate();
|
||||
return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties);
|
||||
} catch (FHIRException e) {
|
||||
|
@ -84,13 +85,14 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD
|
|||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA,
|
||||
@OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB,
|
||||
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails);
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails);
|
||||
return (Parameters) result.toParameters(theRequestDetails.getFhirContext());
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
|
@ -55,6 +55,7 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<C
|
|||
@OperationParam(name="code", min=0, max=1) CodeType theCode,
|
||||
@OperationParam(name="system", min=0, max=1) UriType theSystem,
|
||||
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
|
||||
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
@ -62,7 +63,7 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<C
|
|||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
|
||||
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails);
|
||||
result.throwNotFoundIfAppropriate();
|
||||
return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties);
|
||||
} finally {
|
||||
|
@ -84,13 +85,14 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<C
|
|||
@OperationParam(name="system", min=0, max=1) UriType theSystem,
|
||||
@OperationParam(name="codingA", min=0, max=1) Coding theCodingA,
|
||||
@OperationParam(name="codingB", min=0, max=1) Coding theCodingB,
|
||||
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails);
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails);
|
||||
return (Parameters) result.toParameters(theRequestDetails.getFhirContext());
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
|
@ -55,6 +55,7 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5<C
|
|||
@OperationParam(name="code", min=0, max=1) CodeType theCode,
|
||||
@OperationParam(name="system", min=0, max=1) UriType theSystem,
|
||||
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
|
||||
@OperationParam(name="version", min=0, max=1) org.hl7.fhir.r4.model.StringType theVersion,
|
||||
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
@ -62,7 +63,7 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5<C
|
|||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
|
||||
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails);
|
||||
result.throwNotFoundIfAppropriate();
|
||||
return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties);
|
||||
} finally {
|
||||
|
@ -84,13 +85,14 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5<C
|
|||
@OperationParam(name="system", min=0, max=1) UriType theSystem,
|
||||
@OperationParam(name="codingA", min=0, max=1) Coding theCodingA,
|
||||
@OperationParam(name="codingB", min=0, max=1) Coding theCodingB,
|
||||
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails);
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails);
|
||||
return (Parameters) result.toParameters(theRequestDetails.getFhirContext());
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
|
@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.term;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
|
@ -126,7 +125,6 @@ import org.hl7.fhir.r4.model.IntegerType;
|
|||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -175,7 +173,6 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
|
|||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||
|
||||
public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||
public static final int DEFAULT_FETCH_SIZE = 250;
|
||||
|
@ -238,14 +235,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
private ITermCodeSystemStorageSvc myConceptStorageSvc;
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private DefaultProfileValidationSupport myDefaultProfileValidationSupport;
|
||||
private volatile IValidationSupport myJpaValidationSupport;
|
||||
private volatile IValidationSupport myValidationSupport;
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
TermCodeSystemVersion cs = getCurrentCodeSystemVersion(theSystem);
|
||||
TermCodeSystemVersion cs = getCurrentCodeSystemVersion(theSystem,null);
|
||||
return cs != null;
|
||||
}
|
||||
|
||||
|
@ -932,19 +927,19 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
case "parent":
|
||||
case "child":
|
||||
isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty());
|
||||
handleFilterLoincParentChild(theQb, theBool, theFilter);
|
||||
handleFilterLoincParentChild(theBool, theFilter);
|
||||
break;
|
||||
case "ancestor":
|
||||
isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty());
|
||||
handleFilterLoincAncestor(theSystem, theQb, theBool, theFilter);
|
||||
handleFilterLoincAncestor(theSystem, theBool, theFilter);
|
||||
break;
|
||||
case "descendant":
|
||||
isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty());
|
||||
handleFilterLoincDescendant(theSystem, theQb, theBool, theFilter);
|
||||
handleFilterLoincDescendant(theSystem, theBool, theFilter);
|
||||
break;
|
||||
case "copyright":
|
||||
isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty());
|
||||
handleFilterLoincCopyright(theQb, theBool, theFilter);
|
||||
handleFilterLoincCopyright(theBool, theFilter);
|
||||
break;
|
||||
default:
|
||||
handleFilterRegex(theBool, theFilter);
|
||||
|
@ -952,11 +947,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) {
|
||||
private void isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) {
|
||||
if (!isCodeSystemLoinc(theSystem)) {
|
||||
throw new InvalidRequestException("Invalid filter, property " + theProperty + " is LOINC-specific and cannot be used with system: " + theSystem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isCodeSystemLoinc(String theSystem) {
|
||||
|
@ -1004,7 +998,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincParentChild(QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
private void handleFilterLoincParentChild(BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
case EQUAL:
|
||||
addLoincFilterParentChildEqual(theBool, theFilter.getProperty(), theFilter.getValue());
|
||||
|
@ -1037,29 +1031,29 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincAncestor(String theSystem, QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
private void handleFilterLoincAncestor(String theSystem, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
case EQUAL:
|
||||
addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter);
|
||||
addLoincFilterAncestorEqual(theSystem, theBool, theFilter);
|
||||
break;
|
||||
case IN:
|
||||
addLoincFilterAncestorIn(theSystem, theQb, theBool, theFilter);
|
||||
addLoincFilterAncestorIn(theSystem, theBool, theFilter);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty());
|
||||
}
|
||||
}
|
||||
|
||||
private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter.getProperty(), theFilter.getValue());
|
||||
private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
addLoincFilterAncestorEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue());
|
||||
}
|
||||
|
||||
private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction<?> theBool, String theProperty, String theValue) {
|
||||
private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction<?> theBool, String theProperty, String theValue) {
|
||||
List<Term> terms = getAncestorTerms(theSystem, theProperty, theValue);
|
||||
theBool.must(new TermsQuery(terms));
|
||||
}
|
||||
|
||||
private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
private void addLoincFilterAncestorIn(String theSystem, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
String[] values = theFilter.getValue().split(",");
|
||||
List<Term> terms = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
|
@ -1081,13 +1075,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
private void handleFilterLoincDescendant(String theSystem, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
case EQUAL:
|
||||
addLoincFilterDescendantEqual(theSystem, theBool, theFilter);
|
||||
break;
|
||||
case IN:
|
||||
addLoincFilterDescendantIn(theSystem, theQb, theBool, theFilter);
|
||||
addLoincFilterDescendantIn(theSystem, theBool, theFilter);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty());
|
||||
|
@ -1103,7 +1097,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
theBool.must(new TermsQuery(terms));
|
||||
}
|
||||
|
||||
private void addLoincFilterDescendantIn(String theSystem, QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
private void addLoincFilterDescendantIn(String theSystem, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
String[] values = theFilter.getValue().split(",");
|
||||
List<Term> terms = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
|
@ -1127,7 +1121,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void handleFilterLoincCopyright(QueryBuilder theQb, BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
private void handleFilterLoincCopyright(BooleanJunction<?> theBool, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) {
|
||||
|
||||
String copyrightFilterValue = defaultString(theFilter.getValue()).toLowerCase();
|
||||
|
@ -1364,6 +1358,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
@Override
|
||||
public Optional<TermConcept> findCode(String theCodeSystem, String theCode) {
|
||||
return findCode(theCodeSystem, theCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TermConcept> findCode(String theCodeSystem, String theCode, String theVersion) {
|
||||
/*
|
||||
* Loading concepts without a transaction causes issues later on some
|
||||
* platforms (e.g. PSQL) so this transactiontemplate is here to make
|
||||
|
@ -1372,21 +1371,29 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY);
|
||||
return txTemplate.execute(t -> {
|
||||
TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem);
|
||||
TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem, theVersion);
|
||||
if (csv == null) {
|
||||
return null;
|
||||
return Optional.empty();
|
||||
}
|
||||
return myConceptDao.findByCodeSystemAndCode(csv, theCode);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri) {
|
||||
TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(theUri, uri -> myTxTemplate.execute(tx -> {
|
||||
private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri, String theVersion) {
|
||||
StringBuilder key = new StringBuilder(theUri);
|
||||
if (theVersion != null) {
|
||||
key.append("_").append(theVersion);
|
||||
}
|
||||
TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), t -> myTxTemplate.execute(tx -> {
|
||||
TermCodeSystemVersion csv = null;
|
||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(uri);
|
||||
if (cs != null && cs.getCurrentVersion() != null) {
|
||||
csv = cs.getCurrentVersion();
|
||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theUri);
|
||||
if (cs != null) {
|
||||
if (theVersion != null) {
|
||||
csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(cs.getPid(), theVersion);
|
||||
} else if (cs.getCurrentVersion() != null) {
|
||||
csv = cs.getCurrentVersion();
|
||||
}
|
||||
}
|
||||
if (csv != null) {
|
||||
return csv;
|
||||
|
@ -1820,20 +1827,30 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
ourLog.info("Done storing TermValueSet[{}] for {}", termValueSet.getId(), theValueSet.getIdElement().toVersionless().getValueAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB,
|
||||
IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) {
|
||||
return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) {
|
||||
VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA);
|
||||
VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB);
|
||||
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType<String> theSystemVersion, String theCodingAVersion, String theCodingBVersion) {
|
||||
VersionIndependentConceptWithSystemVersion conceptA = toConcept(theCodeA, theSystem, theCodingA, theSystemVersion, theCodingAVersion);
|
||||
VersionIndependentConceptWithSystemVersion conceptB = toConcept(theCodeB, theSystem, theCodingB, theSystemVersion, theCodingBVersion);
|
||||
|
||||
if (!StringUtils.equals(conceptA.getSystem(), conceptB.getSystem())) {
|
||||
throw new InvalidRequestException("Unable to test subsumption across different code systems");
|
||||
}
|
||||
|
||||
TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode())
|
||||
if (!StringUtils.equals(conceptA.getCodeSystemVersion(), conceptB.getCodeSystemVersion())) {
|
||||
throw new InvalidRequestException("Unable to test subsumption across different code system versions");
|
||||
}
|
||||
|
||||
TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), conceptA.getCodeSystemVersion())
|
||||
.orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptA));
|
||||
|
||||
TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode())
|
||||
TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), conceptB.getCodeSystemVersion())
|
||||
.orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptB));
|
||||
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
|
@ -1852,10 +1869,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
protected abstract ValueSet toCanonicalValueSet(IBaseResource theValueSet);
|
||||
|
||||
protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode) {
|
||||
protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode, String theVersion) {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
return txTemplate.execute(t -> {
|
||||
Optional<TermConcept> codeOpt = findCode(theSystem, theCode);
|
||||
Optional<TermConcept> codeOpt = findCode(theSystem, theCode, theVersion);
|
||||
if (codeOpt.isPresent()) {
|
||||
TermConcept code = codeOpt.get();
|
||||
|
||||
|
@ -2116,6 +2133,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
while (scrollableResultsIterator.hasNext()) {
|
||||
TermConceptMapGroupElement nextElement = scrollableResultsIterator.next();
|
||||
// TODO: The invocation of the size() below does not seem to be necessary but for some reason, removing it causes tests in TerminologySvcImplR4Test to fail.
|
||||
nextElement.getConceptMapGroupElementTargets().size();
|
||||
myEntityManager.detach(nextElement);
|
||||
|
||||
|
@ -2180,7 +2198,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
|
@ -2223,7 +2240,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
CodeValidationResult retVal = null;
|
||||
CodeValidationResult retVal;
|
||||
if (valueSet != null) {
|
||||
retVal = new InMemoryTerminologyServerValidationSupport(myContext).validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, valueSet);
|
||||
} else {
|
||||
|
@ -2456,14 +2473,31 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
@NotNull
|
||||
private static VersionIndependentConcept toConcept(IPrimitiveType<String> theCodeType, IPrimitiveType<String> theSystemType, IBaseCoding theCodingType) {
|
||||
private VersionIndependentConceptWithSystemVersion toConcept(IPrimitiveType<String> theCodeType, IPrimitiveType<String> theSystemType, IBaseCoding theCodingType, IPrimitiveType<String> theSystemVersionType, String theCodingVersionType) {
|
||||
String code = theCodeType != null ? theCodeType.getValueAsString() : null;
|
||||
String system = theSystemType != null ? theSystemType.getValueAsString() : null;
|
||||
String systemVersion = theSystemVersionType != null ? theSystemVersionType.getValueAsString() : null;
|
||||
if (theCodingType != null) {
|
||||
code = theCodingType.getCode();
|
||||
system = theCodingType.getSystem();
|
||||
systemVersion = theCodingVersionType;
|
||||
}
|
||||
return new VersionIndependentConcept(system, code);
|
||||
return new VersionIndependentConceptWithSystemVersion(system, code, systemVersion);
|
||||
}
|
||||
|
||||
private static class VersionIndependentConceptWithSystemVersion extends VersionIndependentConcept {
|
||||
|
||||
String myCodeSystemVersion;
|
||||
|
||||
public VersionIndependentConceptWithSystemVersion(String theSystem, String theCode, String theSystemVersion) {
|
||||
super(theSystem, theCode);
|
||||
myCodeSystemVersion = theSystemVersion;
|
||||
}
|
||||
|
||||
public String getCodeSystemVersion() {
|
||||
return myCodeSystemVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -231,6 +231,26 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) {
|
||||
// Delete TermCodeSystemVersion
|
||||
ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getCodeSystemVersionId());
|
||||
deleteCodeSystemVersion(theCodeSystemVersion.getPid());
|
||||
|
||||
// Check if the version deleted is the current version. If so, delete TermCodeSystem as well.
|
||||
TermCodeSystem termCodeSystem = theCodeSystemVersion.getCodeSystem();
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
txTemplate.executeWithoutResult(t -> {
|
||||
if (myCodeSystemVersionDao.findByCodeSystemPid(termCodeSystem.getPid()).size() == 0) {
|
||||
ourLog.info(" * Deleting code system {}", termCodeSystem.getPid());
|
||||
deleteCodeSystem(termCodeSystem);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of saved concepts
|
||||
*/
|
||||
|
@ -284,16 +304,19 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
ResourcePersistentId codeSystemResourcePid = getCodeSystemResourcePid(theCodeSystem.getIdElement());
|
||||
|
||||
/*
|
||||
* If this is a not-present codesystem, we don't want to store a new version if one
|
||||
* already exists, since that will wipe out the existing concepts. We do create or update
|
||||
* the TermCodeSystem table though, since that allows the DB to reject changes
|
||||
* that would result in duplicate CodeSysten.url values.
|
||||
* If this is a not-present codesystem and codesystem version already exists, we don't want to
|
||||
* overwrite the existing version since that will wipe out the existing concepts. We do create
|
||||
* or update the TermCodeSystem table though, since that allows the DB to reject changes that would
|
||||
* result in duplicate CodeSystem.url values.
|
||||
*/
|
||||
if (theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
TermCodeSystem codeSystem = myCodeSystemDao.findByCodeSystemUri(theCodeSystem.getUrl());
|
||||
if (codeSystem != null) {
|
||||
getOrCreateTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theResourceEntity);
|
||||
return;
|
||||
TermCodeSystem termCodeSystem = myCodeSystemDao.findByCodeSystemUri(theCodeSystem.getUrl());
|
||||
if (termCodeSystem != null) {
|
||||
TermCodeSystemVersion codeSystemVersion = getExistingTermCodeSystemVersion(termCodeSystem.getPid(), theCodeSystem.getVersion());
|
||||
if (codeSystemVersion != null) {
|
||||
TermCodeSystem myCodeSystemEntity = getOrCreateDistinctTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theCodeSystem.getVersion(), theResourceEntity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,29 +361,33 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied");
|
||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystemUri, "No system URI supplied");
|
||||
|
||||
// Grab the existing versions so we can delete them later
|
||||
List<TermCodeSystemVersion> existing = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourcePid.getIdAsLong());
|
||||
|
||||
/*
|
||||
* For now we always delete old versions. At some point it would be nice to allow configuration to keep old versions.
|
||||
*/
|
||||
|
||||
for (TermCodeSystemVersion next : existing) {
|
||||
ourLog.info("Deleting old code system version {}", next.getPid());
|
||||
Long codeSystemVersionPid = next.getPid();
|
||||
deleteCodeSystemVersion(codeSystemVersionPid);
|
||||
// Grab the existing version so we can delete it
|
||||
TermCodeSystem existingCodeSystem = myCodeSystemDao.findByCodeSystemUri(theSystemUri);
|
||||
TermCodeSystemVersion existing = null;
|
||||
if (existingCodeSystem != null) {
|
||||
existing = getExistingTermCodeSystemVersion(existingCodeSystem.getPid(), theSystemVersionId);
|
||||
}
|
||||
|
||||
ourLog.debug("Flushing...");
|
||||
myConceptDao.flush();
|
||||
ourLog.debug("Done flushing");
|
||||
/*
|
||||
* Get CodeSystem and validate CodeSystemVersion
|
||||
*/
|
||||
TermCodeSystem codeSystem = getOrCreateDistinctTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theSystemVersionId, theCodeSystemResourceTable);
|
||||
|
||||
/*
|
||||
* Delete version being replaced.
|
||||
*/
|
||||
|
||||
if(existing != null) {
|
||||
ourLog.info("Deleting old code system version {}", existing.getPid());
|
||||
Long codeSystemVersionPid = existing.getPid();
|
||||
deleteCodeSystemVersion(codeSystemVersionPid);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the upload
|
||||
*/
|
||||
|
||||
TermCodeSystem codeSystem = getOrCreateTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theCodeSystemResourceTable);
|
||||
|
||||
theCodeSystemVersion.setCodeSystem(codeSystem);
|
||||
|
||||
theCodeSystemVersion.setCodeSystemDisplayName(theSystemName);
|
||||
|
@ -408,6 +435,17 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private TermCodeSystemVersion getExistingTermCodeSystemVersion(Long theCodeSystemVersionPid, String theCodeSystemVersion) {
|
||||
TermCodeSystemVersion existing;
|
||||
if (theCodeSystemVersion == null) {
|
||||
existing = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystemVersionPid);
|
||||
} else {
|
||||
existing = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystemVersionPid, theCodeSystemVersion);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
private void deleteCodeSystemVersion(final Long theCodeSystemVersionPid) {
|
||||
ourLog.info(" * Deleting code system version {}", theCodeSystemVersionPid);
|
||||
|
||||
|
@ -455,10 +493,12 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
ourLog.info(" * Removing code system version {} as current version of code system {}", theCodeSystemVersionPid, codeSystem.getPid());
|
||||
codeSystem.setCurrentVersion(null);
|
||||
myCodeSystemDao.save(codeSystem);
|
||||
myCodeSystemDao.flush();
|
||||
}
|
||||
|
||||
ourLog.info(" * Deleting code system version");
|
||||
myCodeSystemVersionDao.deleteById(theCodeSystemVersionPid);
|
||||
myCodeSystemVersionDao.delete(theCodeSystemVersionPid);
|
||||
myCodeSystemVersionDao.flush();
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -651,28 +691,48 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
}
|
||||
|
||||
@Nonnull
|
||||
private TermCodeSystem getOrCreateTermCodeSystem(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, ResourceTable theCodeSystemResourceTable) {
|
||||
private TermCodeSystem getOrCreateDistinctTermCodeSystem(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) {
|
||||
TermCodeSystem codeSystem = myCodeSystemDao.findByCodeSystemUri(theSystemUri);
|
||||
if (codeSystem == null) {
|
||||
codeSystem = myCodeSystemDao.findByResourcePid(theCodeSystemResourcePid.getIdAsLong());
|
||||
if (codeSystem == null) {
|
||||
codeSystem = new TermCodeSystem();
|
||||
}
|
||||
codeSystem.setResource(theCodeSystemResourceTable);
|
||||
} else {
|
||||
if (!ObjectUtil.equals(codeSystem.getResource().getId(), theCodeSystemResourceTable.getId())) {
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri,
|
||||
codeSystem.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
codeSystem.setResource(theCodeSystemResourceTable);
|
||||
codeSystem.setCodeSystemUri(theSystemUri);
|
||||
codeSystem.setName(theSystemName);
|
||||
codeSystem = myCodeSystemDao.save(codeSystem);
|
||||
checkForCodeSystemVersionDuplicate(codeSystem,theSystemUri, theSystemVersionId, theCodeSystemResourceTable);
|
||||
return codeSystem;
|
||||
}
|
||||
|
||||
private void checkForCodeSystemVersionDuplicate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) {
|
||||
// Check if CodeSystemVersion entity already exists.
|
||||
TermCodeSystemVersion codeSystemVersionEntity;
|
||||
String msg = null;
|
||||
if (theSystemVersionId == null) {
|
||||
codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid());
|
||||
if (codeSystemVersionEntity != null) {
|
||||
msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri,
|
||||
codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
} else {
|
||||
codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId);
|
||||
if (codeSystemVersionEntity != null) {
|
||||
msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri,
|
||||
theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
}
|
||||
// Throw exception if the CodeSystemVersion is being duplicated.
|
||||
if (codeSystemVersionEntity != null) {
|
||||
if (!ObjectUtil.equals(codeSystemVersionEntity.getResource().getId(), theCodeSystemResourceTable.getId())) {
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void populateCodeSystemVersionProperties(TermCodeSystemVersion theCodeSystemVersion, CodeSystem theCodeSystemResource, ResourceTable theResourceTable) {
|
||||
theCodeSystemVersion.setResource(theResourceTable);
|
||||
theCodeSystemVersion.setCodeSystemDisplayName(theCodeSystemResource.getName());
|
||||
|
|
|
@ -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.ITermConceptParentChildLinkDao;
|
||||
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.TermConceptParentChildLink;
|
||||
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
||||
|
@ -68,6 +69,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
|||
protected PlatformTransactionManager myTransactionMgr;
|
||||
private boolean myProcessDeferred = true;
|
||||
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<ValueSet> myDeferredValueSets = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<ConceptMap> myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>());
|
||||
|
@ -115,6 +117,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
|||
myDefferedCodeSystemsDeletions.add(theCodeSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) {
|
||||
myDefferedCodeSystemVersionsDeletions.add(theCodeSystemVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAllDeferred() {
|
||||
while (!isStorageQueueEmpty()) {
|
||||
|
@ -266,6 +274,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
|||
}
|
||||
|
||||
private void processDeferredCodeSystemDeletions() {
|
||||
|
||||
for (TermCodeSystemVersion next : myDefferedCodeSystemVersionsDeletions) {
|
||||
myCodeSystemStorageSvc.deleteCodeSystemVersion(next);
|
||||
}
|
||||
|
||||
myDefferedCodeSystemVersionsDeletions.clear();
|
||||
for (TermCodeSystem next : myDefferedCodeSystemsDeletions) {
|
||||
myCodeSystemStorageSvc.deleteCodeSystem(next);
|
||||
}
|
||||
|
@ -300,7 +314,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
|||
}
|
||||
|
||||
private boolean isDeferredCodeSystemDeletions() {
|
||||
return !myDefferedCodeSystemsDeletions.isEmpty();
|
||||
return !myDefferedCodeSystemsDeletions.isEmpty() || !myDefferedCodeSystemVersionsDeletions.isEmpty();
|
||||
}
|
||||
|
||||
private boolean isDeferredConcepts() {
|
||||
|
|
|
@ -378,6 +378,10 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
|
|||
try {
|
||||
String loincCsString = IOUtils.toString(BaseTermReadSvcImpl.class.getResourceAsStream("/ca/uhn/fhir/jpa/term/loinc/loinc.xml"), Charsets.UTF_8);
|
||||
loincCs = FhirContext.forR4().newXmlParser().parseResource(CodeSystem.class, loincCsString);
|
||||
String codeSystemVersionId = theUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode());
|
||||
if (codeSystemVersionId != null) {
|
||||
loincCs.setVersion(codeSystemVersionId);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException("Failed to load loinc.xml", e);
|
||||
}
|
||||
|
|
|
@ -149,7 +149,12 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
return super.lookupCode(theSystem, theCode);
|
||||
return super.lookupCode(theSystem, theCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
return super.lookupCode(theSystem, theCode, theVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -89,9 +89,14 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
|||
return (CodeSystem) theCodeSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
return super.lookupCode(theSystem, theCode, theVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
return super.lookupCode(theSystem, theCode);
|
||||
return super.lookupCode(theSystem, theCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -41,6 +41,8 @@ public interface ITermCodeSystemStorageSvc {
|
|||
|
||||
void deleteCodeSystem(TermCodeSystem theCodeSystem);
|
||||
|
||||
void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion);
|
||||
|
||||
void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable);
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term.api;
|
|||
*/
|
||||
|
||||
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.TermConceptParentChildLink;
|
||||
import org.hl7.fhir.r4.model.ConceptMap;
|
||||
|
@ -54,6 +55,8 @@ public interface ITermDeferredStorageSvc {
|
|||
|
||||
void deleteCodeSystem(TermCodeSystem theCodeSystem);
|
||||
|
||||
void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion);
|
||||
|
||||
/**
|
||||
* This is mostly here for unit tests - Saves any and all deferred concepts and links
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,8 @@ public interface ITermReadSvc extends IValidationSupport {
|
|||
|
||||
List<VersionIndependentConcept> expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet);
|
||||
|
||||
Optional<TermConcept> findCode(String theCodeSystem, String theCode, String theVersion);
|
||||
|
||||
Optional<TermConcept> findCode(String theCodeSystem, String theCode);
|
||||
|
||||
Set<TermConcept> findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode);
|
||||
|
@ -103,6 +105,8 @@ public interface ITermReadSvc extends IValidationSupport {
|
|||
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB);
|
||||
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType<String> theSystemVersion, String theCodingAVersion, String theCodingBVersion);
|
||||
|
||||
void preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
/**
|
||||
|
|
|
@ -96,6 +96,9 @@ public enum LoincUploadPropertiesEnum {
|
|||
/*
|
||||
* OPTIONAL
|
||||
*/
|
||||
// This is the version identifier for the answer list file
|
||||
LOINC_CODESYSTEM_VERSION("loinc.codesystem.version"),
|
||||
|
||||
// This is the version identifier for the answer list file
|
||||
LOINC_ANSWERLIST_VERSION("loinc.answerlist.version"),
|
||||
|
||||
|
|
|
@ -61,6 +61,10 @@ loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersVa
|
|||
### OPTIONAL ###
|
||||
################
|
||||
|
||||
# This is the version identifier for the LOINC code system
|
||||
## Key may be omitted if only a single version of LOINC is being kept.
|
||||
loinc.codesystem.version=2.68
|
||||
|
||||
# This is the version identifier for the answer list file
|
||||
## Key may be omitted
|
||||
loinc.answerlist.version=Beta.1
|
||||
|
|
|
@ -521,7 +521,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
|
||||
IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM),null, mySrd);
|
||||
IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM), null,null, mySrd);
|
||||
assertEquals(true, lookupResults.isFound());
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
|
@ -711,7 +711,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
|
||||
StringType code = new StringType("ParentA");
|
||||
StringType system = new StringType("http://snomed.info/sct");
|
||||
IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
|
||||
IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null,null, mySrd);
|
||||
assertEquals(true, outcome.isFound());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -8,11 +9,14 @@ import org.junit.jupiter.api.AfterAll;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test {
|
||||
|
||||
|
@ -35,15 +39,7 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test {
|
|||
@Test
|
||||
public void testDeleteLargeCompleteCodeSystem() {
|
||||
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.setUrl("http://foo");
|
||||
for (int i = 0; i < 222; i++) {
|
||||
cs.addConcept().setCode("CODE" + i);
|
||||
}
|
||||
IIdType id = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless();
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
IIdType id = createLargeCodeSystem(null);
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
|
@ -72,6 +68,122 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCodeSystemVersion() {
|
||||
|
||||
// Create code system with two versions.
|
||||
IIdType id_first = createLargeCodeSystem("1");
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo"));
|
||||
assertEquals(1, myTermCodeSystemVersionDao.count());
|
||||
List<ResourceTable> resourceList = myResourceTableDao.findAll();
|
||||
assertEquals(222, myTermConceptDao.count());
|
||||
assertEquals(1, resourceList.size());
|
||||
assertNull(resourceList.get(0).getDeleted());
|
||||
});
|
||||
|
||||
IIdType id_second = createLargeCodeSystem("2");
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo"));
|
||||
assertEquals(2, myTermCodeSystemVersionDao.count());
|
||||
assertEquals(444, myTermConceptDao.count());
|
||||
List<ResourceTable> resourceList = myResourceTableDao.findAll();
|
||||
assertEquals(2, resourceList.size());
|
||||
long active = resourceList
|
||||
.stream()
|
||||
.filter(t -> t.getDeleted() == null).count();
|
||||
assertEquals(2, active);
|
||||
});
|
||||
|
||||
// Attempt to delete first version
|
||||
myCodeSystemDao.delete(id_first, mySrd);
|
||||
|
||||
// Only the resource will be deleted initially
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo"));
|
||||
assertEquals(2, myTermCodeSystemVersionDao.count());
|
||||
assertEquals(444, myTermConceptDao.count());
|
||||
List<ResourceTable> resourceList = myResourceTableDao.findAll();
|
||||
assertEquals(2, resourceList.size());
|
||||
long active = resourceList
|
||||
.stream()
|
||||
.filter(t -> t.getDeleted() == null).count();
|
||||
assertEquals(1, active);
|
||||
});
|
||||
|
||||
// Now the background scheduler will do its thing
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
|
||||
// Entities for first resource should be gone now.
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo"));
|
||||
assertEquals(1, myTermCodeSystemVersionDao.count());
|
||||
assertEquals(222, myTermConceptDao.count());
|
||||
List<ResourceTable> resourceList = myResourceTableDao.findAll();
|
||||
assertEquals(2, resourceList.size());
|
||||
long active = resourceList
|
||||
.stream()
|
||||
.filter(t -> t.getDeleted() == null).count();
|
||||
assertEquals(1, active);
|
||||
});
|
||||
|
||||
// Attempt to delete second version
|
||||
myCodeSystemDao.delete(id_second, mySrd);
|
||||
|
||||
// Only the resource will be deleted initially
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo"));
|
||||
assertEquals(1, myTermCodeSystemVersionDao.count());
|
||||
assertEquals(222, myTermConceptDao.count());
|
||||
List<ResourceTable> resourceList = myResourceTableDao.findAll();
|
||||
assertEquals(2, resourceList.size());
|
||||
long active = resourceList
|
||||
.stream()
|
||||
.filter(t -> t.getDeleted() == null).count();
|
||||
assertEquals(0, active);
|
||||
});
|
||||
|
||||
// Now the background scheduler will do its thing
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
|
||||
// The remaining versions and Code System entities should be gone now.
|
||||
runInTransaction(() -> {
|
||||
assertEquals(0, myTermCodeSystemDao.count());
|
||||
assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo"));
|
||||
assertEquals(0, myTermCodeSystemVersionDao.count());
|
||||
List<ResourceTable> resourceList = myResourceTableDao.findAll();
|
||||
assertEquals(2, resourceList.size());
|
||||
long active = resourceList
|
||||
.stream()
|
||||
.filter(t -> t.getDeleted() == null).count();
|
||||
assertEquals(0, active);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private IIdType createLargeCodeSystem(String theVersion) {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.setUrl("http://foo");
|
||||
if (theVersion != null) {
|
||||
cs.setVersion(theVersion);
|
||||
}
|
||||
for (int i = 0; i < 222; i++) {
|
||||
cs.addConcept().setCode("CODE" + i);
|
||||
}
|
||||
IIdType id = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless();
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
return id;
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() {
|
||||
TermReindexingSvcImpl.setForceSaveDeferredAlwaysForUnitTest(false);
|
||||
|
|
|
@ -828,7 +828,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
|
|||
|
||||
StringType code = new StringType("ParentA");
|
||||
StringType system = new StringType("http://snomed.info/sct");
|
||||
IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
|
||||
IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null,null, mySrd);
|
||||
assertEquals(true, outcome.isFound());
|
||||
}
|
||||
|
||||
|
|
|
@ -325,6 +325,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("FOO"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Unknown code: [http://parentchild|FOO]", e.getMessage());
|
||||
}
|
||||
|
@ -340,6 +341,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("FOO"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Unknown code: [http://parentchild|FOO]", e.getMessage());
|
||||
}
|
||||
|
@ -355,6 +357,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD + "A").setCode("ChildAA"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD + "B").setCode("ParentA"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Unable to test subsumption across different code systems", e.getMessage());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,769 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final String SYSTEM_PARENTCHILD = "http://parentchild";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemVersionedTest.class);
|
||||
|
||||
@BeforeEach
|
||||
@Transactional
|
||||
public void before02() throws IOException {
|
||||
CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v1.xml");
|
||||
myCodeSystemDao.create(cs, mySrd);
|
||||
|
||||
cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v2.xml");
|
||||
myCodeSystemDao.create(cs, mySrd);
|
||||
|
||||
ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
|
||||
myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
CodeSystem parentChildCs = new CodeSystem();
|
||||
parentChildCs.setUrl(SYSTEM_PARENTCHILD);
|
||||
parentChildCs.setVersion("1");
|
||||
parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA);
|
||||
|
||||
CodeSystem.ConceptDefinitionComponent parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A");
|
||||
parentA.addConcept().setCode("ChildAA").setDisplay("Child AA");
|
||||
parentA.addConcept().setCode("ParentC").setDisplay("Parent C");
|
||||
parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B");
|
||||
|
||||
myCodeSystemDao.create(parentChildCs);
|
||||
|
||||
parentChildCs = new CodeSystem();
|
||||
parentChildCs.setVersion("2");
|
||||
parentChildCs.setUrl(SYSTEM_PARENTCHILD);
|
||||
parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA);
|
||||
|
||||
parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A v2");
|
||||
parentA.addConcept().setCode("ChildAA").setDisplay("Child AA v2");
|
||||
parentA.addConcept().setCode("ParentB").setDisplay("Parent B v2");
|
||||
parentChildCs.addConcept().setCode("ParentC").setDisplay("Parent C v2");
|
||||
|
||||
myCodeSystemDao.create(parentChildCs);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupOnExternalCodeMultiVersion() {
|
||||
ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "1");
|
||||
ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "2");
|
||||
|
||||
// First test with no version specified (should return from last version created)
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// With HTTP GET
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals(("2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Test with version 1 specified.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
|
||||
.andParameter("version", new StringType("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Parent A1", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// With HTTP GET
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
|
||||
.andParameter("version", new StringType("1"))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals(("1"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Parent A1", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Test with version 2 specified.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
|
||||
.andParameter("version", new StringType("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// With HTTP GET
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
|
||||
.andParameter("version", new StringType("2"))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals(("2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupOperationByCodeAndSystemBuiltInCode() {
|
||||
// First test with no version specified (should return the one and only version defined).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ACSN"))
|
||||
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Repeat with version specified.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ACSN"))
|
||||
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203"))
|
||||
.andParameter("version", new StringType("2.9"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupOperationByCodeAndSystemBuiltInNonexistentVersion() {
|
||||
try {
|
||||
myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("ACSN"))
|
||||
.andParameter("system", new UriType("http://hl7.org/fhir/v2/0203"))
|
||||
.andParameter("version", new StringType("2.8"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
ourLog.info("Lookup failed as expected");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupOperationByCodeAndSystemUserDefinedCode() {
|
||||
// First test with no version specified (should return from last version created)
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
|
||||
.andParameter("system", new UriType("http://acme.org"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Test with version 1 specified.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
|
||||
.andParameter("system", new UriType("http://acme.org"))
|
||||
.andParameter("version", new StringType("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Test with version 2 specified
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
|
||||
.andParameter("system", new UriType("http://acme.org"))
|
||||
.andParameter("version", new StringType("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupOperationByCodeAndSystemUserDefinedNonExistentVersion() {
|
||||
try {
|
||||
myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
|
||||
.andParameter("system", new UriType("http://acme.org"))
|
||||
.andParameter("version", new StringType("3"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
ourLog.info("Lookup failed as expected");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupOperationByCoding() {
|
||||
// First test with no version specified (should return from last version created)
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named("lookup")
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("name", respParam.getParameter().get(0).getName());
|
||||
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
assertEquals("version", respParam.getParameter().get(1).getName());
|
||||
assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals("abstract", respParam.getParameter().get(3).getName());
|
||||
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodes_Subsumes() {
|
||||
// First test with no version specified (should return result for last version created).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentB"))
|
||||
.andParameter("codeB", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentC"))
|
||||
.andParameter("codeB", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.andParameter("version", new StringType("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentB"))
|
||||
.andParameter("codeB", new CodeType("ParentA"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.andParameter("version", new StringType("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodes_Subsumedby() {
|
||||
// First test with no version specified (should return result for last version created).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
|
||||
.andParameter("codeB", new CodeType("ParentB"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
|
||||
.andParameter("codeB", new CodeType("ParentC"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.andParameter("version", new StringType("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
|
||||
.andParameter("codeB", new CodeType("ParentB"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.andParameter("version", new StringType("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodes_Disjoint() {
|
||||
// First test with no version specified (should return result for last version created).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
|
||||
.andParameter("codeB", new CodeType("ParentC"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
|
||||
.andParameter("codeB", new CodeType("ParentB"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.andParameter("version", new StringType("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
|
||||
.andParameter("codeB", new CodeType("ParentC"))
|
||||
.andParameter("system", new UriType(SYSTEM_PARENTCHILD))
|
||||
.andParameter("version", new StringType("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodings_MismatchedCsVersions() {
|
||||
try {
|
||||
myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA").setVersion("1"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Unable to test subsumption across different code system versions", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodings_Subsumes() {
|
||||
// First test with no version specified (should return result for last version created).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodings_Subsumedby() {
|
||||
// First test with no version specified (should return result for last version created).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2.
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumesOnCodings_Disjoint() {
|
||||
// First test with no version specified (should return result for last version created).
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 1
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("1"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
// Test with version set to 2
|
||||
respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_SUBSUMES)
|
||||
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
|
||||
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("2"))
|
||||
.execute();
|
||||
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(1, respParam.getParameter().size());
|
||||
assertEquals("outcome", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@ import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -1089,4 +1090,38 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
|
|||
return codeSystem;
|
||||
}
|
||||
|
||||
public static CodeSystem createExternalCs(IFhirResourceDao<CodeSystem> theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
|
||||
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
|
||||
codeSystem.setVersion(theCodeSystemVersion);
|
||||
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
|
||||
|
||||
ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new);
|
||||
|
||||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
|
||||
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A" + theCodeSystemVersion);
|
||||
cs.getConcepts().add(parentA);
|
||||
|
||||
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA" + theCodeSystemVersion);
|
||||
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA" + theCodeSystemVersion);
|
||||
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB" + theCodeSystemVersion);
|
||||
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB" + theCodeSystemVersion);
|
||||
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B" + theCodeSystemVersion);
|
||||
cs.getConcepts().add(parentB);
|
||||
|
||||
theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", theCodeSystemVersion, cs, table);
|
||||
return codeSystem;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
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.rest.server.exceptions.UnprocessableEntityException;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
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;
|
||||
|
||||
public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test {
|
||||
|
@ -13,39 +19,115 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test {
|
|||
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
|
||||
|
||||
@Test
|
||||
public void testStoreNewCodeSystemVersionForExistingCodeSystem() {
|
||||
CodeSystem upload = createCodeSystemWithMoreThan100Concepts();
|
||||
public void testStoreNewCodeSystemVersionForExistingCodeSystemNoVersionId() {
|
||||
CodeSystem firstUpload = createCodeSystemWithMoreThan100Concepts();
|
||||
CodeSystem duplicateUpload = createCodeSystemWithMoreThan100Concepts();
|
||||
|
||||
// Create CodeSystem resource
|
||||
ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(upload, mySrd).getEntity();
|
||||
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/");
|
||||
|
||||
// Update the CodeSystem resource
|
||||
runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity));
|
||||
runInTransaction(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
assertEquals(1, myTermCodeSystemVersionDao.count());
|
||||
TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM);
|
||||
|
||||
/*
|
||||
Because there are more than 100 concepts in the code system, the first 100 will be persisted immediately and
|
||||
the remaining 25 concepts will be queued up for "deferred save".
|
||||
TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidVersionIsNull( myTermCodeSystem.getPid());
|
||||
assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid());
|
||||
assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStoreNewCodeSystemVersionForExistingCodeSystemVersionId() {
|
||||
CodeSystem firstUpload = createCodeSystemWithMoreThan100Concepts();
|
||||
firstUpload.setVersion("1");
|
||||
|
||||
CodeSystem duplicateUpload = createCodeSystemWithMoreThan100Concepts();
|
||||
duplicateUpload.setVersion("1");
|
||||
|
||||
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(() -> {
|
||||
assertEquals(1, myTermCodeSystemDao.count());
|
||||
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());
|
||||
});
|
||||
|
||||
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() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
|
||||
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
|
||||
for (int i = 0; i < 125; 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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -343,6 +343,37 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
|
|||
assertEquals("13006-2", vs.getCompose().getInclude().get(0).getConcept().get(1).getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadLoincMultipleVersions() throws IOException {
|
||||
|
||||
// Load LOINC marked as version 2.67
|
||||
addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v267_loincupload.properties");
|
||||
mySvc.loadLoinc(myFiles.getFiles(), mySrd);
|
||||
|
||||
verify(myTermCodeSystemStorageSvc, times(1)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture());
|
||||
CodeSystem loincCS = mySystemCaptor.getValue();
|
||||
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
|
||||
myFiles = new ZipCollectionBuilder();
|
||||
addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v268_loincupload.properties");
|
||||
mySvc.loadLoinc(myFiles.getFiles(), mySrd);
|
||||
|
||||
verify(myTermCodeSystemStorageSvc, times(3)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture());
|
||||
loincCS = mySystemCaptor.getValue();
|
||||
assertEquals("2.68", loincCS.getVersion());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testLoadLoincMandatoryFilesOnly() throws IOException {
|
||||
|
@ -383,7 +414,11 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
|
|||
}
|
||||
|
||||
public static void addLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles) throws IOException {
|
||||
theFiles.addFileZip("/loinc/", LOINC_UPLOAD_PROPERTIES_FILE.getCode());
|
||||
addLoincMandatoryFilesWithPropertiesFileToZip(theFiles, LOINC_UPLOAD_PROPERTIES_FILE.getCode());
|
||||
}
|
||||
|
||||
public static void addLoincMandatoryFilesWithPropertiesFileToZip(ZipCollectionBuilder theFiles, String thePropertiesFile) throws IOException {
|
||||
theFiles.addFileZip("/loinc/", thePropertiesFile);
|
||||
theFiles.addFileZip("/loinc/", LOINC_GROUP_FILE_DEFAULT.getCode());
|
||||
theFiles.addFileZip("/loinc/", LOINC_GROUP_TERMS_FILE_DEFAULT.getCode());
|
||||
theFiles.addFileZip("/loinc/", LOINC_PARENT_GROUP_FILE_DEFAULT.getCode());
|
||||
|
|
|
@ -184,7 +184,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
|
|||
assertEquals(2, outcome.getUpdatedConceptCount());
|
||||
|
||||
runInTransaction(() -> {
|
||||
TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA").orElseThrow(() -> new IllegalStateException());
|
||||
TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA", null).orElseThrow(() -> new IllegalStateException());
|
||||
assertEquals(2, concept.getParents().size());
|
||||
assertThat(concept.getParentPidsAsString(), matchesPattern("^[0-9]+ [0-9]+$"));
|
||||
});
|
||||
|
|
|
@ -4,6 +4,9 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
|||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
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.TermConceptMap;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||
|
@ -21,7 +24,6 @@ import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
|||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -32,9 +34,14 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
|||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -1802,10 +1809,10 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
|
|||
createCodeSystem();
|
||||
|
||||
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);
|
||||
assertEquals(false, validation.isOk());
|
||||
assertFalse(validation.isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1904,4 +1911,109 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
|
|||
assertTrue(result.isOk());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCodeSystemTwoVersions() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
codeSystem
|
||||
.addConcept().setCode("A").setDisplay("Code A");
|
||||
codeSystem
|
||||
.addConcept().setCode("B").setDisplay("Code A");
|
||||
|
||||
codeSystem.setVersion("1");
|
||||
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
|
||||
Set<TermConcept> codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A");
|
||||
assertThat(toCodes(codes), containsInAnyOrder("A"));
|
||||
|
||||
codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "B");
|
||||
assertThat(toCodes(codes), containsInAnyOrder("B"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
List<TermCodeSystemVersion> termCodeSystemVersions = myTermCodeSystemVersionDao.findAll();
|
||||
assertEquals(termCodeSystemVersions.size(), 1);
|
||||
TermCodeSystemVersion termCodeSystemVersion_1 = termCodeSystemVersions.get(0);
|
||||
assertEquals(termCodeSystemVersion_1.getConcepts().size(), 2);
|
||||
Set<TermConcept> termConcepts = new HashSet<>(termCodeSystemVersion_1.getConcepts());
|
||||
assertThat(toCodes(termConcepts), containsInAnyOrder("A", "B"));
|
||||
|
||||
TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong());
|
||||
assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId());
|
||||
|
||||
});
|
||||
|
||||
codeSystem.setVersion("2");
|
||||
codeSystem
|
||||
.addConcept().setCode("C").setDisplay("Code C");
|
||||
|
||||
IIdType id_v2 = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
codes = myTermSvc.findCodesBelow(id_v2.getIdPartAsLong(), id_v2.getVersionIdPartAsLong(), "C");
|
||||
assertThat(toCodes(codes), containsInAnyOrder("C"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
List<TermCodeSystemVersion> termCodeSystemVersions_updated = myTermCodeSystemVersionDao.findAll();
|
||||
assertEquals(termCodeSystemVersions_updated.size(), 2);
|
||||
TermCodeSystemVersion termCodeSystemVersion_2 = termCodeSystemVersions_updated.get(1);
|
||||
assertEquals(termCodeSystemVersion_2.getConcepts().size(), 3);
|
||||
Set<TermConcept> termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts());
|
||||
assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B", "C"));
|
||||
|
||||
TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id_v2.getIdPartAsLong());
|
||||
assertEquals("2", termCodeSystem.getCurrentVersion().getCodeSystemVersionId());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCodeSystemContentModeNotPresent() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
codeSystem
|
||||
.addConcept().setCode("A").setDisplay("Code A");
|
||||
codeSystem
|
||||
.addConcept().setCode("B").setDisplay("Code A");
|
||||
|
||||
codeSystem.setVersion("1");
|
||||
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
|
||||
Set<TermConcept> codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A");
|
||||
assertThat(toCodes(codes), containsInAnyOrder("A"));
|
||||
|
||||
codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "B");
|
||||
assertThat(toCodes(codes), containsInAnyOrder("B"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
List<TermCodeSystemVersion> termCodeSystemVersions = myTermCodeSystemVersionDao.findAll();
|
||||
assertEquals(termCodeSystemVersions.size(), 1);
|
||||
TermCodeSystemVersion termCodeSystemVersion_1 = termCodeSystemVersions.get(0);
|
||||
assertEquals(termCodeSystemVersion_1.getConcepts().size(), 2);
|
||||
Set<TermConcept> termConcepts = new HashSet<>(termCodeSystemVersion_1.getConcepts());
|
||||
assertThat(toCodes(termConcepts), containsInAnyOrder("A", "B"));
|
||||
|
||||
TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong());
|
||||
assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId());
|
||||
|
||||
});
|
||||
|
||||
// Remove concepts and changed ContentMode to NOTPRESENT
|
||||
codeSystem.setConcept(new ArrayList<>());
|
||||
codeSystem.setContent((CodeSystem.CodeSystemContentMode.NOTPRESENT));
|
||||
|
||||
myCodeSystemDao.update(codeSystem, mySrd).getId().toUnqualified();
|
||||
runInTransaction(() -> {
|
||||
List<TermCodeSystemVersion> termCodeSystemVersions_updated = myTermCodeSystemVersionDao.findAll();
|
||||
assertEquals(termCodeSystemVersions_updated.size(), 1);
|
||||
TermCodeSystemVersion termCodeSystemVersion_2 = termCodeSystemVersions_updated.get(0);
|
||||
assertEquals(termCodeSystemVersion_2.getConcepts().size(), 2);
|
||||
Set<TermConcept> termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts());
|
||||
assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B"));
|
||||
|
||||
TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong());
|
||||
assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
<CodeSystem xmlns="http://hl7.org/fhir">
|
||||
<url value="http://acme.org" />
|
||||
<version value="1"/>
|
||||
<name value="ACME Codes" />
|
||||
<concept>
|
||||
<code value="8450-9" />
|
||||
<display value="Systolic blood pressure--expiration" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="11378-7" />
|
||||
<display value="Systolic blood pressure at First encounter" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8493-9" />
|
||||
<display value="Systolic blood pressure 10 hour minimum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8494-7" />
|
||||
<display value="Systolic blood pressure 12 hour minimum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8495-4" />
|
||||
<display value="Systolic blood pressure 24 hour minimum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8451-7" />
|
||||
<display value="Systolic blood pressure--inspiration" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8452-5" />
|
||||
<display value="Systolic blood pressure.inspiration - expiration" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8459-0" />
|
||||
<display value="Systolic blood pressure--sitting" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8460-8" />
|
||||
<display value="Systolic blood pressure--standing" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8461-6" />
|
||||
<display value="Systolic blood pressure--supine" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8479-8" />
|
||||
<display value="Systolic blood pressure by palpation" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8480-6" />
|
||||
<display value="Systolic blood pressure" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8481-4" />
|
||||
<display value="Systolic blood pressure 1 hour maximum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8482-2" />
|
||||
<display value="Systolic blood pressure 8 hour maximum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8483-0" />
|
||||
<display value="Systolic blood pressure 10 hour maximum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8484-8" />
|
||||
<display value="Systolic blood pressure 12 hour maximum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8485-5" />
|
||||
<display value="Systolic blood pressure 24 hour maximum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8486-3" />
|
||||
<display value="Systolic blood pressure 1 hour mean" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8487-1" />
|
||||
<display value="Systolic blood pressure 8 hour mean" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8488-9" />
|
||||
<display value="Systolic blood pressure 10 hour mean" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8489-7" />
|
||||
<display value="Systolic blood pressure 12 hour mean" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8490-5" />
|
||||
<display value="Systolic blood pressure 24 hour mean" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8491-3" />
|
||||
<display value="Systolic blood pressure 1 hour minimum" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8492-1" />
|
||||
<display value="Systolic blood pressure 8 hour minimum" />
|
||||
</concept>
|
||||
</CodeSystem>
|
|
@ -0,0 +1,101 @@
|
|||
<CodeSystem xmlns="http://hl7.org/fhir">
|
||||
<url value="http://acme.org" />
|
||||
<version value="2"/>
|
||||
<name value="ACME Codes" />
|
||||
<concept>
|
||||
<code value="8450-9" />
|
||||
<display value="Systolic blood pressure--expiration v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="11378-7" />
|
||||
<display value="Systolic blood pressure at First encounter v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8493-9" />
|
||||
<display value="Systolic blood pressure 10 hour minimum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8494-7" />
|
||||
<display value="Systolic blood pressure 12 hour minimum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8495-4" />
|
||||
<display value="Systolic blood pressure 24 hour minimum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8451-7" />
|
||||
<display value="Systolic blood pressure--inspiration v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8452-5" />
|
||||
<display value="Systolic blood pressure.inspiration - expiration v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8459-0" />
|
||||
<display value="Systolic blood pressure--sitting v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8460-8" />
|
||||
<display value="Systolic blood pressure--standing v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8461-6" />
|
||||
<display value="Systolic blood pressure--supine v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8479-8" />
|
||||
<display value="Systolic blood pressure by palpation v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8480-6" />
|
||||
<display value="Systolic blood pressure v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8481-4" />
|
||||
<display value="Systolic blood pressure 1 hour maximum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8482-2" />
|
||||
<display value="Systolic blood pressure 8 hour maximum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8483-0" />
|
||||
<display value="Systolic blood pressure 10 hour maximum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8484-8" />
|
||||
<display value="Systolic blood pressure 12 hour maximum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8485-5" />
|
||||
<display value="Systolic blood pressure 24 hour maximum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8486-3" />
|
||||
<display value="Systolic blood pressure 1 hour mean v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8487-1" />
|
||||
<display value="Systolic blood pressure 8 hour mean v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8488-9" />
|
||||
<display value="Systolic blood pressure 10 hour mean v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8489-7" />
|
||||
<display value="Systolic blood pressure 12 hour mean v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8490-5" />
|
||||
<display value="Systolic blood pressure 24 hour mean v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8491-3" />
|
||||
<display value="Systolic blood pressure 1 hour minimum v2" />
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="8492-1" />
|
||||
<display value="Systolic blood pressure 8 hour minimum v2" />
|
||||
</concept>
|
||||
</CodeSystem>
|
|
@ -0,0 +1,87 @@
|
|||
#################
|
||||
### MANDATORY ###
|
||||
#################
|
||||
|
||||
# Answer lists (ValueSets of potential answers/values for LOINC "questions")
|
||||
## File must be present
|
||||
loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv
|
||||
# Answer list links (connects LOINC observation codes to answer list codes)
|
||||
## File must be present
|
||||
loinc.answerlist.link.file=AccessoryFiles/AnswerFile/LoincAnswerListLink.csv
|
||||
|
||||
# Document ontology
|
||||
## File must be present
|
||||
loinc.document.ontology.file=AccessoryFiles/DocumentOntology/DocumentOntology.csv
|
||||
|
||||
# LOINC codes
|
||||
## File must be present
|
||||
loinc.file=LoincTable/Loinc.csv
|
||||
|
||||
# LOINC hierarchy
|
||||
## File must be present
|
||||
loinc.hierarchy.file=AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv
|
||||
|
||||
# IEEE medical device codes
|
||||
## File must be present
|
||||
loinc.ieee.medical.device.code.mapping.table.file=AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv
|
||||
|
||||
# Imaging document codes
|
||||
## File must be present
|
||||
loinc.imaging.document.codes.file=AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv
|
||||
|
||||
# Part
|
||||
## File must be present
|
||||
loinc.part.file=AccessoryFiles/PartFile/Part.csv
|
||||
|
||||
# Part link
|
||||
## File must be present
|
||||
loinc.part.link.primary.file=AccessoryFiles/PartFile/LoincPartLink_Primary.csv
|
||||
loinc.part.link.supplementary.file=AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv
|
||||
|
||||
# Part related code mapping
|
||||
## File must be present
|
||||
loinc.part.related.code.mapping.file=AccessoryFiles/PartFile/PartRelatedCodeMapping.csv
|
||||
|
||||
# RSNA playbook
|
||||
## File must be present
|
||||
loinc.rsna.playbook.file=AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv
|
||||
|
||||
# Top 2000 codes - SI
|
||||
## File must be present
|
||||
loinc.top2000.common.lab.results.si.file=AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv
|
||||
# Top 2000 codes - US
|
||||
## File must be present
|
||||
loinc.top2000.common.lab.results.us.file=AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv
|
||||
|
||||
# Universal lab order ValueSet
|
||||
## File must be present
|
||||
loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv
|
||||
|
||||
################
|
||||
### OPTIONAL ###
|
||||
################
|
||||
|
||||
# This is the version identifier for the LOINC code system
|
||||
## Key may be omitted if only a single version of LOINC is being kept.
|
||||
loinc.codesystem.version=2.67
|
||||
|
||||
# This is the version identifier for the answer list file
|
||||
## Key may be omitted
|
||||
loinc.answerlist.version=Beta.1
|
||||
|
||||
# This is the version identifier for uploaded ConceptMap resources
|
||||
## Key may be omitted
|
||||
loinc.conceptmap.version=Beta.1
|
||||
|
||||
# Group
|
||||
## Default value if key not provided: AccessoryFiles/GroupFile/Group.csv
|
||||
## File may be omitted
|
||||
loinc.group.file=AccessoryFiles/GroupFile/Group.csv
|
||||
# Group terms
|
||||
## Default value if key not provided: AccessoryFiles/GroupFile/GroupLoincTerms.csv
|
||||
## File may be omitted
|
||||
loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
|
||||
# Parent group
|
||||
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
|
||||
## File may be omitted
|
||||
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv
|
|
@ -0,0 +1,87 @@
|
|||
#################
|
||||
### MANDATORY ###
|
||||
#################
|
||||
|
||||
# This is the version identifier for the LOINC code system
|
||||
## Key may be omitted if only a single version of LOINC is being kept.
|
||||
loinc.codesystem.version=2.68
|
||||
|
||||
# Answer lists (ValueSets of potential answers/values for LOINC "questions")
|
||||
## File must be present
|
||||
loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv
|
||||
# Answer list links (connects LOINC observation codes to answer list codes)
|
||||
## File must be present
|
||||
loinc.answerlist.link.file=AccessoryFiles/AnswerFile/LoincAnswerListLink.csv
|
||||
|
||||
# Document ontology
|
||||
## File must be present
|
||||
loinc.document.ontology.file=AccessoryFiles/DocumentOntology/DocumentOntology.csv
|
||||
|
||||
# LOINC codes
|
||||
## File must be present
|
||||
loinc.file=LoincTable/Loinc.csv
|
||||
|
||||
# LOINC hierarchy
|
||||
## File must be present
|
||||
loinc.hierarchy.file=AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv
|
||||
|
||||
# IEEE medical device codes
|
||||
## File must be present
|
||||
loinc.ieee.medical.device.code.mapping.table.file=AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv
|
||||
|
||||
# Imaging document codes
|
||||
## File must be present
|
||||
loinc.imaging.document.codes.file=AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv
|
||||
|
||||
# Part
|
||||
## File must be present
|
||||
loinc.part.file=AccessoryFiles/PartFile/Part.csv
|
||||
|
||||
# Part link
|
||||
## File must be present
|
||||
loinc.part.link.primary.file=AccessoryFiles/PartFile/LoincPartLink_Primary.csv
|
||||
loinc.part.link.supplementary.file=AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv
|
||||
|
||||
# Part related code mapping
|
||||
## File must be present
|
||||
loinc.part.related.code.mapping.file=AccessoryFiles/PartFile/PartRelatedCodeMapping.csv
|
||||
|
||||
# RSNA playbook
|
||||
## File must be present
|
||||
loinc.rsna.playbook.file=AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv
|
||||
|
||||
# Top 2000 codes - SI
|
||||
## File must be present
|
||||
loinc.top2000.common.lab.results.si.file=AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv
|
||||
# Top 2000 codes - US
|
||||
## File must be present
|
||||
loinc.top2000.common.lab.results.us.file=AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv
|
||||
|
||||
# Universal lab order ValueSet
|
||||
## File must be present
|
||||
loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv
|
||||
|
||||
################
|
||||
### OPTIONAL ###
|
||||
################
|
||||
|
||||
# This is the version identifier for the answer list file
|
||||
## Key may be omitted
|
||||
loinc.answerlist.version=Beta.1
|
||||
|
||||
# This is the version identifier for uploaded ConceptMap resources
|
||||
## Key may be omitted
|
||||
loinc.conceptmap.version=Beta.1
|
||||
|
||||
# Group
|
||||
## Default value if key not provided: AccessoryFiles/GroupFile/Group.csv
|
||||
## File may be omitted
|
||||
loinc.group.file=AccessoryFiles/GroupFile/Group.csv
|
||||
# Group terms
|
||||
## Default value if key not provided: AccessoryFiles/GroupFile/GroupLoincTerms.csv
|
||||
## File may be omitted
|
||||
loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
|
||||
# Parent group
|
||||
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
|
||||
## File may be omitted
|
||||
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv
|
|
@ -58,6 +58,11 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
return myWrap.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode);
|
||||
|
|
|
@ -83,6 +83,12 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
return loadFromCache(myValidateCodeCache, key, t -> super.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
String key = "lookupCode " + theSystem + " " + theCode + " " + theVersion;
|
||||
return loadFromCache(myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
String key = "lookupCode " + theSystem + " " + theCode;
|
||||
|
|
|
@ -145,6 +145,12 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
// For now Common Code Systems will not be versioned.
|
||||
return lookupCode(theValidationSupportContext, theSystem, theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
|
||||
|
|
|
@ -308,6 +308,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
// TODO: Add support for validating versioned codes as well.
|
||||
return lookupCode(theValidationSupportContext, theSystem, theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
return validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode);
|
||||
|
@ -470,6 +476,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
|
||||
List<VersionIndependentConcept> nextCodeList = new ArrayList<>();
|
||||
String system = nextInclude.getSystem();
|
||||
String systemVersion = nextInclude.getVersion();
|
||||
if (isNotBlank(system)) {
|
||||
|
||||
if (theWantSystem != null && !theWantSystem.equals(system)) {
|
||||
|
@ -492,7 +499,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
|
||||
if (theWantCode != null) {
|
||||
if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, system)) {
|
||||
LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode);
|
||||
LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode, systemVersion);
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
|
|
|
@ -257,6 +257,16 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) {
|
||||
return next.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
|
|
|
@ -254,7 +254,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
when(mockSupport.lookupCode(any(), any(), any())).thenAnswer(t -> {
|
||||
when(mockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> {
|
||||
String system = t.getArgument(1, String.class);
|
||||
String code = t.getArgument(2, String.class);
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
|
|
Loading…
Reference in New Issue