diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 578c0e51dc1..82fae249713 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -125,6 +125,8 @@ ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.matchesFound=Matches fo ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.noMatchesFound=No matches found! ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.matchesFound=Matches found! ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.noMatchesFound=No matches found! +ca.uhn.fhir.jpa.dao.r5.FhirResourceDaoConceptMapR5.matchesFound=Matches found! +ca.uhn.fhir.jpa.dao.r5.FhirResourceDaoConceptMapR5.noMatchesFound=No matches found! ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1} ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderToken.textModifierDisabledForSearchParam=The :text modifier is disabled for this search parameter @@ -143,6 +145,7 @@ ca.uhn.fhir.jpa.binstore.BinaryAccessProvider.unknownType=Content in resource of ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrl=Can not create multiple CodeSystem resources with CodeSystem.url "{0}", already have one with resource ID: {1} ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrlAndVersion=Can not create multiple CodeSystem resources with CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2} ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1} +ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrlAndVersion=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", ConceptMap.version "{1}", already have one with resource ID: {2} ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can not create multiple ValueSet resources with ValueSet.url "{0}", already have one with resource ID: {1} ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrlAndVersion=Can not create multiple ValueSet resources with ValueSet.url "{0}" and ValueSet.version "{1}", already have one with resource ID: {2} diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationQuery.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationQuery.java index 6aefcf9ae3a..06bfd9cd07a 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationQuery.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationQuery.java @@ -23,11 +23,14 @@ package ca.uhn.fhir.jpa.api.model; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.UriType; public class TranslationQuery { private Coding myCoding; private Long myResourceId; + private UriType myUrl; + private StringType myConceptMapVersion; private UriType mySource; private UriType myTarget; private UriType myTargetSystem; @@ -58,6 +61,32 @@ public class TranslationQuery { myResourceId = theResourceId; } + //-- url + public boolean hasUrl() { + return myUrl != null && myUrl.hasValue(); + } + + public UriType getUrl() { + return myUrl; + } + + public void setUrl(UriType theUrl) { + myUrl = theUrl; + } + + //-- ConceptMapVersion + public boolean hasConceptMapVersion() { + return myConceptMapVersion != null && myConceptMapVersion.hasValue(); + } + + public StringType getConceptMapVersion() { + return myConceptMapVersion; + } + + public void setConceptMapVersion(StringType theConceptMapVersion) { + myConceptMapVersion = theConceptMapVersion; + } + public boolean hasSource() { return mySource != null && mySource.hasValue(); } @@ -107,6 +136,8 @@ public class TranslationQuery { .append(getCoding().getSystem(), that.getCoding().getSystem()) .append(getCoding().getVersion(), that.getCoding().getVersion()) .append(getResourceId(), that.getResourceId()) + .append(getUrl(), that.getUrl()) + .append(getConceptMapVersion(), that.getConceptMapVersion()) .append(getSource(), that.getSource()) .append(getTarget(), that.getTarget()) .append(getTargetSystem(), that.getTargetSystem()) @@ -120,6 +151,8 @@ public class TranslationQuery { .append(getCoding().getSystem()) .append(getCoding().getVersion()) .append(getResourceId()) + .append(getUrl()) + .append(getConceptMapVersion()) .append(getSource()) .append(getTarget()) .append(getTargetSystem()) diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationRequest.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationRequest.java index 19d6388b96c..732114732ea 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationRequest.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/TranslationRequest.java @@ -1,5 +1,8 @@ package ca.uhn.fhir.jpa.api.model; +import java.util.ArrayList; +import java.util.List; + /* * #%L * HAPI FHIR JPA API @@ -24,15 +27,15 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.UriType; -import java.util.ArrayList; -import java.util.List; - public class TranslationRequest { private CodeableConcept myCodeableConcept; private Long myResourceId; private BooleanType myReverse; + private UriType myUrl; + private StringType myConceptMapVersion; private UriType mySource; private UriType myTarget; private UriType myTargetSystem; @@ -90,6 +93,24 @@ public class TranslationRequest { return false; } + public UriType getUrl() { + return myUrl; + } + + public TranslationRequest setUrl(UriType theUrl) { + myUrl = theUrl; + return this; + } + + public StringType getConceptMapVersion() { + return myConceptMapVersion; + } + + public TranslationRequest setConceptMapVersion(StringType theConceptMapVersion) { + myConceptMapVersion = theConceptMapVersion; + return this; + } + public UriType getSource() { return mySource; } @@ -130,6 +151,14 @@ public class TranslationRequest { translationQuery.setResourceId(this.getResourceId()); } + if (this.hasUrl()) { + translationQuery.setUrl(this.getUrl()); + } + + if (this.hasConceptMapVersion()) { + translationQuery.setConceptMapVersion(this.getConceptMapVersion()); + } + if (this.hasSource()) { translationQuery.setSource(this.getSource()); } @@ -156,6 +185,14 @@ public class TranslationRequest { return myReverse != null; } + public boolean hasUrl() { + return myUrl != null && myUrl.hasValue(); + } + + public boolean hasConceptMapVersion() { + return myConceptMapVersion != null && myConceptMapVersion.hasValue(); + } + public boolean hasSource() { return mySource != null && mySource.hasValue(); } 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 77525d07b26..64ba0da3697 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; @@ -302,4 +304,10 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao 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/data/ITermConceptMapDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapDao.java index 17598bf8066..a7716ebafe0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapDao.java @@ -1,12 +1,15 @@ package ca.uhn.fhir.jpa.dao.data; -import ca.uhn.fhir.jpa.entity.TermConceptMap; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.Optional; +import ca.uhn.fhir.jpa.entity.TermConceptMap; /* * #%L @@ -36,6 +39,17 @@ public interface ITermConceptMapDao extends JpaRepository @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid") Optional findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid); - @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url") + // Keep backwards compatibility, recommend to use findTermConceptMapByUrlAndNullVersion instead + @Deprecated + @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url and cm.myVersion is null") Optional findTermConceptMapByUrl(@Param("url") String theUrl); + + @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url and cm.myVersion is null") + Optional findTermConceptMapByUrlAndNullVersion(@Param("url") String theUrl); + + @Query(value="SELECT cm FROM TermConceptMap cm INNER JOIN ResourceTable r ON r.myId = cm.myResourcePid WHERE cm.myUrl = :url ORDER BY r.myUpdated DESC") + List getTermConceptMapEntitiesByUrlOrderByVersion(Pageable thePage, @Param("url") String theUrl); + + @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url AND cm.myVersion = :version") + Optional findTermConceptMapByUrlAndVersion(@Param("url") String theUrl, @Param("version") String theVersion); } 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 16f219a6f4c..2f76d45a568 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; @@ -155,4 +156,10 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, + IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); + } + } 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 bb9e2708558..9c9357c37f6 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 { @@ -150,5 +153,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/r4/FhirResourceDaoConceptMapR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java index d63074086db..67b518089f6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.api.server.RequestDetails; + +import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeType; @@ -39,6 +41,7 @@ import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.UriType; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; @@ -152,7 +155,11 @@ public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -152,5 +155,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/dao/r5/FhirResourceDaoConceptMapR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java index 6975c04c964..5effba03e95 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java @@ -1,5 +1,21 @@ package ca.uhn.fhir.jpa.dao.r5; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hl7.fhir.convertors.VersionConvertor_40_50; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.ConceptMap; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.springframework.beans.factory.annotation.Autowired; + /* * #%L * HAPI FHIR JPA Server @@ -29,23 +45,9 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.hl7.fhir.convertors.VersionConvertor_40_50; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r5.model.BooleanType; -import org.hl7.fhir.r5.model.CodeType; -import org.hl7.fhir.r5.model.Coding; -import org.hl7.fhir.r5.model.ConceptMap; -import org.hl7.fhir.r5.model.StringType; -import org.hl7.fhir.r5.model.UriType; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoConceptMap { @Autowired @@ -153,7 +155,11 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao myConceptMapGroups; @@ -131,6 +135,17 @@ public class TermConceptMap implements Serializable { return this; } + public String getVersion() { + return myVersion; + } + + public TermConceptMap setVersion(String theVersion) { + ValidateUtil.isNotTooLongOrThrowIllegalArgument(theVersion, MAX_VER_LENGTH, + "Version exceeds maximum length (" + MAX_VER_LENGTH + "): " + length(theVersion)); + myVersion = theVersion; + return this; + } + @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) @@ -140,6 +155,7 @@ public class TermConceptMap implements Serializable { .append("mySource", mySource) .append("myTarget", myTarget) .append("myUrl", myUrl) + .append("myVersion", myVersion) .append(myConceptMapGroups != null ? ("myConceptMapGroups - size=" + myConceptMapGroups.size()) : ("myConceptMapGroups=(null)")) .toString(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java index bcc7cf17e60..d965649fd00 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java @@ -54,6 +54,8 @@ public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderD public Parameters translate( HttpServletRequest theServletRequest, @IdParam(optional = true) IdType theId, + @OperationParam(name = "url", min = 0, max = 1) UriType theUrl, + @OperationParam(name = "conceptMapVersion", min = 0, max = 1) StringType theConceptMapVersion, @OperationParam(name = "code", min = 0, max = 1) CodeType theSourceCode, @OperationParam(name = "system", min = 0, max = 1) UriType theSourceCodeSystem, @OperationParam(name = "version", min = 0, max = 1) StringType theSourceCodeSystemVersion, @@ -65,6 +67,10 @@ public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderD @OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse, RequestDetails theRequestDetails ) { + boolean haveUrl = theUrl != null + && theUrl.hasValue(); + boolean haveConceptMapVersion = theConceptMapVersion != null + && theConceptMapVersion.hasValue(); boolean haveSourceCode = theSourceCode != null && theSourceCode.hasValue(); boolean haveSourceCodeSystem = theSourceCodeSystem != null @@ -93,6 +99,15 @@ public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderD TranslationRequest translationRequest = new TranslationRequest(); try { + + if (haveUrl) { + translationRequest.setUrl(VersionConvertor_30_40.convertUri(theUrl)); + } + + if (haveConceptMapVersion) { + translationRequest.setConceptMapVersion(VersionConvertor_30_40.convertString(theConceptMapVersion)); + } + // Convert from DSTU3 to R4 if (haveSourceCode) { translationRequest.getCodeableConcept().addCoding().setCodeElement(VersionConvertor_30_40.convertCode(theSourceCode)); 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 a3e16a7fb3c..5712d8d6057 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 { @@ -107,4 +112,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/provider/r4/BaseJpaResourceProviderConceptMapR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java index eb6f7715e32..3bd1b673007 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java @@ -49,6 +49,8 @@ public class BaseJpaResourceProviderConceptMapR4 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/provider/r5/BaseJpaResourceProviderConceptMapR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java index 62b3949663f..02bd03ec35a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java @@ -50,6 +50,8 @@ public class BaseJpaResourceProviderConceptMapR5 extends JpaResourceProviderR5 optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl); + String conceptMapVersion = termConceptMap.getVersion(); + Optional optionalExistingTermConceptMapByUrl = null; + if (isBlank(conceptMapVersion)) { + optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndNullVersion(conceptMapUrl); + } else { + optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); + } if (!optionalExistingTermConceptMapByUrl.isPresent()) { try { if (isNotBlank(source)) { @@ -1710,13 +1718,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } else { TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapByUrl.get(); - String msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, - "cannotCreateDuplicateConceptMapUrl", - conceptMapUrl, - existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + if (isBlank(conceptMapVersion)) { + String msg = myContext.getLocalizer().getMessage( + BaseTermReadSvcImpl.class, + "cannotCreateDuplicateConceptMapUrl", + conceptMapUrl, + existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + throw new UnprocessableEntityException(msg); - throw new UnprocessableEntityException(msg); + } else { + String msg = myContext.getLocalizer().getMessage( + BaseTermReadSvcImpl.class, + "cannotCreateDuplicateConceptMapUrlAndVersion", + conceptMapUrl, conceptMapVersion, + existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + throw new UnprocessableEntityException(msg); + } } ourLog.info("Done storing TermConceptMap[{}] for {}", termConceptMap.getId(), theConceptMap.getIdElement().toVersionless().getValueAsString()); @@ -2049,6 +2066,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { List cachedTargets; ArrayList predicates; Coding coding; + + //-- get the latest ConceptMapVersion if theTranslationRequest has url, but ConceptMapVersion + String latestConceptMapVersion = null; + if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) + latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); + for (TranslationQuery translationQuery : translationQueries) { cachedTargets = myTranslationCache.getIfPresent(translationQuery); if (cachedTargets == null) { @@ -2075,6 +2098,21 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem().getValueAsString())); } + if (translationQuery.hasUrl()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString())); + if (translationQuery.hasConceptMapVersion()) { + // both url and conceptMapVersion + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString())); + } else { + if (StringUtils.isNotBlank(latestConceptMapVersion)) { + // only url and use latestConceptMapVersion + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); + } else { + predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); + } + } + } + if (translationQuery.hasSource()) { predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString())); } @@ -2132,6 +2170,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { List cachedElements; ArrayList predicates; Coding coding; + + //-- get the latest ConceptMapVersion if theTranslationRequest has url, but ConceptMapVersion + String latestConceptMapVersion = null; + if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) + latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); + for (TranslationQuery translationQuery : translationQueries) { cachedElements = myTranslationWithReverseCache.getIfPresent(translationQuery); if (cachedElements == null) { @@ -2158,6 +2202,21 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); } + if (translationQuery.hasUrl()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString())); + if (translationQuery.hasConceptMapVersion()) { + // both url and conceptMapVersion + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString())); + } else { + if (StringUtils.isNotBlank(latestConceptMapVersion)) { + // only url and use latestConceptMapVersion + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); + } else { + predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); + } + } + } + if (translationQuery.hasTargetSystem()) { predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem().getValueAsString())); } @@ -2224,6 +2283,20 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { throw new ResourceNotFoundException("Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSet)); } + // Special case for the translate operation with url and without + // conceptMapVersion, find the latest conecptMapVersion + private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { + + Pageable page = PageRequest.of(0, 1); + List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, + theTranslationRequest.getUrl().asStringValue()); + if (!theConceptMapList.isEmpty()) { + return theConceptMapList.get(0).getVersion(); + } + + return null; + } + @Override @Transactional public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { @@ -2560,5 +2633,120 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return ourLastResultsFromTranslationWithReverseCache; } + @Override + @Transactional + 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)); + } else { + query.orderBy(criteriaBuilder.desc(root.get("myUpdated"))); + } + + 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(SINGLE_FETCH_SIZE); + 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 bfac6ae33d9..b945f337a63 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 @@ -122,4 +122,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/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java index c9c75772799..0652e8163eb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java @@ -52,7 +52,7 @@ public abstract class BaseLoincHandler implements IRecordHandler { private final List myValueSets; private final Map myIdToValueSet = new HashMap<>(); private final Map myCode2Concept; - final Properties myUploadProperties; + protected final Properties myUploadProperties; BaseLoincHandler(Map theCode2Concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { myValueSets = theValueSets; @@ -115,7 +115,7 @@ public abstract class BaseLoincHandler implements IRecordHandler { conceptMap.setId(theMapping.getConceptMapId()); conceptMap.setUrl(theMapping.getConceptMapUri()); conceptMap.setName(theMapping.getConceptMapName()); - conceptMap.setVersion(myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())); + conceptMap.setVersion(theMapping.getConceptMapVersion()); conceptMap.setPublisher(REGENSTRIEF_INSTITUTE_INC); conceptMap.addContact() .setName(REGENSTRIEF_INSTITUTE_INC) @@ -228,6 +228,7 @@ public abstract class BaseLoincHandler implements IRecordHandler { private String myCopyright; private String myConceptMapId; private String myConceptMapUri; + private String myConceptMapVersion; private String myConceptMapName; private String mySourceCodeSystem; private String mySourceCode; @@ -265,6 +266,15 @@ public abstract class BaseLoincHandler implements IRecordHandler { return this; } + String getConceptMapVersion() { + return myConceptMapVersion; + } + + ConceptMapping setConceptMapVersion(String theConceptMapVersion) { + myConceptMapVersion = theConceptMapVersion; + return this; + } + String getCopyright() { return myCopyright; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java index 687ef77c977..abe9d8d16a7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java @@ -52,6 +52,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implemen @Override public void accept(CSVRecord theRecord) { + String loincIeeeCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); String loincNumber = trim(theRecord.get("LOINC_NUM")); String longCommonName = trim(theRecord.get("LOINC_LONG_COMMON_NAME")); String ieeeCode = trim(theRecord.get("IEEE_CF_CODE10")); @@ -64,6 +65,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implemen new ConceptMapping() .setConceptMapId(LOINC_IEEE_CM_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(LOINC_IEEE_CM_URI) + .setConceptMapVersion(loincIeeeCmVersion) .setConceptMapName(LOINC_IEEE_CM_NAME) .setSourceCodeSystem(sourceCodeSystemUri) .setSourceCode(loincNumber) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index 52f48052b5e..9f2251153b7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -76,6 +76,9 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme String extCodeSystemVersion = trim(theRecord.get("ExtCodeSystemVersion")); String extCodeSystemCopyrightNotice = trim(theRecord.get("ExtCodeSystemCopyrightNotice")); + // ConceptMap version from properties files + String loincPartMapVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + Enumerations.ConceptMapEquivalence equivalence; switch (trim(defaultString(mapType))) { case "": @@ -131,6 +134,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme new ConceptMapping() .setConceptMapId(loincPartMapId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(loincPartMapUri) + .setConceptMapVersion(loincPartMapVersion) .setConceptMapName(loincPartMapName) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) .setSourceCode(partNumber) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java index 061dabcd4ac..de060f2aba5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java @@ -81,6 +81,9 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor String rpid = trim(theRecord.get("RPID")); String longName = trim(theRecord.get("LongName")); + // ConceptMap version from properties files + String loincRsnaCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + // RSNA Codes VS ValueSet vs; if (!myIdToValueSet.containsKey(RSNA_CODES_VS_ID)) { @@ -177,6 +180,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor new ConceptMapping() .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI) + .setConceptMapVersion(loincRsnaCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) .setSourceCode(partNumber) @@ -194,6 +198,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor new ConceptMapping() .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI) + .setConceptMapVersion(loincRsnaCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) .setSourceCode(loincNumber) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java index 4599cfd3c6a..f7aa915920d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java @@ -1,23 +1,32 @@ package ca.uhn.fhir.jpa.dao.dstu3; -import ca.uhn.fhir.jpa.api.model.TranslationMatch; -import ca.uhn.fhir.jpa.api.model.TranslationRequest; -import ca.uhn.fhir.jpa.api.model.TranslationResult; -import ca.uhn.fhir.util.TestUtil; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Optional; + import org.hl7.fhir.dstu3.model.ConceptMap; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.UriType; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import static org.junit.jupiter.api.Assertions.*; +import ca.uhn.fhir.jpa.api.model.TranslationMatch; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; +import ca.uhn.fhir.jpa.api.model.TranslationResult; +import ca.uhn.fhir.jpa.entity.TermConceptMap; public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ConceptMapTest.class); @@ -98,4 +107,74 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { }); } + @Test + public void testConcaptMapFindTermConceptMapByUrl() { + + Pageable page = PageRequest.of(0, 1); + List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + assertEquals(1, theExpConceptMapList.size()); + assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl()); + + } + + @Test + public void testConcaptMapTwoConceptMapWithSameUrlDifferentVersion() { + + String theUrl = "http://loinc.org/property/analyte-suffix"; + ConceptMap theConceptMap1 = new ConceptMap(); + ConceptMap theConceptMap2 = new ConceptMap(); + + theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1"); + theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2").setVersion("v2"); + + myConceptMapDao.create(theConceptMap1); + myConceptMapDao.create(theConceptMap2); + + Optional theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1"); + Optional theExpConceptMapV2 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v2"); + + assertTrue(theExpConceptMapV1.isPresent()); + assertEquals(theUrl, theExpConceptMapV1.get().getUrl()); + assertEquals("v1", theExpConceptMapV1.get().getVersion()); + + assertTrue(theExpConceptMapV2.isPresent()); + assertEquals(theUrl, theExpConceptMapV2.get().getUrl()); + assertEquals("v2", theExpConceptMapV2.get().getVersion()); + + // should return the latest one which is v2 + Pageable page = PageRequest.of(0, 1); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + + assertEquals(1, theExpSecondOne.size()); + assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); + assertEquals("v2", theExpSecondOne.get(0).getVersion()); + } + + @Test + public void testConcaptMapTwoConceptMapWithSameUrlOneWithoutVersion() { + + String theUrl = "http://loinc.org/property/analyte-suffix"; + ConceptMap theConceptMap1 = new ConceptMap(); + ConceptMap theConceptMap2 = new ConceptMap(); + + theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1"); + theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2"); + + myConceptMapDao.create(theConceptMap1); + myConceptMapDao.create(theConceptMap2); + + Optional theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1"); + + assertTrue(theExpConceptMapV1.isPresent()); + assertEquals(theUrl, theExpConceptMapV1.get().getUrl()); + assertEquals("v1", theExpConceptMapV1.get().getVersion()); + + // should return the latest one which in this case is not versioned + Pageable page = PageRequest.of(0, 1); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + + assertEquals(1, theExpSecondOne.size()); + assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); + assertNull(theExpSecondOne.get(0).getVersion()); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java index 9d8baa5be04..997e5a35b45 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java @@ -1,31 +1,37 @@ package ca.uhn.fhir.jpa.dao.r4; -import ca.uhn.fhir.jpa.api.model.TranslationMatch; -import ca.uhn.fhir.jpa.api.model.TranslationRequest; -import ca.uhn.fhir.jpa.api.model.TranslationResult; -import ca.uhn.fhir.util.TestUtil; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CanonicalType; 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.ConceptMapEquivalence; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; import org.hl7.fhir.r4.model.UriType; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import ca.uhn.fhir.jpa.api.model.TranslationMatch; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; +import ca.uhn.fhir.jpa.api.model.TranslationResult; +import ca.uhn.fhir.jpa.entity.TermConceptMap; public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4ConceptMapTest.class); @@ -1148,5 +1154,74 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { assertEquals("S52.209A", outcome.getMatches().get(0).getConcept().getCode()); } + @Test + public void testConcaptMapFindTermConceptMapByUrl() { + + Pageable page = PageRequest.of(0, 1); + List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + assertEquals(1, theExpConceptMapList.size()); + assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl()); + + } + @Test + public void testConcaptMapTwoConceptMapWithSameUrlDifferentVersion() { + + String theUrl = "http://loinc.org/property/analyte-suffix"; + ConceptMap theConceptMap1 = new ConceptMap(); + ConceptMap theConceptMap2 = new ConceptMap(); + + theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1"); + theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2").setVersion("v2"); + + myConceptMapDao.create(theConceptMap1); + myConceptMapDao.create(theConceptMap2); + + Optional theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1"); + Optional theExpConceptMapV2 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v2"); + + assertTrue(theExpConceptMapV1.isPresent()); + assertEquals(theUrl, theExpConceptMapV1.get().getUrl()); + assertEquals("v1", theExpConceptMapV1.get().getVersion()); + + assertTrue(theExpConceptMapV2.isPresent()); + assertEquals(theUrl, theExpConceptMapV2.get().getUrl()); + assertEquals("v2", theExpConceptMapV2.get().getVersion()); + + // should return the latest one which is v2 + Pageable page = PageRequest.of(0, 1); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + + assertEquals(1, theExpSecondOne.size()); + assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); + assertEquals("v2", theExpSecondOne.get(0).getVersion()); + } + + @Test + public void testConcaptMapTwoConceptMapWithSameUrlOneWithoutVersion() { + + String theUrl = "http://loinc.org/property/analyte-suffix"; + ConceptMap theConceptMap1 = new ConceptMap(); + ConceptMap theConceptMap2 = new ConceptMap(); + + theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1"); + theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2"); + + myConceptMapDao.create(theConceptMap1); + myConceptMapDao.create(theConceptMap2); + + Optional theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1"); + + assertTrue(theExpConceptMapV1.isPresent()); + assertEquals(theUrl, theExpConceptMapV1.get().getUrl()); + assertEquals("v1", theExpConceptMapV1.get().getVersion()); + + // should return the latest one which is v2 + Pageable page = PageRequest.of(0, 1); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + + assertEquals(1, theExpSecondOne.size()); + assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); + assertNull(theExpSecondOne.get(0).getVersion()); + } } 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 ee2f14e3c5b..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 @@ -330,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()); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java index c64e531fdf4..835e806e93e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java @@ -1,18 +1,30 @@ package ca.uhn.fhir.jpa.provider.dstu3; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.util.TestUtil; -import org.hl7.fhir.dstu3.model.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.ConceptMap; +import org.hl7.fhir.dstu3.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.dstu3.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.dstu3.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.dstu3.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; -import static org.junit.jupiter.api.Assertions.*; +import ca.uhn.fhir.rest.api.MethodOutcome; public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDstu3Test { private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMapTest.class); @@ -114,4 +126,550 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst part = getPartByName(param, "source"); assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); } + + @Test + public void testTranslateWithConceptMapUrlAndVersion() { + + String url = "http://url"; + createConceptMap(url, "v1", "12222", "Target Code 12222"); + createConceptMap(url, "v2", "13333", "Target Code 13333"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_v2() { + + String url = "http://url"; + createConceptMap(url, "v1", "12222", "Target Code 12222"); + createConceptMap(url, "v2", "13333", "Target Code 13333"); + + // Call translate with ConceptMap v2. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + // Should return v2 since v2 specified. + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_v1() { + + String url = "http://url"; + createConceptMap(url, "v1", "12222", "Target Code 12222"); + createConceptMap(url, "v2", "13333", "Target Code 13333"); + + // Call translate with ConceptMap v1. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v1")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + // Should return v1 since v1 specified. + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12222", coding.getCode()); + assertEquals("Target Code 12222", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_NoVersion() { + + String url = "http://url"; + createConceptMap(url, "v1", "12222", "Target Code 12222"); + createConceptMap(url, "v2", "13333", "Target Code 13333"); + + // Call translate with no ConceptMap version. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + // Should return v2 since v2 is the most recently updated version. + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v1() { + + String url = "http://url"; + createConceptMap(url, null, "12222", "Target Code 12222"); // first version is null + createConceptMap(url, "v2", "13333", "Target Code 13333"); + + // Call translate with no ConceptMap version. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + // Should return v2 since v2 is the most recently updated version. + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v2() { + + String url = "http://url"; + createConceptMap(url, "v1", "12222", "Target Code 12222"); + createConceptMap(url, null, "13333", "Target Code 13333"); // second version is null + + // Call translate with no ConceptMap version. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + // Should return v2 since v2 is the most recently updated version. + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithConceptMap_WrongUrl_NoVersion() { + + String url = "http://url"; + createConceptMap(url, "v1", "12222", "Target Code 12222"); + createConceptMap(url, "v2", "13333", "Target Code 13333"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType("http://invalid.url.com")); // no exsits url + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertFalse(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("No matches found!", ((StringType) param.getValue()).getValueAsString()); + + } + + @Test + public void testTranslateWithReverseConceptMapUrlAndVersion() { + + String url = "http://url"; + createReverseConceptMap(url, "v1", "12222", "Source Code 12222"); + createReverseConceptMap(url, "v2", "13333", "Source Code 13333"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Source Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseConceptMapUrl_NoVersion() { + + String url = "http://url"; + createReverseConceptMap(url, "v1", "12222", "Source Code 12222"); + createReverseConceptMap(url, "v2", "13333", "Source Code 13333"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Source Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseConceptMapUrl_NoVersion_null_v1() { + + String url = "http://url"; + createReverseConceptMap(url, null, "12222", "Source Code 12222"); + createReverseConceptMap(url, "v2", "13333", "Source Code 13333"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Source Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseConceptMapUrl_NoVersion_null_v2() { + + String url = "http://url"; + createReverseConceptMap(url, "v1", "12222", "Source Code 12222"); + createReverseConceptMap(url, null, "13333", "Source Code 13333"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = ourClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Source Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + private void createConceptMap(String url, String version, String targetCode, String targetDisplay) { + + ConceptMap conceptMap = new ConceptMap(); + conceptMap.setUrl(url).setVersion(version).setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group1 = conceptMap.addGroup(); + group1.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element1 = group1.addElement(); + element1.setCode("11111").setDisplay("Source Code 11111"); + + TargetElementComponent target1 = element1.addTarget(); + target1.setCode(targetCode).setDisplay(targetDisplay).setEquivalence(ConceptMapEquivalence.EQUAL); + + IIdType conceptMapId = myConceptMapDao.create(conceptMap, mySrd).getId().toUnqualifiedVersionless(); + conceptMap = myConceptMapDao.read(conceptMapId); + + ourLog.info("ConceptMap: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + } + + private void createReverseConceptMap(String url, String version, String sourceCode, String sourceDisplay) { + + //- conceptMap1 v1 + ConceptMap conceptMap = new ConceptMap(); + conceptMap.setUrl(url).setVersion(version).setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group1 = conceptMap.addGroup(); + group1.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element1 = group1.addElement(); + element1.setCode(sourceCode).setDisplay(sourceDisplay); + + TargetElementComponent target1 = element1.addTarget(); + target1.setCode("11111").setDisplay("11111"); + + IIdType conceptMapId = myConceptMapDao.create(conceptMap, mySrd).getId().toUnqualifiedVersionless(); + ConceptMap conceptMap1 = myConceptMapDao.read(conceptMapId); + + ourLog.info("ConceptMap : \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap1)); + + } } 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..c4d705303fc --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -0,0 +1,560 @@ +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; + +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.CodeSystem.ConceptDefinitionComponent; +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 StringType("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 StringType("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(); + fail(); + } 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 StringType("Systolic blood pressure.inspiration - expiration")); + + 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()); + } + } + + @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(); + fail(); + } 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(); + 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()); + } + } + + @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(); + 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()); + } + } + + @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(); + 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()); + } + } + + @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()); + } + + @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")); + + 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()); + } + + + @Test + public void testValidateCodeWithUrlAndVersion_v2() { + + 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("v2")); + inParams.addParameter().setName("code").setValue(new CodeType("1")); + + 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 v2 display", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + + @Test + public void testValidateCodeWithUrlAndVersion_noVersion() { + + 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("code").setValue(new CodeType("1")); + + 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 v2 display", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeWithUrlAndVersion_noVersion_null_v1() { + + String url = "http://url"; + createCodeSystem(url, null, "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("code").setValue(new CodeType("1")); + + 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 v2 display", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + + @Test + public void testValidateCodeWithUrlAndVersion_noVersion_null_v2() { + + String url = "http://url"; + createCodeSystem(url, "v1", "1", "Code v1 display"); + createCodeSystem(url, null, "1", "Code v2 display"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("code").setValue(new CodeType("1")); + + 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 v2 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); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java index 14e2fe2b28f..2888f57b253 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java @@ -1,16 +1,31 @@ package ca.uhn.fhir.jpa.provider.r4; -import ca.uhn.fhir.rest.api.MethodOutcome; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.BooleanType; +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.ConceptMap; +import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; +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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; -import static org.junit.jupiter.api.Assertions.*; +import ca.uhn.fhir.rest.api.MethodOutcome; public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test { private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR4ConceptMapTest.class); @@ -870,6 +885,90 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); } + @Test + public void testTranslateWithConcaptMapUrlAndVersion() { + + //- conceptMap1 v1 + ConceptMap conceptMap1 = new ConceptMap(); + conceptMap1.setUrl(CM_URL).setVersion("v1").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group1 = conceptMap1.addGroup(); + group1.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element1 = group1.addElement(); + element1.setCode("11111").setDisplay("Source Code 11111"); + + TargetElementComponent target1 = element1.addTarget(); + target1.setCode("12222").setDisplay("Target Code 12222").setEquivalence(ConceptMapEquivalence.EQUAL); + + IIdType conceptMapId1 = myConceptMapDao.create(conceptMap1, mySrd).getId().toUnqualifiedVersionless(); + conceptMap1 = myConceptMapDao.read(conceptMapId1); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap1)); + + //- conceptMap1 v2 + ConceptMap conceptMap2 = new ConceptMap(); + conceptMap2.setUrl(CM_URL).setVersion("v2").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group2 = conceptMap2.addGroup(); + group2.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element2 = group2.addElement(); + element2.setCode("11111").setDisplay("Source Code 11111"); + + TargetElementComponent target2 = element2.addTarget(); + target2.setCode("13333").setDisplay("Target Code 13333").setEquivalence(ConceptMapEquivalence.EQUAL); + + IIdType conceptMapId2 = myConceptMapDao.create(conceptMap2, mySrd).getId().toUnqualifiedVersionless(); + conceptMap2 = myConceptMapDao.read(conceptMapId2); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2)); + + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = myClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + } + + @Test public void testTranslateWithInstance() { ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); @@ -1621,6 +1720,89 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); } + @Test + public void testTranslateWithReverseConcaptMapUrlAndVersion() { + + //- conceptMap1 v1 + ConceptMap conceptMap1 = new ConceptMap(); + conceptMap1.setUrl(CM_URL).setVersion("v1").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group1 = conceptMap1.addGroup(); + group1.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element1 = group1.addElement(); + element1.setCode("12222").setDisplay("Source Code 12222"); + + TargetElementComponent target1 = element1.addTarget(); + target1.setCode("11111").setDisplay("11111").setEquivalence(ConceptMapEquivalence.EQUAL); + + IIdType conceptMapId1 = myConceptMapDao.create(conceptMap1, mySrd).getId().toUnqualifiedVersionless(); + conceptMap1 = myConceptMapDao.read(conceptMapId1); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap1)); + + //- conceptMap1 v2 + ConceptMap conceptMap2 = new ConceptMap(); + conceptMap2.setUrl(CM_URL).setVersion("v2").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group2 = conceptMap2.addGroup(); + group2.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element2 = group2.addElement(); + element2.setCode("13333").setDisplay("Source Code 13333"); + + TargetElementComponent target2 = element2.addTarget(); + target2.setCode("11111").setDisplay("Target Code 11111").setEquivalence(ConceptMapEquivalence.EQUAL); + + IIdType conceptMapId2 = myConceptMapDao.create(conceptMap2, mySrd).getId().toUnqualifiedVersionless(); + conceptMap2 = myConceptMapDao.read(conceptMapId2); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2)); + + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = myClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Source Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + + } + @Test public void testTranslateWithReverseAndInstance() { ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); 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); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ConceptMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ConceptMapTest.java new file mode 100644 index 00000000000..04b55803ce4 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ConceptMapTest.java @@ -0,0 +1,191 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.ConceptMap; +import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; +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 ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5ConceptMapTest.class); + + @Test + public void testTranslateWithConcaptMapUrlAndVersion() { + + //- conceptMap1 v1 + ConceptMap conceptMap1 = new ConceptMap(); + conceptMap1.setUrl(CM_URL).setVersion("v1").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group1 = conceptMap1.addGroup(); + group1.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element1 = group1.addElement(); + element1.setCode("11111").setDisplay("Source Code 11111"); + + TargetElementComponent target1 = element1.addTarget(); + target1.setCode("12222").setDisplay("Target Code 12222"); + + IIdType conceptMapId1 = myConceptMapDao.create(conceptMap1, mySrd).getId().toUnqualifiedVersionless(); + conceptMap1 = myConceptMapDao.read(conceptMapId1); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap1)); + + //- conceptMap1 v2 + ConceptMap conceptMap2 = new ConceptMap(); + conceptMap2.setUrl(CM_URL).setVersion("v2").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group2 = conceptMap2.addGroup(); + group2.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element2 = group2.addElement(); + element2.setCode("11111").setDisplay("Source Code 11111"); + + TargetElementComponent target2 = element2.addTarget(); + target2.setCode("13333").setDisplay("Target Code 13333"); + + IIdType conceptMapId2 = myConceptMapDao.create(conceptMap2, mySrd).getId().toUnqualifiedVersionless(); + conceptMap2 = myConceptMapDao.read(conceptMapId2); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2)); + + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = myClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + + ParametersParameterComponent part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Target Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_2, coding.getSystem()); + assertEquals("Version 2", coding.getVersion()); + + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + } + + @Test + public void testTranslateWithReverseConcaptMapUrlAndVersion() { + + //- conceptMap1 v1 + ConceptMap conceptMap1 = new ConceptMap(); + conceptMap1.setUrl(CM_URL).setVersion("v1").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group1 = conceptMap1.addGroup(); + group1.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element1 = group1.addElement(); + element1.setCode("12222").setDisplay("Source Code 12222"); + + TargetElementComponent target1 = element1.addTarget(); + target1.setCode("11111").setDisplay("11111").setRelationship(ConceptMapRelationship.EQUIVALENT); + + IIdType conceptMapId1 = myConceptMapDao.create(conceptMap1, mySrd).getId().toUnqualifiedVersionless(); + conceptMap1 = myConceptMapDao.read(conceptMapId1); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap1)); + + //- conceptMap1 v2 + ConceptMap conceptMap2 = new ConceptMap(); + conceptMap2.setUrl(CM_URL).setVersion("v2").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group2 = conceptMap2.addGroup(); + group2.setSource(CS_URL).setSourceVersion("Version 1").setTarget(CS_URL_2).setTargetVersion("Version 2"); + + SourceElementComponent element2 = group2.addElement(); + element2.setCode("13333").setDisplay("Source Code 13333"); + + TargetElementComponent target2 = element2.addTarget(); + target2.setCode("11111").setDisplay("Target Code 11111").setRelationship(ConceptMapRelationship.EQUIVALENT); + + IIdType conceptMapId2 = myConceptMapDao.create(conceptMap2, mySrd).getId().toUnqualifiedVersionless(); + conceptMap2 = myConceptMapDao.read(conceptMapId2); + + ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2)); + + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); + inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2")); + inParams.addParameter().setName("code").setValue(new CodeType("11111")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + + Parameters respParams = myClient + .operation() + .onType(ConceptMap.class) + .named("translate") + .withParameters(inParams) + .execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams)); + + + ParametersParameterComponent param = getParameterByName(respParams, "result"); + assertTrue(((BooleanType) param.getValue()).booleanValue()); + + param = getParameterByName(respParams, "message"); + assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString()); + + assertEquals(1, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equivalent", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("13333", coding.getCode()); + assertEquals("Source Code 13333", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java index 288dcc85a29..5e400e1ffe0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java @@ -165,7 +165,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { assertEquals(1, group.getElement().get(0).getTarget().size()); assertEquals("420710006", group.getElement().get(0).getTarget().get(0).getCode()); assertEquals("Interferon beta (substance)", group.getElement().get(0).getTarget().get(0).getDisplay()); - + // Document Ontology ValueSet vs = valueSets.get(LoincDocumentOntologyHandler.DOCUMENT_ONTOLOGY_CODES_VS_ID); assertEquals(LoincDocumentOntologyHandler.DOCUMENT_ONTOLOGY_CODES_VS_NAME, vs.getName()); @@ -218,6 +218,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { // RSNA Playbook - LOINC Part -> RadLex RID Mappings conceptMap = conceptMaps.get(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID); assertEquals(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI, conceptMap.getUrl()); + assertEquals("Beta.1", conceptMap.getVersion()); assertEquals(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME, conceptMap.getName()); assertEquals(1, conceptMap.getGroup().size()); group = conceptMap.getGroupFirstRep(); @@ -234,6 +235,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { // RSNA Playbook - LOINC Term -> RadLex RPID Mappings conceptMap = conceptMaps.get(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID); assertEquals(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI, conceptMap.getUrl()); + assertEquals("Beta.1", conceptMap.getVersion()); assertEquals(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_NAME, conceptMap.getName()); assertEquals(1, conceptMap.getGroup().size()); group = conceptMap.getGroupFirstRep(); @@ -300,6 +302,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { ourLog.debug(FhirContext.forCached(FhirVersionEnum.R4).newXmlParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); assertEquals(LoincIeeeMedicalDeviceCodeHandler.LOINC_IEEE_CM_NAME, conceptMap.getName()); assertEquals(LoincIeeeMedicalDeviceCodeHandler.LOINC_IEEE_CM_URI, conceptMap.getUrl()); + assertEquals("Beta.1", conceptMap.getVersion()); assertEquals(1, conceptMap.getGroup().size()); assertEquals(ITermLoaderSvc.LOINC_URI, conceptMap.getGroup().get(0).getSource()); assertEquals(ITermLoaderSvc.IEEE_11073_10101_URI, conceptMap.getGroup().get(0).getTarget()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java index db5702d7819..b713b8a6908 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java @@ -27,6 +27,8 @@ import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; @@ -59,6 +61,13 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { persistConceptMap(conceptMap, HttpVerb.POST); } + private void createAndPersistConceptMap(String version) { + ConceptMap conceptMap = createConceptMap(); + conceptMap.setId("ConceptMap/cm"); + conceptMap.setVersion(version); + persistConceptMap(conceptMap, HttpVerb.POST); + } + @SuppressWarnings("EnumSwitchStatementWhichMissesCases") private void persistConceptMap(ConceptMap theConceptMap, HttpVerb theVerb) { switch (theVerb) { @@ -273,6 +282,19 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { } + @Test + public void testDuplicateConceptMapUrlsAndVersions() { + createAndPersistConceptMap("v1"); + + try { + createAndPersistConceptMap("v1"); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Can not create multiple ConceptMap resources with ConceptMap.url \"http://example.com/my_concept_map\", ConceptMap.version \"v1\", already have one with resource ID: ConceptMap/" + myConceptMapId.getIdPart(), e.getMessage()); + } + + } + @Test public void testDuplicateValueSetUrls() throws Exception { myDaoConfig.setPreExpandValueSets(true); @@ -299,10 +321,11 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - Optional optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(CM_URL); - assertTrue(optionalConceptMap.isPresent()); + Pageable page = PageRequest.of(0, 1); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); - TermConceptMap conceptMap = optionalConceptMap.get(); + TermConceptMap conceptMap = optionalConceptMap.get(0); ourLog.info("ConceptMap:\n" + conceptMap.toString()); @@ -477,10 +500,11 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { - Optional optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(CM_URL); - assertTrue(optionalConceptMap.isPresent()); + Pageable page = PageRequest.of(0, 1); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); - TermConceptMap conceptMap = optionalConceptMap.get(); + TermConceptMap conceptMap = optionalConceptMap.get(0); ourLog.info("ConceptMap:\n" + conceptMap.toString()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 86aaa437274..e1c193e0cc3 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate.tasks; */ import ca.uhn.fhir.jpa.entity.EmpiLink; +import ca.uhn.fhir.jpa.entity.TermConceptMap; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask; @@ -143,6 +144,12 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { //EMPI Target Type empiLink.addColumn("20200727.1","TARGET_TYPE").nullable().type(ColumnTypeEnum.STRING, EmpiLink.TARGET_TYPE_LENGTH); + //ConceptMap add version for search + Builder.BuilderWithTableName trmConceptMap = version.onTable("TRM_CONCEPT_MAP"); + trmConceptMap.addColumn("20200910.1", "VER").nullable().type(ColumnTypeEnum.STRING, TermConceptMap.MAX_VER_LENGTH); + trmConceptMap.dropIndex("20200910.2", "IDX_CONCEPT_MAP_URL"); + trmConceptMap.addIndex("20200910.3", "IDX_CONCEPT_MAP_URL").unique(true).withColumns("URL", "VER"); + //Term CodeSystem Version and Term ValueSet Version Builder.BuilderWithTableName trmCodeSystemVer = version.onTable("TRM_CODESYSTEM_VER"); trmCodeSystemVer.addIndex("20200923.1", "IDX_CODESYSTEM_AND_VER").unique(true).withColumns("CODESYSTEM_PID", "CS_VERSION_ID"); 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 4028f33c450..40322adf98c 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 @@ -326,6 +326,24 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { return url; } + public static String getCodeSystemUrl(@Nonnull IBaseResource theCodeSystem) { + String url; + switch (theCodeSystem.getStructureFhirVersionEnum()) { + 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 DSTU3: + default: + throw new IllegalArgumentException("Can not handle version: " + theCodeSystem.getStructureFhirVersionEnum()); + } + return url; + } + public static String getValueSetVersion(@Nonnull IBaseResource theValueSet) { String version; switch (theValueSet.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()); + } + } }