From 64f2bae2e7ed279df7bcfe7eb6ac2909a27e613c Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Fri, 18 Sep 2020 22:30:59 -0400 Subject: [PATCH 01/11] Impl CodeSystem $validate-code for R4 with basic test cases --- .../api/dao/IFhirResourceDaoCodeSystem.java | 2 + .../jpa/dao/FhirResourceDaoValueSetDstu2.java | 11 + .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 11 + .../dao/r4/FhirResourceDaoCodeSystemR4.java | 11 + .../dao/r5/FhirResourceDaoCodeSystemR5.java | 9 + .../BaseJpaResourceProviderCodeSystemR4.java | 59 ++- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 294 ++++++++---- .../uhn/fhir/jpa/term/api/ITermReadSvc.java | 5 + ...rceProviderR4CodeSystemValidationTest.java | 433 ++++++++++++++++++ .../CommonCodeSystemsTerminologyService.java | 26 ++ 10 files changed, 762 insertions(+), 99 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index 2c00c792b49..a963f82c718 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -47,6 +47,8 @@ public interface IFhirResourceDaoCodeSystem ext SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails); + IValidationSupport.CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails); + class SubsumesResult { private final ConceptSubsumptionOutcome myOutcome; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index 12cbd8fa2f5..e946f626b5b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; @@ -56,6 +57,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static ca.uhn.fhir.jpa.util.LogicUtil.multiXor; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -312,4 +314,13 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { + + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index c42f4125ce1..32d770229ae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; @@ -51,6 +52,8 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; @@ -169,4 +172,12 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 996eba93af4..577f16478ba 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; @@ -50,6 +51,8 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -164,5 +167,13 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index c9bd9125be4..e3fee2f13d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.r5; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.index.IdHelperService; @@ -51,6 +52,8 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -166,5 +169,11 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 9b78c83991b..94e94a10b05 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -1,5 +1,19 @@ package ca.uhn.fhir.jpa.provider.r4; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +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.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; + /* * #%L * HAPI FHIR JPA Server @@ -23,20 +37,11 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; -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.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.UriType; - -import javax.servlet.http.HttpServletRequest; -import java.util.List; public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 { @@ -98,4 +103,36 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); + + IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); + } finally { + endRequest(theServletRequest); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index a70fba614e8..a1709f9a366 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1,5 +1,99 @@ package ca.uhn.fhir.jpa.term; +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 java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.PersistenceContextType; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Fetch; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.lucene.index.Term; +import org.apache.lucene.queries.TermsQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.RegexpQuery; +import org.apache.lucene.search.TermQuery; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; +import org.hibernate.search.jpa.FullTextEntityManager; +import org.hibernate.search.jpa.FullTextQuery; +import org.hibernate.search.query.dsl.BooleanJunction; +import org.hibernate.search.query.dsl.QueryBuilder; +import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; +import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.IdType; +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.quartz.JobExecutionContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; +import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; +import org.springframework.transaction.support.TransactionTemplate; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Stopwatch; + /* * #%L * HAPI FHIR JPA Server @@ -85,94 +179,6 @@ import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.VersionIndependentConcept; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Stopwatch; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.commons.lang3.time.DateUtils; -import org.apache.lucene.index.Term; -import org.apache.lucene.queries.TermsQuery; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.RegexpQuery; -import org.apache.lucene.search.TermQuery; -import org.hibernate.ScrollMode; -import org.hibernate.ScrollableResults; -import org.hibernate.search.jpa.FullTextEntityManager; -import org.hibernate.search.jpa.FullTextQuery; -import org.hibernate.search.query.dsl.BooleanJunction; -import org.hibernate.search.query.dsl.QueryBuilder; -import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; -import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.CanonicalType; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ConceptMap; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.IdType; -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.quartz.JobExecutionContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; -import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; -import org.springframework.transaction.support.TransactionTemplate; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.validation.constraints.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -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; public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public static final int DEFAULT_FETCH_SIZE = 250; @@ -2555,5 +2561,117 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return ourLastResultsFromTranslationWithReverseCache; } + @Override + public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { + CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); + boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0; + + Coding coding = toCanonicalCoding(theCoding); + boolean haveCoding = coding != null && coding.isEmpty() == false; + + boolean haveCode = theCode != null && theCode.isEmpty() == false; + + if (!haveCodeableConcept && !haveCoding && !haveCode) { + throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate."); + } + if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) { + throw new InvalidRequestException("$validate-code can only validate (code) OR (coding) OR (codeableConcept)"); + } + + boolean haveIdentifierParam = isNotBlank(theCodeSystemUrl); + String codeSystemUrl; + if (theCodeSystemId != null) { + IBaseResource codeSystem = myDaoRegistry.getResourceDao("CodeSystem").read(theCodeSystemId); + codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem); + } else if (haveIdentifierParam) { + codeSystemUrl = theCodeSystemUrl; + } else { + throw new InvalidRequestException("Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate."); + } + + + String code = theCode; + String version = theVersion; + String display = theDisplay; + + if (haveCodeableConcept) { + for (int i = 0; i < codeableConcept.getCoding().size(); i++) { + Coding nextCoding = codeableConcept.getCoding().get(i); + if (nextCoding.hasSystem()) { + if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) { + throw new InvalidRequestException("Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate."); + } + codeSystemUrl = nextCoding.getSystem(); + } + code = nextCoding.getCode(); + display = nextCoding.getDisplay(); + CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, version, code, display); + if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) { + return nextValidation; + } + } + } else if (haveCoding) { + if (coding.hasSystem()) { + if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) { + throw new InvalidRequestException("Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate."); + } + codeSystemUrl = coding.getSystem(); + } + code = coding.getCode(); + display = coding.getDisplay(); + } + + return codeSystemValidateCode(codeSystemUrl, version, code, display); + } + + + @SuppressWarnings("unchecked") + private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theCodeSystemVersion, String theCode, String theDisplay) { + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TermConcept.class); + Root root = query.from(TermConcept.class); + + Fetch systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER); + Join systemVersionJoin = (Join)systemVersionFetch; + Fetch systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER); + Join systemJoin = (Join)systemFetch; + + ArrayList predicates = new ArrayList<>(); + + if (isNotBlank(theCode)) { + predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode)); + } + + if (isNotBlank(theDisplay)) { + predicates.add(criteriaBuilder.equal(root.get("myDisplay"), theDisplay)); + } + + if (isNoneBlank(theCodeSystemUrl)) { + predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl)); + } + + if (isNoneBlank(theCodeSystemVersion)) { + predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion)); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + final TypedQuery typedQuery = myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + List resultsList = hibernateQuery.getResultList(); + + if (!resultsList.isEmpty()) { + TermConcept concept = resultsList.get(0); + return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay()); + } + + if (isBlank(theDisplay)) + return createFailureCodeValidationResult(theCodeSystemUrl, theCode); + else + return createFailureCodeValidationResult(theCodeSystemUrl, theCode, " - Concept Display : " + theDisplay); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java index 124cf2d6450..765ff1a43dc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java @@ -126,4 +126,9 @@ public interface ITermReadSvc extends IValidationSupport { */ boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet); + /** + * Version independent + */ + CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theValueSetUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept); + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java new file mode 100644 index 00000000000..5616f517f36 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -0,0 +1,433 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; + +import javax.annotation.Nonnull; + +import org.hl7.fhir.instance.model.api.IIdType; +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.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; + +public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProviderR4Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemValidationTest.class); + + private IIdType myCsId; + private static final String CS_ACMS_URL = "http://acme.org"; + + @BeforeEach + @Transactional + public void before02() throws IOException { + loadAndPersistCodeSystem(); + } + + private void loadAndPersistCodeSystem() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + codeSystem.setId("CodeSystem/cs"); + persistCodeSystem(codeSystem); + } + + private void persistCodeSystem(CodeSystem theCodeSystem) { + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myCsId = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myCsId, null).getId(); + } + + @Test + public void testValidateCodeFoundByCode() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCode() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5-a")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeMatchDisplay() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeNotMatchDisplay() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + inParams.addParameter().setName("display").setValue(new CodeType("Old Systolic blood pressure.inspiration - expiration")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5 - Concept Display : Old Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeWithoutUrl() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodeWithId() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + + Parameters respParam = myClient.operation().onInstance(myCsId).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeWithoutCodeOrCodingOrCodeableConcept() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeWithCodeAndCoding() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-1"))); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $validate-code can only validate (code) OR (coding) OR (codeableConcept)",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodingWithUrlNotMatch() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem("http://url2"))); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCoding() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5"))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodingWithSystem() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem(CS_ACMS_URL))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodingUrlNotMatch() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem("http://url2"))); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodingWithDisplay() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setDisplay("Systolic blood pressure.inspiration - expiration"))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCoding() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5-a"))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConcept() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithSystem() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithDisplay() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCodeableConcept() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5-a"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeableConceptUrlNotMatch() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem("http://url2").setDisplay("Systolic blood pressure.inspiration - expiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedEntries() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedFirstEntry() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7-a").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedSecondEntry() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5-a").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure--inspiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index 51dfe5dd86b..cfea4d2ac89 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -332,6 +332,32 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { return url; } + public static String getCodeSystemUrl(@Nonnull IBaseResource theCodeSystem) { + String url; + switch (theCodeSystem.getStructureFhirVersionEnum()) { + case DSTU2_HL7ORG: { + url = ((CodeSystem) theCodeSystem).getUrl(); + break; + } + case DSTU3: { + url = ((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem).getUrl(); + break; + } + case R4: { + url = ((org.hl7.fhir.r4.model.CodeSystem) theCodeSystem).getUrl(); + break; + } + case R5: { + url = ((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem).getUrl(); + break; + } + case DSTU2: + case DSTU2_1: + default: + throw new IllegalArgumentException("Can not handle version: " + theCodeSystem.getStructureFhirVersionEnum()); + } + return url; + } private static HashMap buildUspsCodes() { HashMap uspsCodes = new HashMap<>(); uspsCodes.put("AK", "Alaska"); From 05de0c503124bd20bfe7081838d533ef68fb8dc5 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 12:36:14 -0400 Subject: [PATCH 02/11] Added more test cases for R4 --- .../BaseJpaResourceProviderCodeSystemR4.java | 2 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 7 +- ...rceProviderR4CodeSystemValidationTest.java | 120 ++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 94e94a10b05..f0f13171899 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -118,7 +118,7 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 Date: Sat, 19 Sep 2020 12:52:18 -0400 Subject: [PATCH 03/11] Changed fetch size to 1 for CodeSystem $validate-code --- .../java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 4e8ba4def3c..6ebc0315f97 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -31,12 +31,11 @@ import javax.persistence.PersistenceContextType; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Fetch; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import javax.persistence.criteria.Fetch; -import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; @@ -182,6 +181,7 @@ import ca.uhn.fhir.util.VersionIndependentConcept; public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public static final int DEFAULT_FETCH_SIZE = 250; + private static final int SINGLE_FETCH_SIZE = 1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTermReadSvcImpl.class); private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions(); private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L); @@ -2663,16 +2663,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { final TypedQuery typedQuery = myEntityManager.createQuery(query.select(root)); org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery; - hibernateQuery.setFetchSize(myFetchSize); + hibernateQuery.setFetchSize(SINGLE_FETCH_SIZE); List resultsList = hibernateQuery.getResultList(); if (!resultsList.isEmpty()) { TermConcept concept = resultsList.get(0); return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay()); } - - for (TermConcept c :resultsList) - System.out.println(c); if (isBlank(theDisplay)) return createFailureCodeValidationResult(theCodeSystemUrl, theCode); From 01d88a665d4db8ff3d8a4abe9b65edeb5f373aae Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 14:13:07 -0400 Subject: [PATCH 04/11] Added CodeSystem/$validate-code for R5 --- .../BaseJpaResourceProviderCodeSystemR4.java | 2 +- .../BaseJpaResourceProviderCodeSystemR5.java | 36 ++++++++++++ ...rceProviderR4CodeSystemValidationTest.java | 6 +- .../r5/ResourceProviderR5CodeSystemTest.java | 58 +++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index f0f13171899..37a8445a412 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -117,8 +117,8 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); + + IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); + } finally { + endRequest(theServletRequest); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java index 6278a6db55a..e92450d6dd2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -94,7 +94,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); - inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + inParams.addParameter().setName("display").setValue(new StringType("Systolic blood pressure.inspiration - expiration")); Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); @@ -112,7 +112,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); - inParams.addParameter().setName("display").setValue(new CodeType("Old Systolic blood pressure.inspiration - expiration")); + inParams.addParameter().setName("display").setValue(new StringType("Old Systolic blood pressure.inspiration - expiration")); Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); @@ -156,7 +156,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); - inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + inParams.addParameter().setName("display").setValue(new StringType("Systolic blood pressure.inspiration - expiration")); try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java new file mode 100644 index 00000000000..2606db1b248 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5CodeSystemTest.class); + + @Test + public void testValidateCodeWithUrlAndVersion_v1() { + + String url = "http://url"; + createCodeSystem(url, "v1", "1", "Code v1 display"); + createCodeSystem(url, "v2", "1", "Code v2 display"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("version").setValue(new StringType("v1")); + inParams.addParameter().setName("code").setValue(new CodeType("1")); + inParams.addParameter().setName("display").setValue(new StringType("Code v1 display")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam)); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Code v1 display", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + private void createCodeSystem(String url, String version, String code, String display) { + + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(url).setVersion(version); + + ConceptDefinitionComponent concept1 = codeSystem.addConcept(); + concept1.setCode("1000").setDisplay("Code Dispaly 1000"); + + ConceptDefinitionComponent concept = codeSystem.addConcept(); + concept.setCode(code).setDisplay(display); + + ConceptDefinitionComponent concept2 = codeSystem.addConcept(); + concept2.setCode("2000").setDisplay("Code Dispaly 2000"); + + ourLog.info("CodeSystem: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); + + myCodeSystemDao.create(codeSystem, mySrd); + } +} From a3a032584bc8c083591f48a4176e94e56aa61860 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 15:18:28 -0400 Subject: [PATCH 05/11] Made Transactional --- .../src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 6ebc0315f97..5c4615f5e56 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -2562,6 +2562,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override + @Transactional public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); From 8f692a4776001ffce2b2ac2779bf106fd4f90775 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 22:54:39 -0400 Subject: [PATCH 06/11] CodeSystem/$validate-code not supported for DSTU2, DSTU3 --- .../uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java | 9 +++------ .../jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index e946f626b5b..c15b8784b25 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java @@ -314,13 +314,10 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCodeSystemUrl, - IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, - CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { - - return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, + IPrimitiveType theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 32d770229ae..29200610dae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -173,11 +173,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeSystemUrl, - IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, - Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { - - return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, + IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); } } From 673274a966580af4f02ad902514bc912bd973816 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:18:18 -0400 Subject: [PATCH 07/11] Added invalid test cases for CodeSystem/$validate-code R3 --- .../ResourceProviderDstu3CodeSystemTest.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index 132bdd8fe15..8dc57145104 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -6,11 +6,9 @@ import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.transaction.annotation.Transactional; @@ -332,6 +330,19 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst return ourCtx.newJsonParser().parseResource(theType, loadResource(theFilename)); } + @Test + public void testValidateCodeOperation() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType("https://url")); + inParams.addParameter().setName("code").setValue(new CodeType("1")); + try { + ourClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Invalid request: The FHIR endpoint on this server does not know how to handle POST operation[CodeSystem/$validate-code] with parameters [[]]", e.getMessage()); + } + } } From 20661c7a2b906e67b17585fa9a2d5b72086520bf Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:19:43 -0400 Subject: [PATCH 08/11] Removed unused import --- .../uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 29200610dae..eccfdac0ffc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -52,8 +52,6 @@ import java.util.Date; import java.util.List; import java.util.Set; -import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; -import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; From b6d86dc5f2ef3f819e5c7c7b09b0cde5629260ac Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:39:20 -0400 Subject: [PATCH 09/11] Added fail(); in the invalid test cases --- .../r4/ResourceProviderR4CodeSystemValidationTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java index e92450d6dd2..c4d705303fc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.provider.r4; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; @@ -131,6 +132,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.",e.getMessage()); } @@ -160,6 +162,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate.",e.getMessage()); } @@ -175,6 +178,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: $validate-code can only validate (code) OR (coding) OR (codeableConcept)",e.getMessage()); } @@ -189,6 +193,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); } @@ -237,6 +242,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); } @@ -367,6 +373,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); } From e6a669444d4278b98fde5c30a9ece182eba19af0 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:51:20 -0400 Subject: [PATCH 10/11] Added invalid test cases for DSTU3 --- .../CommonCodeSystemsTerminologyService.java | 11 +---------- .../CommonCodeSystemsTerminologyServiceTest.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index cfea4d2ac89..41170364e85 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -335,14 +335,6 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { public static String getCodeSystemUrl(@Nonnull IBaseResource theCodeSystem) { String url; switch (theCodeSystem.getStructureFhirVersionEnum()) { - case DSTU2_HL7ORG: { - url = ((CodeSystem) theCodeSystem).getUrl(); - break; - } - case DSTU3: { - url = ((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem).getUrl(); - break; - } case R4: { url = ((org.hl7.fhir.r4.model.CodeSystem) theCodeSystem).getUrl(); break; @@ -351,8 +343,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { url = ((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem).getUrl(); break; } - case DSTU2: - case DSTU2_1: + case DSTU3: default: throw new IllegalArgumentException("Can not handle version: " + theCodeSystem.getStructureFhirVersionEnum()); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java index 1be573dd025..b3cb3984ee9 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java @@ -5,6 +5,8 @@ import ca.uhn.fhir.context.FhirVersionEnum; 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.rest.server.exceptions.InvalidRequestException; + import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ValueSet; @@ -13,6 +15,9 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; + +import javax.annotation.Nonnull; public class CommonCodeSystemsTerminologyServiceTest { @@ -114,4 +119,14 @@ public class CommonCodeSystemsTerminologyServiceTest { assertEquals(null, cs); } + @Test + public void testFetchCodeSystemUrlDstu3() { + try { + CommonCodeSystemsTerminologyService.getCodeSystemUrl(new org.hl7.fhir.dstu3.model.CodeSystem()); + + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Can not handle version: DSTU3", e.getMessage()); + } + } } From 3d3d013d72e6f4d7ca6665897c79641157486b2a Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Wed, 23 Sep 2020 09:19:06 -0400 Subject: [PATCH 11/11] Changed import of IdType to r5. --- .../jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 1c13173d589..122149c6f8e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -29,7 +29,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeType;