Merge pull request #1455 from jamesagnew/1431-validate-code-operation-needs-to-be-optimized-for-large-valuesets
Resolve "$validate-code operation needs to be optimized for large ValueSets."
This commit is contained in:
commit
5650bc9baa
|
@ -41,7 +41,7 @@ public interface IFhirResourceDaoValueSet<T extends IBaseResource, CD, CC> exten
|
||||||
|
|
||||||
ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails);
|
ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
public class ValidateCodeResult {
|
class ValidateCodeResult {
|
||||||
private String myDisplay;
|
private String myDisplay;
|
||||||
private String myMessage;
|
private String myMessage;
|
||||||
private boolean myResult;
|
private boolean myResult;
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ITermValueSetConceptDao extends JpaRepository<TermValueSetConcept, Long> {
|
public interface ITermValueSetConceptDao extends JpaRepository<TermValueSetConcept, Long> {
|
||||||
|
@ -45,4 +46,9 @@ public interface ITermValueSetConceptDao extends JpaRepository<TermValueSetConce
|
||||||
@Query("SELECT vsc FROM TermValueSetConcept vsc WHERE vsc.myValueSet.myId = :pid AND vsc.mySystem = :system_url AND vsc.myCode = :codeval")
|
@Query("SELECT vsc FROM TermValueSetConcept vsc WHERE vsc.myValueSet.myId = :pid AND vsc.mySystem = :system_url AND vsc.myCode = :codeval")
|
||||||
Optional<TermValueSetConcept> findByTermValueSetIdSystemAndCode(@Param("pid") Long theValueSetId, @Param("system_url") String theSystem, @Param("codeval") String theCode);
|
Optional<TermValueSetConcept> findByTermValueSetIdSystemAndCode(@Param("pid") Long theValueSetId, @Param("system_url") String theSystem, @Param("codeval") String theCode);
|
||||||
|
|
||||||
|
@Query("SELECT vsc FROM TermValueSetConcept vsc WHERE vsc.myValueSet.myResourcePid = :resource_pid AND vsc.myCode = :codeval")
|
||||||
|
List<TermValueSetConcept> findOneByValueSetIdAndCode(@Param("resource_pid") Long theValueSetId, @Param("codeval") String theCode);
|
||||||
|
|
||||||
|
@Query("SELECT vsc FROM TermValueSetConcept vsc WHERE vsc.myValueSet.myResourcePid = :resource_pid AND vsc.mySystem = :system_url AND vsc.myCode = :codeval")
|
||||||
|
List<TermValueSetConcept> findOneByValueSetIdSystemAndCode(@Param("resource_pid") Long theValueSetId, @Param("system_url") String theSystem, @Param("codeval") String theCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,9 +330,14 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vs != null) {
|
if (vs != null) {
|
||||||
ValueSet expansion = doExpand(vs); // TODO: DM 2019-08-17 - Need to account for concepts in terminology tables. See #1431
|
ValidateCodeResult result;
|
||||||
|
if (myDaoConfig.isPreExpandValueSetsExperimental()) {
|
||||||
|
result = myTerminologySvc.validateCodeIsInPreExpandedValueSet(vs, toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
} else {
|
||||||
|
ValueSet expansion = doExpand(vs);
|
||||||
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
|
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
|
||||||
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
|
result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
|
||||||
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
|
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
|
||||||
if (!theDisplay.getValue().equals(result.getDisplay())) {
|
if (!theDisplay.getValue().equals(result.getDisplay())) {
|
||||||
|
|
|
@ -286,15 +286,15 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
|
public ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
|
||||||
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
|
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
|
||||||
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
|
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
List<IIdType> valueSetIds = Collections.emptyList();
|
List<IIdType> valueSetIds = Collections.emptyList();
|
||||||
|
|
||||||
boolean haveCodeableConcept = theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0;
|
boolean haveCodeableConcept = theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0;
|
||||||
boolean haveCoding = theCoding != null && theCoding.isEmpty() == false;
|
boolean haveCoding = theCoding != null && !theCoding.isEmpty();
|
||||||
boolean haveCode = theCode != null && theCode.isEmpty() == false;
|
boolean haveCode = theCode != null && !theCode.isEmpty();
|
||||||
|
|
||||||
if (!haveCodeableConcept && !haveCoding && !haveCode) {
|
if (!haveCodeableConcept && !haveCoding && !haveCode) {
|
||||||
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate");
|
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate");
|
||||||
|
@ -303,7 +303,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
|
||||||
throw new InvalidRequestException("$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)");
|
throw new InvalidRequestException("$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean haveIdentifierParam = theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false;
|
boolean haveIdentifierParam = theValueSetIdentifier != null && !theValueSetIdentifier.isEmpty();
|
||||||
ValueSet vs = null;
|
ValueSet vs = null;
|
||||||
if (theId != null) {
|
if (theId != null) {
|
||||||
vs = read(theId, theRequestDetails);
|
vs = read(theId, theRequestDetails);
|
||||||
|
@ -320,15 +320,20 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
|
||||||
// String system = toStringOrNull(theSystem);
|
// String system = toStringOrNull(theSystem);
|
||||||
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
|
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
|
||||||
if (result.isFound()) {
|
if (result.isFound()) {
|
||||||
ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay());
|
ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vs != null) {
|
if (vs != null) {
|
||||||
ValueSet expansion = doExpand(vs); // TODO: DM 2019-08-17 - Need to account for concepts in terminology tables. See #1431
|
ValidateCodeResult result;
|
||||||
|
if (myDaoConfig.isPreExpandValueSetsExperimental()) {
|
||||||
|
result = myTerminologySvc.validateCodeIsInPreExpandedValueSet(vs, toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
} else {
|
||||||
|
ValueSet expansion = doExpand(vs);
|
||||||
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
|
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
|
||||||
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
|
result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
|
||||||
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
|
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
|
||||||
if (!theDisplay.getValue().equals(result.getDisplay())) {
|
if (!theDisplay.getValue().equals(result.getDisplay())) {
|
||||||
|
@ -347,10 +352,10 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
|
||||||
return thePrimitive != null ? thePrimitive.getValue() : null;
|
return thePrimitive != null ? thePrimitive.getValue() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode,
|
private ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode,
|
||||||
Coding theCoding, CodeableConcept theCodeableConcept) {
|
Coding theCoding, CodeableConcept theCodeableConcept) {
|
||||||
for (ValueSetExpansionContainsComponent nextCode : contains) {
|
for (ValueSetExpansionContainsComponent nextCode : contains) {
|
||||||
ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept);
|
ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,9 +332,14 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5<ValueSet> imple
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vs != null) {
|
if (vs != null) {
|
||||||
ValueSet expansion = doExpand(vs); // TODO: DM 2019-08-17 - Need to account for concepts in terminology tables. See #1431
|
ValidateCodeResult result;
|
||||||
|
if (myDaoConfig.isPreExpandValueSetsExperimental()) {
|
||||||
|
result = myTerminologySvc.validateCodeIsInPreExpandedValueSet(vs, toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
} else {
|
||||||
|
ValueSet expansion = doExpand(vs);
|
||||||
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
|
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
|
||||||
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
|
result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
|
||||||
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
|
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
|
||||||
if (!theDisplay.getValue().equals(result.getDisplay())) {
|
if (!theDisplay.getValue().equals(result.getDisplay())) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.term;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||||
import ca.uhn.fhir.jpa.dao.*;
|
import ca.uhn.fhir.jpa.dao.*;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.dao.data.*;
|
import ca.uhn.fhir.jpa.dao.data.*;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||||
|
@ -959,6 +960,48 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ValidateCodeResult validateCodeIsInPreExpandedValueSet(
|
||||||
|
ValueSet theValueSet, String theSystem, String theCode, String theDisplay, Coding theCoding, CodeableConcept theCodeableConcept) {
|
||||||
|
|
||||||
|
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet.hasId(), "ValueSet.id is required");
|
||||||
|
|
||||||
|
Long valueSetId = theValueSet.getIdElement().toUnqualifiedVersionless().getIdPartAsLong();
|
||||||
|
|
||||||
|
List<TermValueSetConcept> concepts = new ArrayList<>();
|
||||||
|
if (isNotBlank(theCode)) {
|
||||||
|
if (isNotBlank(theSystem)) {
|
||||||
|
concepts = myValueSetConceptDao.findOneByValueSetIdSystemAndCode(valueSetId, theSystem, theCode);
|
||||||
|
} else {
|
||||||
|
concepts = myValueSetConceptDao.findOneByValueSetIdAndCode(valueSetId, theCode);
|
||||||
|
}
|
||||||
|
} else if (theCoding != null) {
|
||||||
|
if (theCoding.hasSystem() && theCoding.hasCode()) {
|
||||||
|
concepts = myValueSetConceptDao.findOneByValueSetIdSystemAndCode(valueSetId, theCoding.getSystem(), theCoding.getCode());
|
||||||
|
}
|
||||||
|
} else if (theCodeableConcept != null){
|
||||||
|
for (Coding coding : theCodeableConcept.getCoding()) {
|
||||||
|
if (coding.hasSystem() && coding.hasCode()) {
|
||||||
|
concepts = myValueSetConceptDao.findOneByValueSetIdSystemAndCode(valueSetId, coding.getSystem(), coding.getCode());
|
||||||
|
if (!concepts.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TermValueSetConcept concept : concepts) {
|
||||||
|
if (isNotBlank(theDisplay) && theDisplay.equals(concept.getDisplay())) {
|
||||||
|
return new ValidateCodeResult(true, "Validation succeeded", concept.getDisplay());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!concepts.isEmpty()) {
|
||||||
|
return new ValidateCodeResult(true, "Validation succeeded", concepts.get(0).getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
|
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
|
||||||
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
|
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
|
||||||
TermConcept nextChild = nextChildLink.getChild();
|
TermConcept nextChild = nextChildLink.getChild();
|
||||||
|
|
|
@ -20,13 +20,13 @@ package ca.uhn.fhir.jpa.term;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -149,4 +149,8 @@ public class HapiTerminologySvcDstu2 extends BaseHapiTerminologySvcImpl {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.term;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -15,6 +16,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
|
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||||
|
@ -29,7 +31,8 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -359,5 +362,26 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||||
|
ValueSet valueSet = (ValueSet) theValueSet;
|
||||||
|
Coding coding = (Coding) theCoding;
|
||||||
|
CodeableConcept codeableConcept = (CodeableConcept) theCodeableConcept;
|
||||||
|
|
||||||
|
try {
|
||||||
|
org.hl7.fhir.r4.model.ValueSet valueSetR4;
|
||||||
|
valueSetR4 = VersionConvertor_30_40.convertValueSet(valueSet);
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.Coding codingR4 = new org.hl7.fhir.r4.model.Coding(coding.getSystem(), coding.getCode(), coding.getDisplay());
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.CodeableConcept codeableConceptR4 = new org.hl7.fhir.r4.model.CodeableConcept();
|
||||||
|
for (Coding nestedCoding : codeableConcept.getCoding()) {
|
||||||
|
codeableConceptR4.addCoding(new org.hl7.fhir.r4.model.Coding(nestedCoding.getSystem(), nestedCoding.getCode(), nestedCoding.getDisplay()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.validateCodeIsInPreExpandedValueSet(valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConceptR4);
|
||||||
|
} catch (FHIRException e) {
|
||||||
|
throw new InternalErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,17 @@ package ca.uhn.fhir.jpa.term;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
|
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
|
||||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
|
||||||
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
|
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
|
||||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||||
|
@ -278,4 +277,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
|
||||||
return super.lookupCode(theContext, theSystem, theCode);
|
return super.lookupCode(theContext, theSystem, theCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||||
|
ValueSet valueSet = (ValueSet) theValueSet;
|
||||||
|
Coding coding = (Coding) theCoding;
|
||||||
|
CodeableConcept codeableConcept = (CodeableConcept) theCodeableConcept;
|
||||||
|
return super.validateCodeIsInPreExpandedValueSet(valueSet, theSystem, theCode, theDisplay, coding, codeableConcept);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@ package ca.uhn.fhir.jpa.term;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
|
||||||
import org.hl7.fhir.r5.model.*;
|
import org.hl7.fhir.r5.model.*;
|
||||||
|
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
||||||
|
@ -287,4 +289,19 @@ public class HapiTerminologySvcR5 extends BaseHapiTerminologySvcImpl implements
|
||||||
return super.lookupCode(theContext, theSystem, theCode);
|
return super.lookupCode(theContext, theSystem, theCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||||
|
org.hl7.fhir.r4.model.ValueSet valueSetR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet((ValueSet) theValueSet);
|
||||||
|
|
||||||
|
Coding coding = (Coding) theCoding;
|
||||||
|
org.hl7.fhir.r4.model.Coding codingR4 = new org.hl7.fhir.r4.model.Coding(coding.getSystem(), coding.getCode(), coding.getDisplay());
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept = (CodeableConcept) theCodeableConcept;
|
||||||
|
org.hl7.fhir.r4.model.CodeableConcept codeableConceptR4 = new org.hl7.fhir.r4.model.CodeableConcept();
|
||||||
|
for (Coding nestedCoding : codeableConcept.getCoding()) {
|
||||||
|
codeableConceptR4.addCoding(new org.hl7.fhir.r4.model.Coding(nestedCoding.getSystem(), nestedCoding.getCode(), nestedCoding.getDisplay()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.validateCodeIsInPreExpandedValueSet(valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConceptR4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
package ca.uhn.fhir.jpa.term;
|
package ca.uhn.fhir.jpa.term;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -116,4 +112,9 @@ public interface IHapiTerminologySvc {
|
||||||
AtomicInteger applyDeltaCodesystemsRemove(String theSystem, CodeSystem theDelta);
|
AtomicInteger applyDeltaCodesystemsRemove(String theSystem, CodeSystem theDelta);
|
||||||
|
|
||||||
void preExpandValueSetToTerminologyTables();
|
void preExpandValueSetToTerminologyTables();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version independent
|
||||||
|
*/
|
||||||
|
ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
@ -13,7 +14,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||||
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
||||||
|
@ -29,7 +30,6 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -2607,13 +2607,63 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
|
||||||
public void testValidateCode() {
|
public void testValidateCode() {
|
||||||
createCodeSystem();
|
createCodeSystem();
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ParentWithNoChildrenA", null);
|
CodeValidationResult validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ParentWithNoChildrenA", null);
|
||||||
assertEquals(true, validation.isOk());
|
assertEquals(true, validation.isOk());
|
||||||
|
|
||||||
validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ZZZZZZZ", null);
|
validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ZZZZZZZ", null);
|
||||||
assertEquals(false, validation.isOk());
|
assertEquals(false, validation.isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeIsInPreExpandedValueSet() throws Exception {
|
||||||
|
myDaoConfig.setPreExpandValueSetsExperimental(true);
|
||||||
|
|
||||||
|
loadAndPersistCodeSystemAndValueSetWithDesignations();
|
||||||
|
|
||||||
|
CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId);
|
||||||
|
ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem));
|
||||||
|
|
||||||
|
ValueSet valueSet = myValueSetDao.read(myExtensionalVsId);
|
||||||
|
ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet));
|
||||||
|
|
||||||
|
myTermSvc.preExpandValueSetToTerminologyTables();
|
||||||
|
|
||||||
|
ValidateCodeResult result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, null, null, null, null, null);
|
||||||
|
assertNull(result);
|
||||||
|
|
||||||
|
result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, null, "BOGUS", null, null, null);
|
||||||
|
assertNull(result);
|
||||||
|
|
||||||
|
result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, null, "11378-7", null, null, null);
|
||||||
|
assertTrue(result.isResult());
|
||||||
|
assertEquals("Validation succeeded", result.getMessage());
|
||||||
|
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||||
|
|
||||||
|
result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, null, "11378-7", "Systolic blood pressure at First encounter", null, null);
|
||||||
|
assertTrue(result.isResult());
|
||||||
|
assertEquals("Validation succeeded", result.getMessage());
|
||||||
|
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||||
|
|
||||||
|
result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, "http://acme.org", "11378-7", null, null, null);
|
||||||
|
assertTrue(result.isResult());
|
||||||
|
assertEquals("Validation succeeded", result.getMessage());
|
||||||
|
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||||
|
|
||||||
|
Coding coding = new Coding("http://acme.org", "11378-7", "Systolic blood pressure at First encounter");
|
||||||
|
result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, null, null, null, coding, null);
|
||||||
|
assertTrue(result.isResult());
|
||||||
|
assertEquals("Validation succeeded", result.getMessage());
|
||||||
|
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
codeableConcept.addCoding(new Coding("BOGUS", "BOGUS", "BOGUS"));
|
||||||
|
codeableConcept.addCoding(coding);
|
||||||
|
result = myTermSvc.validateCodeIsInPreExpandedValueSet(valueSet, null, null, null, null, codeableConcept);
|
||||||
|
assertTrue(result.isResult());
|
||||||
|
assertEquals("Validation succeeded", result.getMessage());
|
||||||
|
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClassClearContext() {
|
public static void afterClassClearContext() {
|
||||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
|
Loading…
Reference in New Issue